Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarifications on implicit cast on function/method calls #1312

Open
jaehyun1ee opened this issue Oct 7, 2024 · 5 comments
Open

Clarifications on implicit cast on function/method calls #1312

jaehyun1ee opened this issue Oct 7, 2024 · 5 comments

Comments

@jaehyun1ee
Copy link
Contributor

There seems to be unspecified bits regarding implicit cast on function/method/(and action) calls.

Spec section 8.20. Method invocations and function calls mentions:

The calling convention is copy-in/copy-out (Section 6.8). For generic functions the type arguments can be explicitly specified in the function call. The compiler only inserts implicit casts for direction in arguments to methods or functions as described in Section 8.11. The types for all other arguments must match the parameter types exactly.

(1) How do we define implicit cast for action calls?

Strictly speaking, actions are neither a function nor a method, while they are quite similar constructs.
But, the spec does not mention how casts should be inserted on action calls.
Would it be safe to consider that implicit cast on action arguments behave the same as function/method arguments?

(2) Do we insert implicit cast for directionless arguments?

While the spec mentions that we only insert cast for direction in arguments, there exist some examples in the p4c compiler test suite where it expects to insert implicit casts for directionless arguments also.

(a) extern2.p4

extern ext<H> {
    ext(H v);
    void method<T>(H h, T t);
}

control c() {
    ext<bit<16>>(16w0) x;
    apply {
        x.method(0, 8w0);
    }
}

Here, it is expected that the first directionless argument, 0 of x.method is implicitly casted to bit<16>.

(b) action-bind.p4

control c(inout bit<32> x) {
    action a(inout bit<32> b, bit<32> d) {
        b = d;
    }
    table t {
        actions = { a(x); }
        default_action = a(x, 0);
    }
    apply {
        t.apply();
    }
}

control proto(inout bit<32> x);
package top(proto p);

top(c()) main;

Also here, the second directionless argument 0 for the default action a is expected to be implicitly cast to bit<32> type.

I wonder whether the p4c test suite is in a disagreement with the current spec, or the current spec is ambiguous.

@jaehyun1ee
Copy link
Contributor Author

jaehyun1ee commented Nov 2, 2024

For (1), I noticed that the spec mentions the following in section 6.8.1.

Actions can also be explicitly invoked using function call syntax, either from a control block or from another action. In this case, values for all action parameters must be supplied explicitly, including values for the directionless parameters. The directionless parameters in this case behave like in parameters.

So a directionless parameter passed to an action in a P4 program can be implicitly cast, because they are treated as direction in.

But regarding (2), I found a lot of use cases in the p4c compiler test suite that expects implicit cast on function/method and constructor calls.

List of tests
  • extern-funcs-bmv2.p4
  • extern2.p4
  • gauntlet_extern_arguments_2.p4
  • gauntlet_hdr_in_value-bmv2.p4
  • issue1001-1-bmv2.p4
  • issue1001-bmv2.p4
  • issue1043-bmv2.p4
  • issue1334.p4
  • issue1642-bmv2.p4
  • issue1653-bmv2.p4
  • issue1653-complex-bmv2.p4
  • issue1660-bmv2.p4
  • issue1765-1-bmv2.p4
  • issue2648.p4
  • issue3246-1.p4
  • issue383-bmv2.p4
  • issue562-bmv2.p4
  • issue933-1.p4
  • named-arg1.p4
  • v1model-special-ops-bmv2.p4
  • bfd_offload.p4
  • calc-ebpf.p4
  • constructor_cast.p4
  • issue1006.p4
  • issue1097-2-bmv2.p4
  • issue1097-bmv2.p4
  • issue1814-1-bmv2.p4
  • issue1814-bmv2.p4
  • issue1958.p4
  • issue2844-enum.p4
  • issue298-bmv2.p4
  • issue4288.p4
  • issue754.p4
  • list7.p4
  • pr1363.p4
  • psa-action-profile1.p4
  • psa-action-profile3.p4
  • psa-action-profile4.p4
  • psa-basic-counter-bmv2.p4
  • psa-counter1.p4
  • psa-counter2.p4
  • psa-counter3.p4
  • psa-custom-type-counter-index.p4
  • psa-end-of-ingress-test-bmv2.p4
  • psa-example-dpdk-byte-alignment_1.p4
  • psa-example-dpdk-byte-alignment_2.p4
  • psa-example-dpdk-byte-alignment_3.p4
  • psa-example-dpdk-byte-alignment_5.p4
  • psa-example-dpdk-byte-alignment_6.p4
  • psa-example-dpdk-byte-alignment_7.p4
  • psa-example-dpdk-byte-alignment_8.p4
  • psa-example-dpdk-byte-alignment_9.p4
  • pse-example-dpdk-counter.p4
  • psa-example-dpdk-externs.p4
  • psa-example-dpdk-meter-execute-err.p4
  • psa-example-dpdk-meter.p4
  • psa-example-dpdk-meter1.p4
  • psa-example-dpdk-varbit-bmv2.p4
  • psa-meter1.p4
  • psa-meter3.p4
  • psa-meter7-bmv2.p4
  • psa-random.p4
  • psa-register-complex-bmv2.p4
  • psa-register-read-write-2-bmv2.p4
  • psa-register-read-write-bmv2.p4
  • psa-register1.p4
  • psa-register2.p4
  • psa-register3.p4
  • rcp.p4
  • rcp1.p4
  • register-serenum-bmv2.p4
  • simple-firewall_ubpf.p4
  • unused-counter-bmv2.p4
  • value-sets.p4

Would allowing implicit cast for directionless parameters, as well as in parameters make sense?

@jafingerhut
Copy link
Collaborator

Are you asking about directionless parameters on actions only?

Or directionless parameters on other kinds of things?

The reason I ask is because directionless parameters on actions are a bit different than on other types of things. Section 6.8 of the spec describes this for actions vs. non-actions. That difference implies to me that perhaps the answer for implicit casts might be done by the compiler could be different for directionless parameters to actions, vs. directionless parameters to non-actions.

@jaehyun1ee
Copy link
Contributor Author

I was asking about directionless parameters for other kinds (such as extern methods, functions, and constructors).

For actions (and when the parameter is supplied by the P4 program, not the control plane), I think the below parts mention that we can apply implicit cast.

Actions can also be explicitly invoked using function call syntax, either from a control block or from another action. In this case, values for all action parameters must be supplied explicitly, including values for the directionless parameters. The directionless parameters in this case behave like in parameters. (section 6.8.1)

The compiler only inserts implicit casts for direction in arguments to methods or functions as described in Section 8.11. The types for all other arguments must match the parameter types exactly. (section 8.20)

However for other kinds, the spec mentions only that the directionless parameter should be a compile-time known value. But it does not mention that they can be implicitly cast.

For anything other than an action, e.g. a control, parser, or function, a directionless parameter means that the value supplied as an argument in a call must be a compile-time known value (see Section 18.1). (section 6.8)

The compiler only inserts implicit casts for direction in arguments to methods or functions as described in Section 8.11. The types for all other arguments must match the parameter types exactly. (section 8.20)

So although there seem to be many use cases expecting implicit casts on directionless parameter, for example like the one below, when calling an extern constructor, we cannot type the program as valid as per the spec.

extern E {
    E(bit<32> size);
}

control c() {
    E(12) e;
    apply {}
}

@ChrisDodd
Copy link
Contributor

ChrisDodd commented Nov 3, 2024

I don't think there's anything special about in arguments or directionless arguments -- implicit casts are allowed anywhere a type that can be implicitly cast to the type needed in the context of where the expression appears. So they're allowed for any expression that is used for any argument whether that is in, out, or directionless -- the obviously for out arguments the implicit cast is in the other direction (casting the type of the argment to the type of the lvalue expression used). inout is a bit different as it would need to be (implicitly) case both directions, and I don't think we actually have any implicit casts like that.

The requirement that the value given for a directionless argument be compile-time known is independent of the typing.

@jaehyun1ee
Copy link
Contributor Author

How about revising the spec such that it mentions,

(Current)

The compiler only inserts implicit casts for direction in arguments to methods or functions as described in Section 8.11. The types for all other arguments must match the parameter types exactly. (section 8.20)

(Revised)

The compiler inserts implicit casts for arguments to methods or functions as described in Section 8.11. The types for all other arguments must match the parameter types exactly. (section 8.20)

Or at least allowing it for directionless arguments,

The compiler only inserts implicit casts for direction in or directionless arguments to methods or functions as described in Section 8.11. The types for all other arguments must match the parameter types exactly. (section 8.20)

If we allow implicit cast for inout, I believe the only valid case would be for serializable enums, which is the only type that can be implicitly cast back and forth between serenum t and its underlying type t. Other types, say int, can be implicitly cast to int<w> or bit<w> but not the other way around.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants