diff --git a/frontends/p4/typeChecking/typeChecker.cpp b/frontends/p4/typeChecking/typeChecker.cpp index b9648b3d62..2c76751d2a 100644 --- a/frontends/p4/typeChecking/typeChecker.cpp +++ b/frontends/p4/typeChecking/typeChecker.cpp @@ -3493,7 +3493,7 @@ const IR::Expression *TypeInference::actionCall(bool inActionList, if (paramType == nullptr || argType == nullptr) // type checking failed before return actionCall; - constraints.addEqualityConstraint(actionCall, paramType, argType); + constraints.addImplicitCastConstraint(actionCall, paramType, argType); if (param->direction == IR::Direction::None) { if (inActionList) { typeError("%1%: parameter %2% cannot be bound: it is set by the control plane", arg, @@ -3509,6 +3509,15 @@ const IR::Expression *TypeInference::actionCall(bool inActionList, typeError("%1%: action argument must be a compile-time constant", arg->expression); } + // This is like an assignment; may make additional conversions. + newExpr = assignment(arg, param->type, arg->expression); + if (readOnly) { + // FIXME -- if we're in readonly mode, we should not have introduced any mods + // here, but there's a bug in the DPDK backend where it generates a ListExpression + // that would be converted to a StructExpression, and other problems where it + // can't deal with that StructExpressions, so we hack to avoid breaking those tests + newExpr = arg->expression; + } } else if (param->direction == IR::Direction::Out || param->direction == IR::Direction::InOut) { if (!isLeftValue(arg->expression)) diff --git a/frontends/p4/typeChecking/typeConstraints.cpp b/frontends/p4/typeChecking/typeConstraints.cpp index 448466ec22..71144722bb 100644 --- a/frontends/p4/typeChecking/typeConstraints.cpp +++ b/frontends/p4/typeChecking/typeConstraints.cpp @@ -31,6 +31,12 @@ void TypeConstraints::addEqualityConstraint(const IR::Node *source, const IR::Ty add(c); } +void TypeConstraints::addImplicitCastConstraint(const IR::Node *source, const IR::Type *left, + const IR::Type *right) { + auto c = new CanBeImplicitlyCastConstraint(left, right, source); + add(c); +} + TypeVariableSubstitution *TypeConstraints::solve() { LOG3("Solving constraints:\n" << *this); currentSubstitution = new TypeVariableSubstitution(); diff --git a/frontends/p4/typeChecking/typeConstraints.h b/frontends/p4/typeChecking/typeConstraints.h index b338dabcb6..dbafff929f 100644 --- a/frontends/p4/typeChecking/typeConstraints.h +++ b/frontends/p4/typeChecking/typeConstraints.h @@ -221,6 +221,8 @@ class TypeConstraints final : public IHasDbPrint { constraints.push_back(constraint); } void addEqualityConstraint(const IR::Node *source, const IR::Type *left, const IR::Type *right); + void addImplicitCastConstraint(const IR::Node *source, const IR::Type *left, + const IR::Type *right); /* * Solve the specified constraint. * @param subst Variable substitution which is updated with new constraints. diff --git a/testdata/p4_16_errors/serEnumImplCast.p4 b/testdata/p4_16_errors/serEnumImplCast.p4 new file mode 100644 index 0000000000..49e793a81f --- /dev/null +++ b/testdata/p4_16_errors/serEnumImplCast.p4 @@ -0,0 +1,22 @@ +#include + +enum bit<2> foo_t { A = 0, B = 1, C = 2, D = 3 } + +struct meta_t { + bit<2> x; + bit<6> y; +} + +control c(inout meta_t m) { + action set_x(foo_t v) { m.x = v; } + + table t { + key = { m.y : exact; } + actions = { set_x; } + default_action = set_x(2w0); // not allowed to implicitly cast to serenum + } + + apply { + t.apply(); + } +} diff --git a/testdata/p4_16_errors_outputs/serEnumImplCast.p4 b/testdata/p4_16_errors_outputs/serEnumImplCast.p4 new file mode 100644 index 0000000000..cf5c706e18 --- /dev/null +++ b/testdata/p4_16_errors_outputs/serEnumImplCast.p4 @@ -0,0 +1,32 @@ +#include + +enum bit<2> foo_t { + A = 0, + B = 1, + C = 2, + D = 3 +} + +struct meta_t { + bit<2> x; + bit<6> y; +} + +control c(inout meta_t m) { + action set_x(foo_t v) { + m.x = v; + } + table t { + key = { + m.y: exact; + } + actions = { + set_x; + } + default_action = set_x(2w0); + } + apply { + t.apply(); + } +} + diff --git a/testdata/p4_16_errors_outputs/serEnumImplCast.p4-stderr b/testdata/p4_16_errors_outputs/serEnumImplCast.p4-stderr new file mode 100644 index 0000000000..ca221d13b0 --- /dev/null +++ b/testdata/p4_16_errors_outputs/serEnumImplCast.p4-stderr @@ -0,0 +1,6 @@ +serEnumImplCast.p4(16): [--Werror=type-error] error: '2w0': values of type 'bit<2>' cannot be implicitly cast to type 'foo_t' + default_action = set_x(2w0); // not allowed to implicitly cast to serenum + ^^^ +serEnumImplCast.p4(3) +enum bit<2> foo_t { A = 0, B = 1, C = 2, D = 3 } + ^^^^^ diff --git a/testdata/p4_16_samples/serEnumImplCast.p4 b/testdata/p4_16_samples/serEnumImplCast.p4 new file mode 100644 index 0000000000..f554431d1c --- /dev/null +++ b/testdata/p4_16_samples/serEnumImplCast.p4 @@ -0,0 +1,26 @@ +#include +control generic(inout M m); +package top(generic c); + +enum bit<2> foo_t { A = 0, B = 1, C = 2, D = 3 } + +struct meta_t { + bit<2> x; + bit<6> y; +} + +control c(inout meta_t m) { + action set_x(bit<2> v) { m.x = v; } + + table t { + key = { m.y : exact; } + actions = { set_x; } + default_action = set_x(foo_t.A); + } + + apply { + t.apply(); + } +} + +top(c()) main; diff --git a/testdata/p4_16_samples_outputs/serEnumImplCast-first.p4 b/testdata/p4_16_samples_outputs/serEnumImplCast-first.p4 new file mode 100644 index 0000000000..1eec9cf64b --- /dev/null +++ b/testdata/p4_16_samples_outputs/serEnumImplCast-first.p4 @@ -0,0 +1,35 @@ +#include + +control generic(inout M m); +package top(generic c); +enum bit<2> foo_t { + A = 2w0, + B = 2w1, + C = 2w2, + D = 2w3 +} + +struct meta_t { + bit<2> x; + bit<6> y; +} + +control c(inout meta_t m) { + action set_x(bit<2> v) { + m.x = v; + } + table t { + key = { + m.y: exact @name("m.y"); + } + actions = { + set_x(); + } + default_action = set_x(foo_t.A); + } + apply { + t.apply(); + } +} + +top(c()) main; diff --git a/testdata/p4_16_samples_outputs/serEnumImplCast-frontend.p4 b/testdata/p4_16_samples_outputs/serEnumImplCast-frontend.p4 new file mode 100644 index 0000000000..c2fd6e6209 --- /dev/null +++ b/testdata/p4_16_samples_outputs/serEnumImplCast-frontend.p4 @@ -0,0 +1,35 @@ +#include + +control generic(inout M m); +package top(generic c); +enum bit<2> foo_t { + A = 2w0, + B = 2w1, + C = 2w2, + D = 2w3 +} + +struct meta_t { + bit<2> x; + bit<6> y; +} + +control c(inout meta_t m) { + @name("c.set_x") action set_x(@name("v") bit<2> v) { + m.x = v; + } + @name("c.t") table t_0 { + key = { + m.y: exact @name("m.y"); + } + actions = { + set_x(); + } + default_action = set_x(foo_t.A); + } + apply { + t_0.apply(); + } +} + +top(c()) main; diff --git a/testdata/p4_16_samples_outputs/serEnumImplCast-midend.p4 b/testdata/p4_16_samples_outputs/serEnumImplCast-midend.p4 new file mode 100644 index 0000000000..54b050653a --- /dev/null +++ b/testdata/p4_16_samples_outputs/serEnumImplCast-midend.p4 @@ -0,0 +1,28 @@ +#include + +control generic(inout M m); +package top(generic c); +struct meta_t { + bit<2> x; + bit<6> y; +} + +control c(inout meta_t m) { + @name("c.set_x") action set_x(@name("v") bit<2> v) { + m.x = v; + } + @name("c.t") table t_0 { + key = { + m.y: exact @name("m.y"); + } + actions = { + set_x(); + } + default_action = set_x(2w0); + } + apply { + t_0.apply(); + } +} + +top(c()) main; diff --git a/testdata/p4_16_samples_outputs/serEnumImplCast.p4 b/testdata/p4_16_samples_outputs/serEnumImplCast.p4 new file mode 100644 index 0000000000..4ba63130cb --- /dev/null +++ b/testdata/p4_16_samples_outputs/serEnumImplCast.p4 @@ -0,0 +1,35 @@ +#include + +control generic(inout M m); +package top(generic c); +enum bit<2> foo_t { + A = 0, + B = 1, + C = 2, + D = 3 +} + +struct meta_t { + bit<2> x; + bit<6> y; +} + +control c(inout meta_t m) { + action set_x(bit<2> v) { + m.x = v; + } + table t { + key = { + m.y: exact; + } + actions = { + set_x; + } + default_action = set_x(foo_t.A); + } + apply { + t.apply(); + } +} + +top(c()) main; diff --git a/testdata/p4_16_samples_outputs/serEnumImplCast.p4-stderr b/testdata/p4_16_samples_outputs/serEnumImplCast.p4-stderr new file mode 100644 index 0000000000..e69de29bb2