12. Statements
Every statement in P4 except block statements must end with a semicolon. Statements can appear in several places:
- Within
parserstates - Within a
controlblock - Within an
action
There are restrictions for the kinds of statements that can appear in
each of these places. For example, returns are not supported in
parsers, and switch statements are only supported in control blocks.
We present here the most general case, for control blocks.
\~ Begin P4Grammar statement : assignmentOrMethodCallStatement | conditionalStatement | emptyStatement | blockStatement | exitStatement | returnStatement | switchStatement ;
- [INCLUDE=grammar.mdk:assignmentOrMethodCallStatement]
End P4Grammar
In addition, parsers support a transition statement (Section
[#sec-transition]).
An assignment, written with the = sign, first evaluates its left
sub-expression to an l-value, then evaluates its right sub-expression to
a value, and finally copies the value into the l-value. Derived types
(e.g. structs) are copied recursively, and all components of headers
are copied, including “validity” bits. Assignment is not defined for
extern values.
-
The empty statement, written
;is a no-op.
Begin P4Grammar [INCLUDE=grammar.mdk:emptyStatement]End P4Grammar
A block statement is denoted by curly braces. It contains a sequence of statements and declarations, which are executed sequentially. The declarations (e.g., variables and constants) within a block statement are only visible within the block.
\~ Begin P4Grammar [INCLUDE=grammar.mdk:blockStatement]
[INCLUDE=grammar.mdk:statOrDeclList]
- [INCLUDE=grammar.mdk:statementOrDeclaration]
End P4Grammar
The return statement immediately terminates the execution of the
action, function or control containing it. return statements are
not allowed within parsers. return statements followed by an
expression are only allowed within functions that return values; in this
case the type of the expression must match the return type of the
function. Any copy-out behavior due to direction out or inout
parameters of the enclosing action, function, or control are still
performed after the execution of the return statement. See Section
[#sec-calling-convention] for details on copy-out behavior.
\~ Begin P4Grammar [INCLUDE=grammar.mdk:returnStatement] \~ End P4Grammar
The exit statement immediately terminates the execution of all the
blocks currently executing: the current action (if invoked within an
action), the current control, and all its callers. exit statements
are not allowed within parsers or functions.
Any copy-out behavior due to direction out or inout parameters of
the enclosing action or control, and all of its callers, are still
performed after the execution of the exit statement. See Section
[#sec-calling-convention] for details on copy-out behavior.
\~ Begin P4Grammar [INCLUDE=grammar.mdk:exitStatement] \~ End P4Grammar
There are some expressions whose evaluation might cause an exit
statement to be executed. Examples include:
table.apply().action_run, which can only appear as the expression of aswitchstatement (see Section [#sec-switch-stmt]), and when it appears there, it must be the entire expression.- Any expression containing
table.apply().hitortable.apply().miss(see Section [#sec-invoke-mau]), which can be part of arbitrarily complex expressions in many places of a P4 program, such as:- the expression in an
ifstatement. - the expression
e1in a conditional expressione1 ? e2 : e3. - in an assignment statement, in the left and/or right hand sides.
- an argument passed to a function or method call.
- an expression to calculate a table key (see Section [#sec-mau-semantics]).
- the expression in an
This list is not intended to be exhaustive.
If applying the table causes an action to be executed, which in turn
causes an exit statement to be executed, then evaluation of the
expression ends immediately, and the rest of the current expression or
statement does not complete its execution. See Section
[#sec-expr-eval-order] for the order of evaluation of the parts of an
expression. For the examples listed above, it also means the following
behavior after the expression evaluation is interrupted.
- For a
switchstatement, iftable.apply()exits, then none of the blocks in theswitchstatement are executed. - If
table.apply().hitortable.apply().misscause an exit during the evaluation of an expression:- If it is the expression of an
ifstatement, then neither the ‘then’ nor ‘else’ branches of theifstatement are executed. - If it is the expression
e1in a conditional expressione1 ? e2 : e3, then neither expressione2nore3are evaluated. - If the expression is the right hand side of an assignment statement, or part of the calculation of the L-value on the left hand side (e.g. the index expression of a header stack reference), then no assignment occurs.
- If the expression is an argument passed to a function or method call, then the function/method call does not occur.
- If the expression is a table key, then the table is not applied.
- If it is the expression of an
The conditional statement uses standard syntax and semantics familiar from many programming languages.
However, the condition expression in P4 is required to be a Boolean (and not an integer).
\~ Begin P4Grammar [INCLUDE=grammar.mdk:conditionalStatement] \~ End P4Grammar
When several if statements are nested, the else applies to the
innermost if statement that does not have an else statement.
- The
switchstatement can only be used withincontrolblocks.
Begin P4Grammar [INCLUDE=grammar.mdk:switchStatement]
[INCLUDE=grammar.mdk:switchCases]
[INCLUDE=grammar.mdk:switchCase]
[INCLUDE=grammar.mdk:switchLabel]
- [INCLUDE=grammar.mdk:nonBraceExpression]
End P4Grammar
The nonBraceExpression is the same as expression as defined in
Section [#sec-exprs], except it does not include any cases that can
begin with a left brace { character, to avoid syntactic ambiguity with
a block statement.
There are two kinds of switch expressions allowed, described
separately in the following two subsections.
Switch statement with action_run expression
For this variant of switch statement, the expression must be of the
form t.apply().action_run, where t is the name of a table (see
Section [#sec-invoke-mau]). All switch labels must be names of
actions of the table t, or default.
\~ Begin P4Example switch (t.apply().action_run) { action1: // fall-through to action2: action2: { /* body omitted / } action3: { / body omitted / } // no fall-through from action2 to action3 labels default: { / body omitted */ } } \~ End P4Example
Note that the default label of the switch statement is used to match
on the kind of action executed, no matter whether there was a table hit
or miss. The default label does not indicate that the table missed and
the default_action was executed.
Switch statement with integer or enumerated type expression
For this variant of switch statement, the expression must evaluate to
a result with one of these types:
bit<W>int<W>enum, either with or without an underlying representation specifiederror
All switch labels must be expressions with compile-time known values,
and must have a type that can be implicitly cast to the type of the
switch expression (see Section [#sec-implicit-casts]). Switch
labels must not begin with a left brace character {, to avoid
ambiguity with a block statement.
\~ Begin P4Example // Assume the expression hdr.ethernet.etherType has type bit\<16> switch (hdr.ethernet.etherType) { 0x86dd: { /* body omitted / } 0x0800: // fall-through to the next body 0x0802: { / body omitted / } 0xcafe: { / body omitted / } default: { / body omitted */ } } \~ End P4Example
Notes common to all switch statements
It is a compile-time error if two labels of a switch statement equal
each other. The switch label values need not include all possible values
of the switch expression. It is optional to have a switch case with
the default label, but if one is present, it must be the last one in
the switch statement.
If a switch label is not followed by a block statement, it falls through
to the next label. However, if a block statement is present, it does not
fall through. Note that this is different from C-style switch
statements, where a break is needed to prevent fall-through. If the
last switch label is not followed by a block statement, the behavior is
the same as if the last switch label were followed by an empty block
statement { }.
When a switch statement is executed, first the switch expression is
evaluated, and any side effects from evaluating this expression are
visible to any switch case that is executed. Among switch labels that
are not default, at most one of them can equal the value of the switch
expression. If one is equal, that switch case is executed.
If no labels are equal to the switch expression, then:
- if there is a
defaultlabel, the case with thedefaultlabel is executed. - if there is no
defaultlabel, then no switch case is executed, and execution continues after the end of theswitchstatement, with no side effects (except any that were caused by evaluating theswitchexpression).
See “Implementing generalized P4_16 switch statements” GeneralizedSwitchStatements for possible techniques that one might use to implement generalized switch statements.