p4lang / p4c

P4_16 reference compiler
https://p4.org/
Apache License 2.0
677 stars 444 forks source link

Compiler Bug: `Could not find type of <Type_Header> ...` on specialized generic struct type #4835

Open jaehyun1ee opened 3 months ago

jaehyun1ee commented 3 months ago

The following program, bug-generic.p4 fails to typecheck when given a command $ p4c --target bmv2 --arch v1model bug-generic.p4.

#include <core.p4>
#include <v1model.p4>

typedef standard_metadata_t std_meta_t;

header standard_t<T> {
  T src;
  T dst;
}

struct headers_t<T> {
  standard_t<T> standard;
}

struct meta_t { }

parser MyParser(packet_in pkt, out headers_t<bit<8>> hdr, inout meta_t meta, inout std_meta_t std_meta) {
    state start {
        pkt.extract<standard_t<bit<8>>>(hdr.standard);
        // pkt.extract(hdr.standard); <-- this also fails
        transition accept;
    }
}

control MyVerifyChecksum(inout headers_t<bit<8>> hdr, inout meta_t meta) {
    apply { }
}
control MyComputeChecksum(inout headers_t<bit<8>> hdr, inout meta_t meta) {
    apply { }
}
control MyIngress(inout headers_t<bit<8>> hdr, inout meta_t meta, inout std_meta_t std_meta) {
   apply { }
}
control MyEgress(inout headers_t<bit<8>> hdr, inout meta_t meta, inout std_meta_t std_meta) {
    apply { }
}
control MyDeparser(packet_out pkt, in headers_t<bit<8>> hdr) {
    apply { }
}
V1Switch(MyParser(), MyVerifyChecksum(), MyIngress(), MyEgress(), MyComputeChecksum(), MyDeparser()) main;

Here, I defined a generic struct headers_t<T> that has a field of generic header type standard_t<T>. In the parameter of MyParser, the type is specialized as headers_t<big<8>>. So I expect that both pkt.extract(hdr.standard); or pkt.extract<standard_t<bit<8>>>(hdr.standard); should typecheck.

However, the compiler fails to typecheck with an error message:

Compiler Bug: bug-generic.p4(19): Could not find type of <Type_Header>(40455) standard_t_0/66 header standard_t_0 {
  bit<8> src;
  bit<8> dst; }
header standard_t<T> {
       ^^^^^^^^^^

The same error message occurs when I do not pass the type argument standard_t<bit<8>> to pkt.extract method.

iamAyushChamoli commented 3 months ago

In your P4 program, you're trying to use a generic header standard_t, which should work with different types. However, the P4 language and its compiler have limitations when it comes to generics. Specifically, the pkt.extract() method requires a concrete header type, and the compiler struggles to resolve generic types at compile time. The P4 compiler requires that all header types be fully specified at compile time. Something like: header standard_8_t { bit<8> src; bit<8> dst; }

struct headers_t { standard_8_t standard; }

jaehyun1ee commented 3 months ago

Thank you for the explanation!

Yet, I'm afraid I did not understand what you meant by "concrete". Isn't the type argument standard_t<bit<8>> for pkt.extract method "concrete"? The type constructor standard_t<T> has been concretized (or specialized) by bit<8>. Similarly, the type of hdr is also concretized as headers_t<bit<8>>.

So my question would boil down to: i) Is my program allowed according to the P4 specification, but not being compiled due to compiler limitation, or ii) Is it now allowed by the P4 specification at the first place?

iamAyushChamoli commented 3 months ago

If I am understanding your issue correctly, then it is basically an issue about concretization and generic methods/constructors. I certainly am not the expert here but yes,

Isn't the type argument standard_t<bit<8>> for pkt.extract method "concrete"?

this is concrete, and as you've mentioned in your issue, the error pops up when you do not pass this concretized argument. So we're on the same page so far.

Is my program allowed according to the P4 specification, but not being compiled due to compiler limitation,

Yes, it is allowed as The P4 language specification allows the use of generic types and their specialization into concrete types. This is part of the language's design to provide flexibility and reuse through parameterized types. The issue that you are facing is most likely a compiler limitation issue. It is likely that the version of P4 Compiler that you are using does not support your current problem.

I'm certainly not the expert here but this is what I can make out of the entire situation.

jaehyun1ee commented 3 months ago

Ah, I see. Now I guess we are on the same page :)

One minor clarification though,

the error pops up when you do not pass this concretized argument. So we're on the same page so far.

The error pops up regardless of the type argument being passed. i.e., pkt.extract(hdr.standard); fails as well as pkt.extract<standard_t<bit<8>>(hdr.standard);, with the same error message.

iamAyushChamoli commented 3 months ago

This might be a silly question but while running pkt.extract<standard_t<bit<8>>(hdr.standard);, did you make sure that you concretize it during it's instantiation/declaration? Could it be possible that you tried passing a concretized value to the pkt.extract() method but forgot to concretize it during declaration?

fruffy commented 3 months ago

The compiler should never throw an exception like Compiler Bug: to a user. This is a problem with the compiler regardless whether this is valid P4 or not.

jaehyun1ee commented 3 months ago

@iamAyushChamoli

Hmm, I'm not sure if I can answer the question. The code example doesn't compile due to failure in the compiler's type checker, so I didn't have a chance to execute the code.