From 202d3f599331faae6eeddab87a663eaf9bda1cc6 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 13 Jun 2023 14:38:27 -0700 Subject: [PATCH] Semantic analysis for `if` expressions (#2893) Add semantic analysis and semantics IR building for `if` expressions, and add the first parts of control flow handling to semantics IR. After discussion with @chandlerc, use [block arguments](https://en.wikipedia.org/wiki/Static_single-assignment_form#Block_arguments) to convey values from the two arms of the `if` to the result. For now, only a single block argument is supported, but we should revisit this as we explore more of the requirements of the Semantics IR form. Functions can now contain multiple code blocks, so grab the entry block up-front instead of assuming the entry block will be at the top of the block stack when we reach the end of function emission. Add trivial support for `bool` type literal, because without it we can't write testcases. --- .../testdata/semantics_builtin_nodes.carbon | 1 + toolchain/lexer/token_kind.def | 1 + toolchain/lowering/lowering_handle.cpp | 24 ++++ toolchain/parser/parse_node_kind.def | 14 +- toolchain/parser/parser_handle_expression.cpp | 60 +++++---- toolchain/parser/parser_state.def | 48 ++++--- .../testdata/if_expression/basic.carbon | 14 +- .../fail_condition_missing.carbon | 11 +- .../fail_else_expr_missing.carbon | 10 +- .../if_expression/fail_else_missing.carbon | 10 +- .../fail_then_expr_missing.carbon | 10 +- .../if_expression/fail_then_missing.carbon | 11 +- .../testdata/if_expression/precedence.carbon | 22 ++-- .../semantics/semantics_builtin_kind.def | 3 + toolchain/semantics/semantics_context.cpp | 6 +- toolchain/semantics/semantics_context.h | 4 + toolchain/semantics/semantics_handle.cpp | 19 +-- .../semantics/semantics_handle_function.cpp | 24 ++-- .../semantics_handle_if_expression.cpp | 77 +++++++++++ toolchain/semantics/semantics_ir.cpp | 4 + toolchain/semantics/semantics_node.h | 13 ++ .../semantics/semantics_node_block_stack.h | 6 + toolchain/semantics/semantics_node_kind.def | 4 + .../testdata/basics/fail_name_lookup.carbon | 4 +- .../function/call/fail_param_count.carbon | 14 +- .../function/call/fail_param_type.carbon | 6 +- .../function/call/more_param_ir.carbon | 8 +- .../testdata/function/call/params_one.carbon | 8 +- .../function/call/params_one_comma.carbon | 10 +- .../testdata/function/call/params_two.carbon | 8 +- .../function/call/params_two_comma.carbon | 10 +- .../testdata/function/call/params_zero.carbon | 6 +- .../fail_param_name_conflict.carbon | 4 +- .../testdata/function/definition/order.carbon | 12 +- .../function/definition/params_one.carbon | 4 +- .../definition/params_one_comma.carbon | 4 +- .../function/definition/params_two.carbon | 4 +- .../definition/params_two_comma.carbon | 4 +- .../function/definition/params_zero.carbon | 4 +- .../definition/same_param_name.carbon | 8 +- .../testdata/if_expression/basic.carbon | 80 ++++++++++++ .../if_expression/control_flow.carbon | 93 ++++++++++++++ .../testdata/if_expression/nested.carbon | 120 ++++++++++++++++++ 43 files changed, 646 insertions(+), 161 deletions(-) create mode 100644 toolchain/semantics/semantics_handle_if_expression.cpp create mode 100644 toolchain/semantics/testdata/if_expression/basic.carbon create mode 100644 toolchain/semantics/testdata/if_expression/control_flow.carbon create mode 100644 toolchain/semantics/testdata/if_expression/nested.carbon diff --git a/toolchain/driver/testdata/semantics_builtin_nodes.carbon b/toolchain/driver/testdata/semantics_builtin_nodes.carbon index ba3dfd4756df..75b13af639b8 100644 --- a/toolchain/driver/testdata/semantics_builtin_nodes.carbon +++ b/toolchain/driver/testdata/semantics_builtin_nodes.carbon @@ -18,6 +18,7 @@ // CHECK:STDOUT: nodes: [ // CHECK:STDOUT: {kind: CrossReference, arg0: ir0, arg1: nodeTypeType, type: typeTypeType}, // CHECK:STDOUT: {kind: CrossReference, arg0: ir0, arg1: nodeInvalidType, type: typeInvalidType}, +// CHECK:STDOUT: {kind: CrossReference, arg0: ir0, arg1: nodeBoolType, type: typeTypeType}, // CHECK:STDOUT: {kind: CrossReference, arg0: ir0, arg1: nodeIntegerType, type: typeTypeType}, // CHECK:STDOUT: {kind: CrossReference, arg0: ir0, arg1: nodeFloatingPointType, type: typeTypeType}, // CHECK:STDOUT: {kind: CrossReference, arg0: ir0, arg1: nodeStringType, type: typeTypeType}, diff --git a/toolchain/lexer/token_kind.def b/toolchain/lexer/token_kind.def index 9ce9970625d1..686de1ef4a57 100644 --- a/toolchain/lexer/token_kind.def +++ b/toolchain/lexer/token_kind.def @@ -121,6 +121,7 @@ CARBON_KEYWORD_TOKEN(Api, "api") CARBON_KEYWORD_TOKEN(As, "as") CARBON_KEYWORD_TOKEN(Auto, "auto") CARBON_KEYWORD_TOKEN(Base, "base") +CARBON_KEYWORD_TOKEN(Bool, "bool") CARBON_KEYWORD_TOKEN(Break, "break") CARBON_KEYWORD_TOKEN(Case, "case") CARBON_KEYWORD_TOKEN(Class, "class") diff --git a/toolchain/lowering/lowering_handle.cpp b/toolchain/lowering/lowering_handle.cpp index 88f4666fc66a..0e692dde2444 100644 --- a/toolchain/lowering/lowering_handle.cpp +++ b/toolchain/lowering/lowering_handle.cpp @@ -37,6 +37,30 @@ auto LoweringHandleBindName(LoweringContext& /*context*/, // Probably need to do something here, but not necessary for now. } +auto LoweringHandleBlockArg(LoweringContext& /*context*/, + SemanticsNodeId /*node_id*/, SemanticsNode node) + -> void { + CARBON_FATAL() << "TODO: Add support: " << node; +} + +auto LoweringHandleBranch(LoweringContext& /*context*/, + SemanticsNodeId /*node_id*/, SemanticsNode node) + -> void { + CARBON_FATAL() << "TODO: Add support: " << node; +} + +auto LoweringHandleBranchIf(LoweringContext& /*context*/, + SemanticsNodeId /*node_id*/, SemanticsNode node) + -> void { + CARBON_FATAL() << "TODO: Add support: " << node; +} + +auto LoweringHandleBranchWithArg(LoweringContext& /*context*/, + SemanticsNodeId /*node_id*/, + SemanticsNode node) -> void { + CARBON_FATAL() << "TODO: Add support: " << node; +} + auto LoweringHandleBuiltin(LoweringContext& /*context*/, SemanticsNodeId /*node_id*/, SemanticsNode node) -> void { diff --git a/toolchain/parser/parse_node_kind.def b/toolchain/parser/parse_node_kind.def index 7d628c53666e..0834e04469a4 100644 --- a/toolchain/parser/parse_node_kind.def +++ b/toolchain/parser/parse_node_kind.def @@ -260,15 +260,15 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(InfixOperator, 2) CARBON_PARSE_NODE_KIND_CHILD_COUNT(PostfixOperator, 1) // `if` expression + `then` + `else`: -// _external_: expression +// _external_: expression +// IfExpressionIf +// _external_: expression // IfExpressionThen // _external_: expression -// IfExpressionElse -// _external_: expression -// IfExpression -CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExpression, 5) -CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExpressionThen, 0) -CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExpressionElse, 0) +// IfExpressionElse +CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExpressionIf, 1) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExpressionThen, 1) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(IfExpressionElse, 3) // Struct literals, such as `{.a = 0}`: // StructLiteralOrStructTypeLiteralStart diff --git a/toolchain/parser/parser_handle_expression.cpp b/toolchain/parser/parser_handle_expression.cpp index 7bae24ab9deb..e2bba4182303 100644 --- a/toolchain/parser/parser_handle_expression.cpp +++ b/toolchain/parser/parser_handle_expression.cpp @@ -28,8 +28,8 @@ auto ParserHandleExpression(ParserContext& context) -> void { } if (context.PositionIs(TokenKind::If)) { - context.PushState(ParserState::ExpressionIfFinish); - context.PushState(ParserState::ExpressionThen); + context.PushState(ParserState::IfExpressionFinish); + context.PushState(ParserState::IfExpressionFinishCondition); } else { context.PushStateForExpressionLoop(ParserState::ExpressionLoopForPrefix, state.ambient_precedence, @@ -64,6 +64,7 @@ auto ParserHandleExpressionInPostfix(ParserContext& context) -> void { case TokenKind::IntegerLiteral: case TokenKind::RealLiteral: case TokenKind::StringLiteral: + case TokenKind::Bool: case TokenKind::IntegerTypeLiteral: case TokenKind::UnsignedIntegerTypeLiteral: case TokenKind::FloatingPointTypeLiteral: @@ -209,12 +210,15 @@ auto ParserHandleExpressionLoopForPrefix(ParserContext& context) -> void { context.PushState(state); } -auto ParserHandleExpressionThen(ParserContext& context) -> void { +auto ParserHandleIfExpressionFinishCondition(ParserContext& context) -> void { auto state = context.PopState(); - if (context.ConsumeAndAddLeafNodeIf(TokenKind::Then, - ParseNodeKind::IfExpressionThen)) { - context.PushState(ParserState::ExpressionElse); + context.AddNode(ParseNodeKind::IfExpressionIf, state.token, + state.subtree_start, state.has_error); + + if (context.PositionIs(TokenKind::Then)) { + context.PushState(ParserState::IfExpressionFinishThen); + context.ConsumeChecked(TokenKind::Then); context.PushStateForExpression(*PrecedenceGroup::ForLeading(TokenKind::If)); } else { // TODO: Include the location of the `if` token. @@ -223,20 +227,24 @@ auto ParserHandleExpressionThen(ParserContext& context) -> void { if (!state.has_error) { context.emitter().Emit(*context.position(), ExpectedThenAfterIf); } - // Add placeholders for `then expression else expression`. - for (int i = 0; i != 4; ++i) { - context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(), - /*has_error=*/true); - } + // Add placeholders for `IfExpressionThen` and final `Expression`. + context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(), + /*has_error=*/true); + context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(), + /*has_error=*/true); context.ReturnErrorOnState(); } } -auto ParserHandleExpressionElse(ParserContext& context) -> void { +auto ParserHandleIfExpressionFinishThen(ParserContext& context) -> void { auto state = context.PopState(); - if (context.ConsumeAndAddLeafNodeIf(TokenKind::Else, - ParseNodeKind::IfExpressionElse)) { + context.AddNode(ParseNodeKind::IfExpressionThen, state.token, + state.subtree_start, state.has_error); + + if (context.PositionIs(TokenKind::Else)) { + context.PushState(ParserState::IfExpressionFinishElse); + context.ConsumeChecked(TokenKind::Else); context.PushStateForExpression(*PrecedenceGroup::ForLeading(TokenKind::If)); } else { // TODO: Include the location of the `if` token. @@ -245,20 +253,28 @@ auto ParserHandleExpressionElse(ParserContext& context) -> void { if (!state.has_error) { context.emitter().Emit(*context.position(), ExpectedElseAfterIf); } - // Add placeholders for `else expression`. - for (int i = 0; i != 2; ++i) { - context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(), - /*has_error=*/true); - } + // Add placeholder for the final `Expression`. + context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(), + /*has_error=*/true); context.ReturnErrorOnState(); } } -auto ParserHandleExpressionIfFinish(ParserContext& context) -> void { +auto ParserHandleIfExpressionFinishElse(ParserContext& context) -> void { + auto else_state = context.PopState(); + + // Propagate the location of `else`. + auto if_state = context.PopState(); + if_state.token = else_state.token; + if_state.has_error |= else_state.has_error; + context.PushState(if_state); +} + +auto ParserHandleIfExpressionFinish(ParserContext& context) -> void { auto state = context.PopState(); - context.AddNode(ParseNodeKind::IfExpression, state.token, state.subtree_start, - state.has_error); + context.AddNode(ParseNodeKind::IfExpressionElse, state.token, + state.subtree_start, state.has_error); } auto ParserHandleExpressionStatementFinish(ParserContext& context) -> void { diff --git a/toolchain/parser/parser_state.def b/toolchain/parser/parser_state.def index b570870ba55a..1ef944ea8c1f 100644 --- a/toolchain/parser/parser_state.def +++ b/toolchain/parser/parser_state.def @@ -183,8 +183,8 @@ CARBON_PARSER_STATE_VARIANTS2(Designator, Expression, Struct) // // If `If`: // 1. Expression -// 2. ExpressionThen -// 3. ExpressionIfFinish +// 2. IfExpressionCondition +// 3. IfExpressionFinish // Else if valid prefix operator: // 1. Expression // 2. ExpressionLoopForPrefix @@ -246,28 +246,36 @@ CARBON_PARSER_STATE(ExpressionLoopForBinary) // 1. ExpressionLoop CARBON_PARSER_STATE(ExpressionLoopForPrefix) +// Completes the condition of an `if` expression and handles the `then` token. +// +// If `Then`: +// 1. Expression +// 2. IfExpressionFinishThen +// Else: +// (state done) +CARBON_PARSER_STATE(IfExpressionFinishCondition) + +// Completes the first alternative in an `if` expression and handles the `else` +// token. +// +// If `Else`: +// 1. Expression +// 2. IfExpressionFinishElse +// Else: +// (state done) +CARBON_PARSER_STATE(IfExpressionFinishThen) + +// Completes the second alternative in an `if` expression. +// +// Always: +// (state done) +CARBON_PARSER_STATE(IfExpressionFinishElse) + // Completes an IfExpression. // // Always: // (state done) -CARBON_PARSER_STATE(ExpressionIfFinish) - -// Handles the `then` token in an `if` expression. -// -// If `Then`: -// 1. Expression -// 2. ExpressionElse -// Else: -// (state done) -CARBON_PARSER_STATE(ExpressionThen) - -// Handles the `else` token in an `if` expression. -// -// If `Else`: -// 1. Expression -// Else: -// (state done) -CARBON_PARSER_STATE(ExpressionElse) +CARBON_PARSER_STATE(IfExpressionFinish) // Handles the `;` for an expression statement, which is different from most // keyword statements. diff --git a/toolchain/parser/testdata/if_expression/basic.carbon b/toolchain/parser/testdata/if_expression/basic.carbon index aaf62427d6d1..c5056c1ee66c 100644 --- a/toolchain/parser/testdata/if_expression/basic.carbon +++ b/toolchain/parser/testdata/if_expression/basic.carbon @@ -8,7 +8,7 @@ // CHECK:STDOUT: {kind: 'DeclaredName', text: 'F'}, // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, // CHECK:STDOUT: {kind: 'DeclaredName', text: 'b'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'bool'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'bool'}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ParameterListComma', text: ','}, // CHECK:STDOUT: {kind: 'DeclaredName', text: 'x'}, @@ -19,16 +19,16 @@ // CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 13}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'bool'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'bool'}, // CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 18}, // CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, -// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'x'}, -// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else'}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'x'}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, // CHECK:STDOUT: {kind: 'NameReference', text: 'y'}, -// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 6}, // CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 8}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 27}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parser/testdata/if_expression/fail_condition_missing.carbon b/toolchain/parser/testdata/if_expression/fail_condition_missing.carbon index c8fc41f10493..7197aad32a35 100644 --- a/toolchain/parser/testdata/if_expression/fail_condition_missing.carbon +++ b/toolchain/parser/testdata/if_expression/fail_condition_missing.carbon @@ -14,14 +14,13 @@ // CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', has_error: yes, subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', has_error: yes, subtree_size: 6}, -// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 12}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'if', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 17}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/if_expression/fail_else_expr_missing.carbon b/toolchain/parser/testdata/if_expression/fail_else_expr_missing.carbon index 021332457980..0b817742042d 100644 --- a/toolchain/parser/testdata/if_expression/fail_else_expr_missing.carbon +++ b/toolchain/parser/testdata/if_expression/fail_else_expr_missing.carbon @@ -14,12 +14,12 @@ // CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'true'}, -// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then'}, -// CHECK:STDOUT: {kind: 'Literal', text: '1'}, -// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else'}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'true'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', has_error: yes, subtree_size: 6}, // CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 12}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parser/testdata/if_expression/fail_else_missing.carbon b/toolchain/parser/testdata/if_expression/fail_else_missing.carbon index 23fcaa30a067..336663d19ed5 100644 --- a/toolchain/parser/testdata/if_expression/fail_else_missing.carbon +++ b/toolchain/parser/testdata/if_expression/fail_else_missing.carbon @@ -14,12 +14,12 @@ // CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'true'}, -// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then'}, -// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'true'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'if', has_error: yes, subtree_size: 6}, // CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 12}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parser/testdata/if_expression/fail_then_expr_missing.carbon b/toolchain/parser/testdata/if_expression/fail_then_expr_missing.carbon index 3c78e4d1e3e5..ec136930c384 100644 --- a/toolchain/parser/testdata/if_expression/fail_then_expr_missing.carbon +++ b/toolchain/parser/testdata/if_expression/fail_then_expr_missing.carbon @@ -14,12 +14,12 @@ // CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'true'}, -// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then'}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'true'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', has_error: yes, subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'if', has_error: yes, subtree_size: 6}, // CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 12}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parser/testdata/if_expression/fail_then_missing.carbon b/toolchain/parser/testdata/if_expression/fail_then_missing.carbon index e6ad066d67b6..e26ef9042753 100644 --- a/toolchain/parser/testdata/if_expression/fail_then_missing.carbon +++ b/toolchain/parser/testdata/if_expression/fail_then_missing.carbon @@ -14,14 +14,13 @@ // CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'true'}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'true'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, // CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', has_error: yes, subtree_size: 6}, -// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 12}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'if', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 17}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/if_expression/precedence.carbon b/toolchain/parser/testdata/if_expression/precedence.carbon index d2c5dc990794..e69ff6eceb51 100644 --- a/toolchain/parser/testdata/if_expression/precedence.carbon +++ b/toolchain/parser/testdata/if_expression/precedence.carbon @@ -8,25 +8,25 @@ // CHECK:STDOUT: {kind: 'DeclaredName', text: 'F'}, // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, // CHECK:STDOUT: {kind: 'DeclaredName', text: 'b'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'bool'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'bool'}, // CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'bool'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'bool'}, // CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 10}, // CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else'}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 4}, // CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, // CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, // CHECK:STDOUT: {kind: 'InfixOperator', text: 'or', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'IfExpression', text: 'if', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 12}, // CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 14}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 25}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/semantics/semantics_builtin_kind.def b/toolchain/semantics/semantics_builtin_kind.def index c283024fa6bb..f54cbe9d6a32 100644 --- a/toolchain/semantics/semantics_builtin_kind.def +++ b/toolchain/semantics/semantics_builtin_kind.def @@ -50,6 +50,9 @@ CARBON_SEMANTICS_BUILTIN_KIND(InvalidType, "") // Keeping distinct placeholders can help find usages for later fixes. // ----------------------------------------------------------------------------- +// The type of bool literals and branch conditions, bool. +CARBON_SEMANTICS_BUILTIN_KIND(BoolType, "bool") + // The type of integer values and integer literals, currently always i32. CARBON_SEMANTICS_BUILTIN_KIND(IntegerType, "i32") diff --git a/toolchain/semantics/semantics_context.cpp b/toolchain/semantics/semantics_context.cpp index 412b4340991c..dd240e7144c3 100644 --- a/toolchain/semantics/semantics_context.cpp +++ b/toolchain/semantics/semantics_context.cpp @@ -61,7 +61,11 @@ auto SemanticsContext::VerifyOnFinish() -> void { } auto SemanticsContext::AddNode(SemanticsNode node) -> SemanticsNodeId { - auto block = node_block_stack_.PeekForAdd(); + return AddNodeToBlock(node_block_stack_.PeekForAdd(), node); +} + +auto SemanticsContext::AddNodeToBlock(SemanticsNodeBlockId block, + SemanticsNode node) -> SemanticsNodeId { CARBON_VLOG() << "AddNode " << block << ": " << node << "\n"; return semantics_ir_->AddNode(block, node); } diff --git a/toolchain/semantics/semantics_context.h b/toolchain/semantics/semantics_context.h index ad0b373673e9..d44cc4efbb87 100644 --- a/toolchain/semantics/semantics_context.h +++ b/toolchain/semantics/semantics_context.h @@ -35,6 +35,10 @@ class SemanticsContext { // Adds a node to the current block, returning the produced ID. auto AddNode(SemanticsNode node) -> SemanticsNodeId; + // Adds a node to the given block, returning the produced ID. + auto AddNodeToBlock(SemanticsNodeBlockId block, SemanticsNode node) + -> SemanticsNodeId; + // Pushes a parse tree node onto the stack, storing the SemanticsNode as the // result. auto AddNodeAndPush(ParseTree::Node parse_node, SemanticsNode node) -> void; diff --git a/toolchain/semantics/semantics_handle.cpp b/toolchain/semantics/semantics_handle.cpp index 7c89cbb2d6f4..eafc8cfa6697 100644 --- a/toolchain/semantics/semantics_handle.cpp +++ b/toolchain/semantics/semantics_handle.cpp @@ -178,21 +178,6 @@ auto SemanticsHandleIfConditionStart(SemanticsContext& context, return context.TODO(parse_node, "HandleIfConditionStart"); } -auto SemanticsHandleIfExpression(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleIfExpression"); -} - -auto SemanticsHandleIfExpressionElse(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleIfExpressionElse"); -} - -auto SemanticsHandleIfExpressionThen(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleIfExpressionThen"); -} - auto SemanticsHandleIfStatement(SemanticsContext& context, ParseTree::Node parse_node) -> bool { return context.TODO(parse_node, "HandleIfStatement"); @@ -275,6 +260,10 @@ auto SemanticsHandleLiteral(SemanticsContext& context, id)); break; } + case TokenKind::Bool: { + context.node_stack().Push(parse_node, SemanticsNodeId::BuiltinBoolType); + break; + } case TokenKind::IntegerTypeLiteral: { auto text = context.tokens().GetTokenText(token); if (text != "i32") { diff --git a/toolchain/semantics/semantics_handle_function.cpp b/toolchain/semantics/semantics_handle_function.cpp index 9fe0de7bcece..029429dfc9a9 100644 --- a/toolchain/semantics/semantics_handle_function.cpp +++ b/toolchain/semantics/semantics_handle_function.cpp @@ -18,17 +18,10 @@ auto SemanticsHandleFunctionDefinition(SemanticsContext& context, ParseNodeKind::FunctionDefinitionStart) { context.node_stack().PopAndIgnore(); } - auto function_id = context.node_stack().Pop( - ParseNodeKind::FunctionDefinitionStart); - + context.node_stack().PopAndDiscardId(ParseNodeKind::FunctionDefinitionStart); context.return_scope_stack().pop_back(); context.PopScope(); - auto body_id = context.node_block_stack().Pop(); - auto& function = context.semantics_ir().GetFunction(function_id); - CARBON_CHECK(!function.body_id.is_valid()) - << "Already have function body: " << function.body_id; - function.body_id = body_id; - + context.node_block_stack().Pop(); return true; } @@ -54,18 +47,21 @@ auto SemanticsHandleFunctionDefinitionStart(SemanticsContext& context, auto name_str = context.parse_tree().GetNodeText(name_node); auto name_id = context.semantics_ir().AddString(name_str); - // Add the callable, but with an invalid body for now. The body ID is only - // finalized on function completion. + // Create the entry block. + auto outer_block = context.node_block_stack().PeekForAdd(); + context.node_block_stack().Push(); + + // Add the callable. auto function_id = context.semantics_ir().AddFunction( {.name_id = name_id, .param_refs_id = param_refs_id, .return_type_id = return_type_id, - .body_id = SemanticsNodeBlockId::Invalid}); - auto decl_id = context.AddNode( + .body_id = context.node_block_stack().PeekForAdd()}); + auto decl_id = context.AddNodeToBlock( + outer_block, SemanticsNode::FunctionDeclaration::Make(fn_node, function_id)); context.AddNameToLookup(name_node, name_id, decl_id); - context.node_block_stack().Push(); context.PushScope(); for (auto ref_id : context.semantics_ir().GetNodeBlock(param_refs_id)) { auto ref = context.semantics_ir().GetNode(ref_id); diff --git a/toolchain/semantics/semantics_handle_if_expression.cpp b/toolchain/semantics/semantics_handle_if_expression.cpp new file mode 100644 index 000000000000..5294670c9aef --- /dev/null +++ b/toolchain/semantics/semantics_handle_if_expression.cpp @@ -0,0 +1,77 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "toolchain/semantics/semantics_context.h" + +namespace Carbon { + +auto SemanticsHandleIfExpressionIf(SemanticsContext& context, + ParseTree::Node if_node) -> bool { + auto cond_value_id = context.node_stack().Pop(); + + context.node_stack().Push(if_node); + + // Convert the condition to `bool`. + cond_value_id = context.ImplicitAsRequired( + if_node, cond_value_id, + context.CanonicalizeType(SemanticsNodeId::BuiltinBoolType)); + + // Stop emitting the current block. We'll add some branch instructions to it + // later, but we don't want it on the stack any more. + auto if_block_id = context.node_block_stack().PeekForAdd(); + context.node_block_stack().Pop(); + + // Create the resumption block, `else` block, and `then` block, and branches + // to them. + context.node_block_stack().Push(); + auto else_block_id = context.node_block_stack().PushForAdd(); + auto then_block_id = context.node_block_stack().PushForAdd(); + context.AddNodeToBlock( + if_block_id, + SemanticsNode::BranchIf::Make(if_node, then_block_id, cond_value_id)); + context.AddNodeToBlock(if_block_id, + SemanticsNode::Branch::Make(if_node, else_block_id)); + return true; +} + +auto SemanticsHandleIfExpressionThen(SemanticsContext& context, + ParseTree::Node then_node) -> bool { + context.node_stack().Push(then_node, context.node_block_stack().Pop()); + return true; +} + +auto SemanticsHandleIfExpressionElse(SemanticsContext& context, + ParseTree::Node else_node) -> bool { + auto else_value_id = context.node_stack().Pop(); + auto [then_node, then_end_block_id] = + context.node_stack().PopWithParseNode( + ParseNodeKind::IfExpressionThen); + auto then_value_id = context.node_stack().Pop(); + auto if_node = + context.node_stack().PopForSoloParseNode(ParseNodeKind::IfExpressionIf); + + // Convert the `else` value to the `then` value's type, and finish the `else` + // block. + // TODO: Find a common type, and convert both operands to it instead. + auto result_type_id = context.semantics_ir().GetNode(then_value_id).type_id(); + else_value_id = + context.ImplicitAsRequired(else_node, else_value_id, result_type_id); + auto else_end_block_id = context.node_block_stack().Pop(); + + // Create branches to the resumption block. + auto resume_block_id = context.node_block_stack().PeekForAdd(); + context.AddNodeToBlock(then_end_block_id, + SemanticsNode::BranchWithArg::Make( + then_node, resume_block_id, then_value_id)); + context.AddNodeToBlock(else_end_block_id, + SemanticsNode::BranchWithArg::Make( + else_node, resume_block_id, else_value_id)); + + // Obtain the value in the resumption block and push it. + context.AddNodeAndPush( + if_node, SemanticsNode::BlockArg::Make(if_node, result_type_id)); + return true; +} + +} // namespace Carbon diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index 6df1cc559920..2d2a33d0d181 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/toolchain/semantics/semantics_ir.cpp @@ -198,6 +198,10 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { case SemanticsNodeKind::Assign: case SemanticsNodeKind::BinaryOperatorAdd: case SemanticsNodeKind::BindName: + case SemanticsNodeKind::BlockArg: + case SemanticsNodeKind::Branch: + case SemanticsNodeKind::BranchIf: + case SemanticsNodeKind::BranchWithArg: case SemanticsNodeKind::Builtin: case SemanticsNodeKind::Call: case SemanticsNodeKind::CodeBlock: diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index 6b05de13470e..521ac631040c 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -262,6 +262,19 @@ class SemanticsNode { SemanticsStringId /*name_id*/, SemanticsNodeId /*node_id*/>; + using BlockArg = Factory; + + using Branch = FactoryNoType; + + using BranchIf = FactoryNoType; + + using BranchWithArg = FactoryNoType; + class Builtin { public: static auto Make(SemanticsBuiltinKind builtin_kind, SemanticsTypeId type_id) diff --git a/toolchain/semantics/semantics_node_block_stack.h b/toolchain/semantics/semantics_node_block_stack.h index 7a724b0cebf8..3d850bca0c83 100644 --- a/toolchain/semantics/semantics_node_block_stack.h +++ b/toolchain/semantics/semantics_node_block_stack.h @@ -27,6 +27,12 @@ class SemanticsNodeBlockStack { // order to support lazy allocation. auto Push() -> void; + // Allocates and pushes a new node block. + auto PushForAdd() -> SemanticsNodeBlockId { + Push(); + return PeekForAdd(); + } + // Peeks at the top node block. This does not trigger lazy allocation, so the // returned node block may be invalid. auto Peek() -> SemanticsNodeBlockId { return stack_.back(); } diff --git a/toolchain/semantics/semantics_node_kind.def b/toolchain/semantics/semantics_node_kind.def index 784e222a9ae1..b70903dbfd23 100644 --- a/toolchain/semantics/semantics_node_kind.def +++ b/toolchain/semantics/semantics_node_kind.def @@ -23,6 +23,10 @@ CARBON_SEMANTICS_NODE_KIND(CrossReference) CARBON_SEMANTICS_NODE_KIND(Assign) CARBON_SEMANTICS_NODE_KIND(BinaryOperatorAdd) CARBON_SEMANTICS_NODE_KIND(BindName) +CARBON_SEMANTICS_NODE_KIND(BlockArg) +CARBON_SEMANTICS_NODE_KIND(Branch) +CARBON_SEMANTICS_NODE_KIND(BranchIf) +CARBON_SEMANTICS_NODE_KIND(BranchWithArg) CARBON_SEMANTICS_NODE_KIND(Builtin) CARBON_SEMANTICS_NODE_KIND(Call) CARBON_SEMANTICS_NODE_KIND(CodeBlock) diff --git a/toolchain/semantics/testdata/basics/fail_name_lookup.carbon b/toolchain/semantics/testdata/basics/fail_name_lookup.carbon index 9fed8695edf8..439b679f61fa 100644 --- a/toolchain/semantics/testdata/basics/fail_name_lookup.carbon +++ b/toolchain/semantics/testdata/basics/fail_name_lookup.carbon @@ -5,7 +5,7 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str0, param_refs: block0, body: block0}, +// CHECK:STDOUT: {name: str0, param_refs: block0, body: block2}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: ] @@ -26,6 +26,8 @@ // CHECK:STDOUT: [ // CHECK:STDOUT: node+0, // CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], // CHECK:STDOUT: ] fn Main() { diff --git a/toolchain/semantics/testdata/function/call/fail_param_count.carbon b/toolchain/semantics/testdata/function/call/fail_param_count.carbon index 1b4162a4885f..c9641dea66ab 100644 --- a/toolchain/semantics/testdata/function/call/fail_param_count.carbon +++ b/toolchain/semantics/testdata/function/call/fail_param_count.carbon @@ -5,10 +5,10 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str0, param_refs: block0, body: block0}, -// CHECK:STDOUT: {name: str2, param_refs: block3, body: block0}, -// CHECK:STDOUT: {name: str4, param_refs: block5, body: block0}, -// CHECK:STDOUT: {name: str5, param_refs: block0, body: block6}, +// CHECK:STDOUT: {name: str0, param_refs: block0, body: block2}, +// CHECK:STDOUT: {name: str2, param_refs: block4, body: block5}, +// CHECK:STDOUT: {name: str4, param_refs: block7, body: block8}, +// CHECK:STDOUT: {name: str5, param_refs: block0, body: block9}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: 1, @@ -66,6 +66,8 @@ // CHECK:STDOUT: node+9, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+1, // CHECK:STDOUT: node+2, // CHECK:STDOUT: ], @@ -73,6 +75,8 @@ // CHECK:STDOUT: node+2, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+4, // CHECK:STDOUT: node+5, // CHECK:STDOUT: node+6, @@ -83,6 +87,8 @@ // CHECK:STDOUT: node+7, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+10, // CHECK:STDOUT: node+11, // CHECK:STDOUT: node+12, diff --git a/toolchain/semantics/testdata/function/call/fail_param_type.carbon b/toolchain/semantics/testdata/function/call/fail_param_type.carbon index e83edf36a404..05f87b55c615 100644 --- a/toolchain/semantics/testdata/function/call/fail_param_type.carbon +++ b/toolchain/semantics/testdata/function/call/fail_param_type.carbon @@ -5,8 +5,8 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str1, param_refs: block2, body: block0}, -// CHECK:STDOUT: {name: str2, param_refs: block0, body: block4}, +// CHECK:STDOUT: {name: str1, param_refs: block2, body: block4}, +// CHECK:STDOUT: {name: str2, param_refs: block0, body: block5}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: ] @@ -46,6 +46,8 @@ // CHECK:STDOUT: node+3, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+4, // CHECK:STDOUT: node+5, // CHECK:STDOUT: ], diff --git a/toolchain/semantics/testdata/function/call/more_param_ir.carbon b/toolchain/semantics/testdata/function/call/more_param_ir.carbon index 5a2dfdcb2ff5..4225e5de0583 100644 --- a/toolchain/semantics/testdata/function/call/more_param_ir.carbon +++ b/toolchain/semantics/testdata/function/call/more_param_ir.carbon @@ -5,8 +5,8 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str3, param_refs: block2, body: block0}, -// CHECK:STDOUT: {name: str4, param_refs: block0, body: block4}, +// CHECK:STDOUT: {name: str3, param_refs: block2, body: block4}, +// CHECK:STDOUT: {name: str4, param_refs: block0, body: block5}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: 1, @@ -50,7 +50,7 @@ // CHECK:STDOUT: {kind: StubReference, arg0: node+16, type: type0}, // CHECK:STDOUT: {kind: IntegerLiteral, arg0: int5, type: type0}, // CHECK:STDOUT: {kind: StubReference, arg0: node+18, type: type0}, -// CHECK:STDOUT: {kind: Call, arg0: block5, arg1: function0}, +// CHECK:STDOUT: {kind: Call, arg0: block6, arg1: function0}, // CHECK:STDOUT: ] // CHECK:STDOUT: node_blocks: [ // CHECK:STDOUT: [ @@ -73,6 +73,8 @@ // CHECK:STDOUT: node+7, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+8, // CHECK:STDOUT: node+9, // CHECK:STDOUT: node+10, diff --git a/toolchain/semantics/testdata/function/call/params_one.carbon b/toolchain/semantics/testdata/function/call/params_one.carbon index f4f8e5b1e184..3094c285aed4 100644 --- a/toolchain/semantics/testdata/function/call/params_one.carbon +++ b/toolchain/semantics/testdata/function/call/params_one.carbon @@ -5,8 +5,8 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str1, param_refs: block2, body: block0}, -// CHECK:STDOUT: {name: str2, param_refs: block0, body: block4}, +// CHECK:STDOUT: {name: str1, param_refs: block2, body: block4}, +// CHECK:STDOUT: {name: str2, param_refs: block0, body: block5}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: 1, @@ -29,7 +29,7 @@ // CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function1}, // CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type0}, // CHECK:STDOUT: {kind: StubReference, arg0: node+4, type: type0}, -// CHECK:STDOUT: {kind: Call, arg0: block5, arg1: function0}, +// CHECK:STDOUT: {kind: Call, arg0: block6, arg1: function0}, // CHECK:STDOUT: ] // CHECK:STDOUT: node_blocks: [ // CHECK:STDOUT: [ @@ -46,6 +46,8 @@ // CHECK:STDOUT: node+3, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+4, // CHECK:STDOUT: node+5, // CHECK:STDOUT: node+6, diff --git a/toolchain/semantics/testdata/function/call/params_one_comma.carbon b/toolchain/semantics/testdata/function/call/params_one_comma.carbon index 5ac3e2f7e1df..c3923fbcc37b 100644 --- a/toolchain/semantics/testdata/function/call/params_one_comma.carbon +++ b/toolchain/semantics/testdata/function/call/params_one_comma.carbon @@ -5,8 +5,8 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str1, param_refs: block2, body: block0}, -// CHECK:STDOUT: {name: str2, param_refs: block0, body: block4}, +// CHECK:STDOUT: {name: str1, param_refs: block2, body: block4}, +// CHECK:STDOUT: {name: str2, param_refs: block0, body: block5}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: 1, @@ -30,10 +30,10 @@ // CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function1}, // CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type0}, // CHECK:STDOUT: {kind: StubReference, arg0: node+4, type: type0}, -// CHECK:STDOUT: {kind: Call, arg0: block5, arg1: function0}, +// CHECK:STDOUT: {kind: Call, arg0: block6, arg1: function0}, // CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0}, // CHECK:STDOUT: {kind: StubReference, arg0: node+7, type: type0}, -// CHECK:STDOUT: {kind: Call, arg0: block6, arg1: function0}, +// CHECK:STDOUT: {kind: Call, arg0: block7, arg1: function0}, // CHECK:STDOUT: ] // CHECK:STDOUT: node_blocks: [ // CHECK:STDOUT: [ @@ -50,6 +50,8 @@ // CHECK:STDOUT: node+3, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+4, // CHECK:STDOUT: node+5, // CHECK:STDOUT: node+6, diff --git a/toolchain/semantics/testdata/function/call/params_two.carbon b/toolchain/semantics/testdata/function/call/params_two.carbon index 41beda7f60e1..333fba654a52 100644 --- a/toolchain/semantics/testdata/function/call/params_two.carbon +++ b/toolchain/semantics/testdata/function/call/params_two.carbon @@ -5,8 +5,8 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str2, param_refs: block2, body: block0}, -// CHECK:STDOUT: {name: str3, param_refs: block0, body: block4}, +// CHECK:STDOUT: {name: str2, param_refs: block2, body: block4}, +// CHECK:STDOUT: {name: str3, param_refs: block0, body: block5}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: 1, @@ -35,7 +35,7 @@ // CHECK:STDOUT: {kind: StubReference, arg0: node+6, type: type0}, // CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0}, // CHECK:STDOUT: {kind: StubReference, arg0: node+8, type: type0}, -// CHECK:STDOUT: {kind: Call, arg0: block5, arg1: function0}, +// CHECK:STDOUT: {kind: Call, arg0: block6, arg1: function0}, // CHECK:STDOUT: ] // CHECK:STDOUT: node_blocks: [ // CHECK:STDOUT: [ @@ -55,6 +55,8 @@ // CHECK:STDOUT: node+5, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+6, // CHECK:STDOUT: node+7, // CHECK:STDOUT: node+8, diff --git a/toolchain/semantics/testdata/function/call/params_two_comma.carbon b/toolchain/semantics/testdata/function/call/params_two_comma.carbon index 50afe4998794..f65b74d5ad74 100644 --- a/toolchain/semantics/testdata/function/call/params_two_comma.carbon +++ b/toolchain/semantics/testdata/function/call/params_two_comma.carbon @@ -5,8 +5,8 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str2, param_refs: block2, body: block0}, -// CHECK:STDOUT: {name: str3, param_refs: block0, body: block4}, +// CHECK:STDOUT: {name: str2, param_refs: block2, body: block4}, +// CHECK:STDOUT: {name: str3, param_refs: block0, body: block5}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: 1, @@ -37,12 +37,12 @@ // CHECK:STDOUT: {kind: StubReference, arg0: node+6, type: type0}, // CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0}, // CHECK:STDOUT: {kind: StubReference, arg0: node+8, type: type0}, -// CHECK:STDOUT: {kind: Call, arg0: block5, arg1: function0}, +// CHECK:STDOUT: {kind: Call, arg0: block6, arg1: function0}, // CHECK:STDOUT: {kind: IntegerLiteral, arg0: int2, type: type0}, // CHECK:STDOUT: {kind: StubReference, arg0: node+11, type: type0}, // CHECK:STDOUT: {kind: IntegerLiteral, arg0: int3, type: type0}, // CHECK:STDOUT: {kind: StubReference, arg0: node+13, type: type0}, -// CHECK:STDOUT: {kind: Call, arg0: block6, arg1: function0}, +// CHECK:STDOUT: {kind: Call, arg0: block7, arg1: function0}, // CHECK:STDOUT: ] // CHECK:STDOUT: node_blocks: [ // CHECK:STDOUT: [ @@ -62,6 +62,8 @@ // CHECK:STDOUT: node+5, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+6, // CHECK:STDOUT: node+7, // CHECK:STDOUT: node+8, diff --git a/toolchain/semantics/testdata/function/call/params_zero.carbon b/toolchain/semantics/testdata/function/call/params_zero.carbon index 50ea6007bbc4..111e132ebaa5 100644 --- a/toolchain/semantics/testdata/function/call/params_zero.carbon +++ b/toolchain/semantics/testdata/function/call/params_zero.carbon @@ -5,8 +5,8 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str0, param_refs: block0, body: block0}, -// CHECK:STDOUT: {name: str1, param_refs: block0, body: block2}, +// CHECK:STDOUT: {name: str0, param_refs: block0, body: block2}, +// CHECK:STDOUT: {name: str1, param_refs: block0, body: block3}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: ] @@ -32,6 +32,8 @@ // CHECK:STDOUT: node+1, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+2, // CHECK:STDOUT: ], // CHECK:STDOUT: ] diff --git a/toolchain/semantics/testdata/function/definition/fail_param_name_conflict.carbon b/toolchain/semantics/testdata/function/definition/fail_param_name_conflict.carbon index 1594468c8510..b09f4457a9bb 100644 --- a/toolchain/semantics/testdata/function/definition/fail_param_name_conflict.carbon +++ b/toolchain/semantics/testdata/function/definition/fail_param_name_conflict.carbon @@ -5,7 +5,7 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str1, param_refs: block2, body: block0}, +// CHECK:STDOUT: {name: str1, param_refs: block2, body: block4}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: ] @@ -42,6 +42,8 @@ // CHECK:STDOUT: [ // CHECK:STDOUT: node+4, // CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], // CHECK:STDOUT: ] // CHECK:STDERR: fail_param_name_conflict.carbon:[[@LINE+2]]:16: Redefining a in the same scope. diff --git a/toolchain/semantics/testdata/function/definition/order.carbon b/toolchain/semantics/testdata/function/definition/order.carbon index 517e6e4b4084..6b25ef48edd3 100644 --- a/toolchain/semantics/testdata/function/definition/order.carbon +++ b/toolchain/semantics/testdata/function/definition/order.carbon @@ -5,9 +5,9 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str0, param_refs: block0, body: block0}, -// CHECK:STDOUT: {name: str1, param_refs: block0, body: block0}, -// CHECK:STDOUT: {name: str2, param_refs: block0, body: block0}, +// CHECK:STDOUT: {name: str0, param_refs: block0, body: block2}, +// CHECK:STDOUT: {name: str1, param_refs: block0, body: block3}, +// CHECK:STDOUT: {name: str2, param_refs: block0, body: block4}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: ] @@ -34,6 +34,12 @@ // CHECK:STDOUT: node+1, // CHECK:STDOUT: node+2, // CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], // CHECK:STDOUT: ] fn Foo() {} diff --git a/toolchain/semantics/testdata/function/definition/params_one.carbon b/toolchain/semantics/testdata/function/definition/params_one.carbon index c0169825cd08..0ffe39d4df14 100644 --- a/toolchain/semantics/testdata/function/definition/params_one.carbon +++ b/toolchain/semantics/testdata/function/definition/params_one.carbon @@ -5,7 +5,7 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str1, param_refs: block2, body: block0}, +// CHECK:STDOUT: {name: str1, param_refs: block2, body: block4}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: ] @@ -37,6 +37,8 @@ // CHECK:STDOUT: [ // CHECK:STDOUT: node+2, // CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], // CHECK:STDOUT: ] fn Foo(a: i32) {} diff --git a/toolchain/semantics/testdata/function/definition/params_one_comma.carbon b/toolchain/semantics/testdata/function/definition/params_one_comma.carbon index d8f40919a478..88071f7e3489 100644 --- a/toolchain/semantics/testdata/function/definition/params_one_comma.carbon +++ b/toolchain/semantics/testdata/function/definition/params_one_comma.carbon @@ -5,7 +5,7 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str1, param_refs: block2, body: block0}, +// CHECK:STDOUT: {name: str1, param_refs: block2, body: block4}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: ] @@ -37,6 +37,8 @@ // CHECK:STDOUT: [ // CHECK:STDOUT: node+2, // CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], // CHECK:STDOUT: ] fn Foo(a: i32,) {} diff --git a/toolchain/semantics/testdata/function/definition/params_two.carbon b/toolchain/semantics/testdata/function/definition/params_two.carbon index 0e1b618101e5..fae5404c628e 100644 --- a/toolchain/semantics/testdata/function/definition/params_two.carbon +++ b/toolchain/semantics/testdata/function/definition/params_two.carbon @@ -5,7 +5,7 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str2, param_refs: block2, body: block0}, +// CHECK:STDOUT: {name: str2, param_refs: block2, body: block4}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: ] @@ -43,6 +43,8 @@ // CHECK:STDOUT: [ // CHECK:STDOUT: node+4, // CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], // CHECK:STDOUT: ] fn Foo(a: i32, b: i32) {} diff --git a/toolchain/semantics/testdata/function/definition/params_two_comma.carbon b/toolchain/semantics/testdata/function/definition/params_two_comma.carbon index b508b2d1ddb7..dd8306af9458 100644 --- a/toolchain/semantics/testdata/function/definition/params_two_comma.carbon +++ b/toolchain/semantics/testdata/function/definition/params_two_comma.carbon @@ -5,7 +5,7 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str2, param_refs: block2, body: block0}, +// CHECK:STDOUT: {name: str2, param_refs: block2, body: block4}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: ] @@ -43,6 +43,8 @@ // CHECK:STDOUT: [ // CHECK:STDOUT: node+4, // CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], // CHECK:STDOUT: ] fn Foo(a: i32, b: i32,) {} diff --git a/toolchain/semantics/testdata/function/definition/params_zero.carbon b/toolchain/semantics/testdata/function/definition/params_zero.carbon index b1fe8b956a8b..2d1be58c2a47 100644 --- a/toolchain/semantics/testdata/function/definition/params_zero.carbon +++ b/toolchain/semantics/testdata/function/definition/params_zero.carbon @@ -5,7 +5,7 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str0, param_refs: block0, body: block0}, +// CHECK:STDOUT: {name: str0, param_refs: block0, body: block2}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: ] @@ -26,6 +26,8 @@ // CHECK:STDOUT: [ // CHECK:STDOUT: node+0, // CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], // CHECK:STDOUT: ] fn Foo() {} diff --git a/toolchain/semantics/testdata/function/definition/same_param_name.carbon b/toolchain/semantics/testdata/function/definition/same_param_name.carbon index 06ac0f3ae416..63c3d177d347 100644 --- a/toolchain/semantics/testdata/function/definition/same_param_name.carbon +++ b/toolchain/semantics/testdata/function/definition/same_param_name.carbon @@ -5,8 +5,8 @@ // AUTOUPDATE // CHECK:STDOUT: cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ -// CHECK:STDOUT: {name: str1, param_refs: block2, body: block0}, -// CHECK:STDOUT: {name: str2, param_refs: block5, body: block0}, +// CHECK:STDOUT: {name: str1, param_refs: block2, body: block4}, +// CHECK:STDOUT: {name: str2, param_refs: block6, body: block7}, // CHECK:STDOUT: ] // CHECK:STDOUT: integer_literals: [ // CHECK:STDOUT: ] @@ -44,12 +44,16 @@ // CHECK:STDOUT: node+5, // CHECK:STDOUT: ], // CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ // CHECK:STDOUT: node+3, // CHECK:STDOUT: node+4, // CHECK:STDOUT: ], // CHECK:STDOUT: [ // CHECK:STDOUT: node+4, // CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], // CHECK:STDOUT: ] fn Foo(a: i32) {} diff --git a/toolchain/semantics/testdata/if_expression/basic.carbon b/toolchain/semantics/testdata/if_expression/basic.carbon new file mode 100644 index 000000000000..ca2c2fe1f11c --- /dev/null +++ b/toolchain/semantics/testdata/if_expression/basic.carbon @@ -0,0 +1,80 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str3, param_refs: block2, return_type: type1, body: block4}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: b, +// CHECK:STDOUT: n, +// CHECK:STDOUT: m, +// CHECK:STDOUT: F, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeBoolType, +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: VarStorage, type: type0}, +// CHECK:STDOUT: {kind: BindName, arg0: str0, arg1: node+0, type: type0}, +// CHECK:STDOUT: {kind: VarStorage, type: type1}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+2, type: type1}, +// CHECK:STDOUT: {kind: VarStorage, type: type1}, +// CHECK:STDOUT: {kind: BindName, arg0: str2, arg1: node+4, type: type1}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: BranchIf, arg0: block6, arg1: node+0}, +// CHECK:STDOUT: {kind: Branch, arg0: block5}, +// CHECK:STDOUT: {kind: BinaryOperatorAdd, arg0: node+2, arg1: node+4, type: type1}, +// CHECK:STDOUT: {kind: BinaryOperatorAdd, arg0: node+4, arg1: node+2, type: type1}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block7, arg1: node+9}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block7, arg1: node+10}, +// CHECK:STDOUT: {kind: BlockArg, type: type1}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+13, type: type1}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: node+14, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn F(b: bool, n: i32, m: i32) -> i32 { + return if b then n + m else m + n; +} diff --git a/toolchain/semantics/testdata/if_expression/control_flow.carbon b/toolchain/semantics/testdata/if_expression/control_flow.carbon new file mode 100644 index 000000000000..deadb0688725 --- /dev/null +++ b/toolchain/semantics/testdata/if_expression/control_flow.carbon @@ -0,0 +1,93 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str0, param_refs: block0, return_type: type0, body: block2}, +// CHECK:STDOUT: {name: str1, param_refs: block0, return_type: type0, body: block3}, +// CHECK:STDOUT: {name: str3, param_refs: block5, return_type: type0, body: block6}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: 1, +// CHECK:STDOUT: 2, +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: A, +// CHECK:STDOUT: B, +// CHECK:STDOUT: b, +// CHECK:STDOUT: F, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: nodeBoolType, +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+1, type: type0}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function1}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+4, type: type0}, +// CHECK:STDOUT: {kind: VarStorage, type: type1}, +// CHECK:STDOUT: {kind: BindName, arg0: str2, arg1: node+6, type: type1}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function2}, +// CHECK:STDOUT: {kind: BranchIf, arg0: block8, arg1: node+6}, +// CHECK:STDOUT: {kind: Branch, arg0: block7}, +// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function0, type: type0}, +// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function1, type: type0}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block9, arg1: node+11}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block9, arg1: node+12}, +// CHECK:STDOUT: {kind: BlockArg, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+15, type: type0}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: node+14, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+15, +// CHECK:STDOUT: node+16, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn A() -> i32 { return 1; } +fn B() -> i32 { return 2; } + +fn F(b: bool) -> i32 { + return if b then A() else B(); +} diff --git a/toolchain/semantics/testdata/if_expression/nested.carbon b/toolchain/semantics/testdata/if_expression/nested.carbon new file mode 100644 index 000000000000..da14e4d65702 --- /dev/null +++ b/toolchain/semantics/testdata/if_expression/nested.carbon @@ -0,0 +1,120 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str3, param_refs: block2, return_type: type1, body: block4}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: 1, +// CHECK:STDOUT: 2, +// CHECK:STDOUT: 3, +// CHECK:STDOUT: 4, +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: a, +// CHECK:STDOUT: b, +// CHECK:STDOUT: c, +// CHECK:STDOUT: F, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeBoolType, +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: VarStorage, type: type0}, +// CHECK:STDOUT: {kind: BindName, arg0: str0, arg1: node+0, type: type0}, +// CHECK:STDOUT: {kind: VarStorage, type: type0}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+2, type: type0}, +// CHECK:STDOUT: {kind: VarStorage, type: type0}, +// CHECK:STDOUT: {kind: BindName, arg0: str2, arg1: node+4, type: type0}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: BranchIf, arg0: block6, arg1: node+0}, +// CHECK:STDOUT: {kind: Branch, arg0: block5}, +// CHECK:STDOUT: {kind: BranchIf, arg0: block8, arg1: node+2}, +// CHECK:STDOUT: {kind: Branch, arg0: block7}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type1}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type1}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block9, arg1: node+11}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block9, arg1: node+12}, +// CHECK:STDOUT: {kind: BlockArg, type: type1}, +// CHECK:STDOUT: {kind: BranchIf, arg0: block11, arg1: node+4}, +// CHECK:STDOUT: {kind: Branch, arg0: block10}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int2, type: type1}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int3, type: type1}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block12, arg1: node+18}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block12, arg1: node+19}, +// CHECK:STDOUT: {kind: BlockArg, type: type1}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block13, arg1: node+15}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block13, arg1: node+22}, +// CHECK:STDOUT: {kind: BlockArg, type: type1}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+25, type: type1}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+16, +// CHECK:STDOUT: node+17, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: node+14, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+15, +// CHECK:STDOUT: node+23, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+19, +// CHECK:STDOUT: node+21, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+18, +// CHECK:STDOUT: node+20, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+22, +// CHECK:STDOUT: node+24, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+25, +// CHECK:STDOUT: node+26, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn F(a: bool, b: bool, c: bool) -> i32 { + return if a then if b then 1 else 2 else if c then 3 else 4; +}