From 06ce3b0161571c49a15d74b77c644b1cd32d40c1 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 14 Jun 2023 13:03:13 -0700 Subject: [PATCH] Parsing, semantic analysis, and lowering for `and`, `or`, `not`. (#2897) Lowering for `and` and `or` is not yet complete because `Branch` lowering isn't done yet. --- toolchain/lexer/token_kind.cpp | 9 ++ toolchain/lexer/token_kind.def | 14 ++- toolchain/lexer/token_kind.h | 4 + toolchain/lexer/tokenized_buffer.cpp | 1 + toolchain/lexer/tokenized_buffer.h | 8 ++ toolchain/lowering/lowering_handle.cpp | 7 ++ .../lowering/testdata/operators/not.carbon | 17 +++ toolchain/parser/parse_node_kind.def | 7 ++ toolchain/parser/parse_tree.cpp | 9 +- toolchain/parser/parse_tree.h | 2 +- toolchain/parser/parser_handle_expression.cpp | 10 +- .../testdata/if_expression/precedence.carbon | 23 ++-- .../testdata/operators/associative.carbon | 14 ++- .../operators/fail_precedence_and_or.carbon | 14 ++- .../operators/fail_precedence_or_and.carbon | 14 ++- .../testdata/operators/precedence_not.carbon | 18 +-- .../parser/testdata/operators/prefix.carbon | 9 ++ toolchain/semantics/semantics_context.cpp | 7 ++ toolchain/semantics/semantics_context.h | 4 + toolchain/semantics/semantics_handle.cpp | 105 ++++++++++++++++-- .../semantics_handle_if_expression.cpp | 4 +- toolchain/semantics/semantics_ir.cpp | 1 + toolchain/semantics/semantics_node.h | 9 ++ .../semantics/semantics_node_block_stack.h | 7 ++ toolchain/semantics/semantics_node_kind.def | 1 + .../semantics/testdata/operators/and.carbon | 78 +++++++++++++ .../semantics/testdata/operators/or.carbon | 80 +++++++++++++ .../testdata/operators/unary_op.carbon | 49 ++++++++ 28 files changed, 470 insertions(+), 55 deletions(-) create mode 100644 toolchain/lowering/testdata/operators/not.carbon create mode 100644 toolchain/semantics/testdata/operators/and.carbon create mode 100644 toolchain/semantics/testdata/operators/or.carbon create mode 100644 toolchain/semantics/testdata/operators/unary_op.carbon diff --git a/toolchain/lexer/token_kind.cpp b/toolchain/lexer/token_kind.cpp index ac262ce6902b..6b5cca17eff3 100644 --- a/toolchain/lexer/token_kind.cpp +++ b/toolchain/lexer/token_kind.cpp @@ -104,4 +104,13 @@ auto TokenKind::fixed_spelling() const -> llvm::StringRef { return Table[AsInt()]; } +auto TokenKind::expected_parse_tree_size() const -> int { + static constexpr int8_t Table[] = { +#define CARBON_TOKEN(Name) 1, +#define CARBON_TOKEN_WITH_VIRTUAL_NODE(size) 1 + size +#include "toolchain/lexer/token_kind.def" + }; + return Table[AsInt()]; +} + } // namespace Carbon diff --git a/toolchain/lexer/token_kind.def b/toolchain/lexer/token_kind.def index 54add7ea6932..37a37ef24e8c 100644 --- a/toolchain/lexer/token_kind.def +++ b/toolchain/lexer/token_kind.def @@ -21,6 +21,9 @@ // - CARBON_KEYWORD_TOKEN(Name, Spelling) // Defines a keyword which has the provided spelling, such as `if`. // Spellings must be unique. +// - CARBON_TOKEN_WITH_VIRTUAL_NODE(Token) +// Wrapped around one of the abokve _TOKEN macros, indicates that this +// token has one additional virtual node in the parse tree. // // This tree represents the subset relationship between these macros, where if a // specific x-macro isn't defined, it'll fall back to the parent macro. @@ -33,6 +36,10 @@ #define CARBON_SYMBOL_TOKEN(Name, Spelling) CARBON_TOKEN(Name) #endif +#ifndef CARBON_TOKEN_WITH_VIRTUAL_NODE +#define CARBON_TOKEN_WITH_VIRTUAL_NODE(Name) Name +#endif + // Note that symbols need to be ordered from longest to shortest to effectively // provide max-munch lexing. @@ -116,7 +123,8 @@ CARBON_CLOSING_GROUP_SYMBOL_TOKEN(CloseSquareBracket, "]", OpenSquareBracket) CARBON_KEYWORD_TOKEN(Abstract, "abstract") CARBON_KEYWORD_TOKEN(Addr, "addr") CARBON_KEYWORD_TOKEN(Alias, "alias") -CARBON_KEYWORD_TOKEN(And, "and") +CARBON_TOKEN_WITH_VIRTUAL_NODE( + CARBON_KEYWORD_TOKEN(And, "and")) CARBON_KEYWORD_TOKEN(Api, "api") CARBON_KEYWORD_TOKEN(As, "as") CARBON_KEYWORD_TOKEN(Auto, "auto") @@ -149,7 +157,8 @@ CARBON_KEYWORD_TOKEN(Match, "match") CARBON_KEYWORD_TOKEN(Namespace, "namespace") CARBON_KEYWORD_TOKEN(Not, "not") CARBON_KEYWORD_TOKEN(Observe, "observe") -CARBON_KEYWORD_TOKEN(Or, "or") +CARBON_TOKEN_WITH_VIRTUAL_NODE( + CARBON_KEYWORD_TOKEN(Or, "or")) CARBON_KEYWORD_TOKEN(Override, "override") CARBON_KEYWORD_TOKEN(Package, "package") CARBON_KEYWORD_TOKEN(Partial, "partial") @@ -186,3 +195,4 @@ CARBON_TOKEN(Error) CARBON_TOKEN(EndOfFile) #undef CARBON_TOKEN +#undef CARBON_TOKEN_WITH_VIRTUAL_NODE diff --git a/toolchain/lexer/token_kind.h b/toolchain/lexer/token_kind.h index b206bc55c183..1df50e188b07 100644 --- a/toolchain/lexer/token_kind.h +++ b/toolchain/lexer/token_kind.h @@ -60,6 +60,10 @@ class TokenKind : public CARBON_ENUM_BASE(TokenKind) { // Otherwise returns an empty string. [[nodiscard]] auto fixed_spelling() const -> llvm::StringRef; + // Get the expected number of parse tree nodes that will be created for this + // token. + [[nodiscard]] auto expected_parse_tree_size() const -> int; + // Test whether this token kind is in the provided list. [[nodiscard]] auto IsOneOf(std::initializer_list kinds) const -> bool { diff --git a/toolchain/lexer/tokenized_buffer.cpp b/toolchain/lexer/tokenized_buffer.cpp index c17799e53cc0..5094c319d0ad 100644 --- a/toolchain/lexer/tokenized_buffer.cpp +++ b/toolchain/lexer/tokenized_buffer.cpp @@ -876,6 +876,7 @@ auto TokenizedBuffer::GetTokenInfo(Token token) const -> const TokenInfo& { auto TokenizedBuffer::AddToken(TokenInfo info) -> Token { token_infos_.push_back(info); + expected_parse_tree_size_ += info.kind.expected_parse_tree_size(); return Token(static_cast(token_infos_.size()) - 1); } diff --git a/toolchain/lexer/tokenized_buffer.h b/toolchain/lexer/tokenized_buffer.h index 039f06d5728f..20de687a451f 100644 --- a/toolchain/lexer/tokenized_buffer.h +++ b/toolchain/lexer/tokenized_buffer.h @@ -284,6 +284,10 @@ class TokenizedBuffer { [[nodiscard]] auto size() const -> int { return token_infos_.size(); } + [[nodiscard]] auto expected_parse_tree_size() const -> int { + return expected_parse_tree_size_; + } + private: // Implementation detail struct implementing the actual lexer logic. class Lexer; @@ -403,6 +407,10 @@ class TokenizedBuffer { llvm::DenseMap identifier_map_; + // The number of parse tree nodes that we expect to be created for the tokens + // in this buffer. + int expected_parse_tree_size_ = 0; + bool has_errors_ = false; }; diff --git a/toolchain/lowering/lowering_handle.cpp b/toolchain/lowering/lowering_handle.cpp index 4843f14c0f65..d6267af6a69a 100644 --- a/toolchain/lowering/lowering_handle.cpp +++ b/toolchain/lowering/lowering_handle.cpp @@ -209,6 +209,13 @@ auto LoweringHandleStubReference(LoweringContext& context, context.SetLocal(node_id, context.GetLocal(node.GetAsStubReference())); } +auto LoweringHandleUnaryOperatorNot(LoweringContext& context, + SemanticsNodeId node_id, SemanticsNode node) + -> void { + context.SetLocal(node_id, context.builder().CreateNot(context.GetLocal( + node.GetAsUnaryOperatorNot()))); +} + auto LoweringHandleVarStorage(LoweringContext& context, SemanticsNodeId node_id, SemanticsNode node) -> void { // TODO: This should provide a name, not just `var`. Also, LLVM requires diff --git a/toolchain/lowering/testdata/operators/not.carbon b/toolchain/lowering/testdata/operators/not.carbon new file mode 100644 index 000000000000..470b4e3f1494 --- /dev/null +++ b/toolchain/lowering/testdata/operators/not.carbon @@ -0,0 +1,17 @@ +// 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: ; ModuleID = 'not.carbon' +// CHECK:STDOUT: source_filename = "not.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @Not(i1 %b) { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %0 = xor i1 %b, true +// CHECK:STDOUT: ret i1 %0 +// CHECK:STDOUT: } + +fn Not(b: bool) -> bool { + return not b; +} diff --git a/toolchain/parser/parse_node_kind.def b/toolchain/parser/parse_node_kind.def index 0834e04469a4..ac6b32c7c88f 100644 --- a/toolchain/parser/parse_node_kind.def +++ b/toolchain/parser/parse_node_kind.def @@ -254,6 +254,13 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(PrefixOperator, 1) // InfixOperator CARBON_PARSE_NODE_KIND_CHILD_COUNT(InfixOperator, 2) +// The first operand of a short-circuiting infix operator: +// _external_: expression +// ShortCircuitOperand +// _external_: expression +// _external_: InfixOperator +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ShortCircuitOperand, 1) + // A postfix operator: // _external_: expression // PostfixOperator diff --git a/toolchain/parser/parse_tree.cpp b/toolchain/parser/parse_tree.cpp index 721b2d65c195..988800bff84b 100644 --- a/toolchain/parser/parse_tree.cpp +++ b/toolchain/parser/parse_tree.cpp @@ -310,12 +310,13 @@ auto ParseTree::Verify() const -> ErrorOr { prev_index = n.index; } - if (!has_errors_ && - static_cast(node_impls_.size()) != tokens_->size()) { + if (!has_errors_ && static_cast(node_impls_.size()) != + tokens_->expected_parse_tree_size()) { return Error( llvm::formatv("ParseTree has {0} nodes and no errors, but " - "TokenizedBuffer has {1} tokens.", - node_impls_.size(), tokens_->size())); + "TokenizedBuffer expected {1} nodes for {2} tokens.", + node_impls_.size(), tokens_->expected_parse_tree_size(), + tokens_->size())); } return Success(); } diff --git a/toolchain/parser/parse_tree.h b/toolchain/parser/parse_tree.h index b9934c3cc930..4814f83770e5 100644 --- a/toolchain/parser/parse_tree.h +++ b/toolchain/parser/parse_tree.h @@ -209,7 +209,7 @@ class ParseTree { // be used to actually parse the tokens into a tree. explicit ParseTree(TokenizedBuffer& tokens_arg) : tokens_(&tokens_arg) { // If the tree is valid, there will be one node per token, so reserve once. - node_impls_.reserve(tokens_->size()); + node_impls_.reserve(tokens_->expected_parse_tree_size()); } // Prints a single node for Print(). Returns true when preorder and there are diff --git a/toolchain/parser/parser_handle_expression.cpp b/toolchain/parser/parser_handle_expression.cpp index 14fe8aab9975..65e94497e06f 100644 --- a/toolchain/parser/parser_handle_expression.cpp +++ b/toolchain/parser/parser_handle_expression.cpp @@ -140,8 +140,9 @@ auto ParserHandleExpressionInPostfixLoop(ParserContext& context) -> void { auto ParserHandleExpressionLoop(ParserContext& context) -> void { auto state = context.PopState(); + auto operator_kind = context.PositionKind(); auto trailing_operator = PrecedenceGroup::ForTrailing( - context.PositionKind(), context.IsTrailingOperatorInfix()); + operator_kind, context.IsTrailingOperatorInfix()); if (!trailing_operator) { if (state.has_error) { context.ReturnErrorOnState(); @@ -181,6 +182,13 @@ auto ParserHandleExpressionLoop(ParserContext& context) -> void { state.lhs_precedence = operator_precedence; if (is_binary) { + if (operator_kind == TokenKind::And || operator_kind == TokenKind::Or) { + // For `and` and `or`, wrap the first operand in a virtual parse tree + // node so that semantics can insert control flow here. + context.AddNode(ParseNodeKind::ShortCircuitOperand, state.token, + state.subtree_start, state.has_error); + } + state.state = ParserState::ExpressionLoopForBinary; context.PushState(state); context.PushStateForExpression(operator_precedence); diff --git a/toolchain/parser/testdata/if_expression/precedence.carbon b/toolchain/parser/testdata/if_expression/precedence.carbon index e69ff6eceb51..a1cb639eff43 100644 --- a/toolchain/parser/testdata/if_expression/precedence.carbon +++ b/toolchain/parser/testdata/if_expression/precedence.carbon @@ -15,20 +15,23 @@ // 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: 'ShortCircuitOperand', text: 'and', subtree_size: 2}, // CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 2}, // 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: 'InfixOperator', text: 'and', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 5}, // 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: 'ShortCircuitOperand', text: 'or', subtree_size: 2}, // CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: 'or', subtree_size: 3}, -// 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: 'InfixOperator', text: 'or', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 17}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 28}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/associative.carbon b/toolchain/parser/testdata/operators/associative.carbon index ff44d4e5ef79..0ed5391584ec 100644 --- a/toolchain/parser/testdata/operators/associative.carbon +++ b/toolchain/parser/testdata/operators/associative.carbon @@ -9,13 +9,15 @@ // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'a'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'a'}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 5}, // CHECK:STDOUT: {kind: 'NameReference', text: 'c'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 6}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/fail_precedence_and_or.carbon b/toolchain/parser/testdata/operators/fail_precedence_and_or.carbon index 6f9ad22e8a2d..dd8af727169d 100644 --- a/toolchain/parser/testdata/operators/fail_precedence_and_or.carbon +++ b/toolchain/parser/testdata/operators/fail_precedence_and_or.carbon @@ -9,13 +9,15 @@ // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'a'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'a'}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperand', text: 'or', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'NameReference', text: 'c'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: 'or', has_error: yes, subtree_size: 5}, -// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 6}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'or', has_error: yes, subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/fail_precedence_or_and.carbon b/toolchain/parser/testdata/operators/fail_precedence_or_and.carbon index e1bcbba27b25..07f4b668a4ef 100644 --- a/toolchain/parser/testdata/operators/fail_precedence_or_and.carbon +++ b/toolchain/parser/testdata/operators/fail_precedence_or_and.carbon @@ -9,13 +9,15 @@ // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'a'}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: 'or', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'a'}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperand', text: 'or', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'or', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperand', text: 'and', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'NameReference', text: 'c'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', has_error: yes, subtree_size: 5}, -// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 6}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', has_error: yes, subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/precedence_not.carbon b/toolchain/parser/testdata/operators/precedence_not.carbon index 4ae4da2102a9..c70f0103718e 100644 --- a/toolchain/parser/testdata/operators/precedence_not.carbon +++ b/toolchain/parser/testdata/operators/precedence_not.carbon @@ -9,16 +9,18 @@ // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'a'}, -// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'not', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, -// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'not', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'a'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'not', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameReference', text: 'b'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'not', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 7}, // CHECK:STDOUT: {kind: 'NameReference', text: 'c'}, // CHECK:STDOUT: {kind: 'PrefixOperator', text: 'not', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 8}, -// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 9}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', 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/operators/prefix.carbon b/toolchain/parser/testdata/operators/prefix.carbon index f263a3d2af7e..da5423becdaf 100644 --- a/toolchain/parser/testdata/operators/prefix.carbon +++ b/toolchain/parser/testdata/operators/prefix.carbon @@ -12,7 +12,16 @@ // CHECK:STDOUT: {kind: 'NameReference', text: 'n'}, // CHECK:STDOUT: {kind: 'PrefixOperator', text: '-', subtree_size: 2}, // CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: 'b'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'bool'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'Literal', text: 'true'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'not', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 8}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] var n: i8 = -n; +var b: bool = not true; diff --git a/toolchain/semantics/semantics_context.cpp b/toolchain/semantics/semantics_context.cpp index dd240e7144c3..98eeabbb1401 100644 --- a/toolchain/semantics/semantics_context.cpp +++ b/toolchain/semantics/semantics_context.cpp @@ -202,6 +202,13 @@ auto SemanticsContext::ImplicitAsRequired(ParseTree::Node parse_node, return output_value_id; } +auto SemanticsContext::ImplicitAsBool(ParseTree::Node parse_node, + SemanticsNodeId value_id) + -> SemanticsNodeId { + return ImplicitAsRequired(parse_node, value_id, + CanonicalizeType(SemanticsNodeId::BuiltinBoolType)); +} + auto SemanticsContext::ImplicitAsImpl(SemanticsNodeId value_id, SemanticsTypeId as_type_id, SemanticsNodeId* output_value_id) diff --git a/toolchain/semantics/semantics_context.h b/toolchain/semantics/semantics_context.h index d44cc4efbb87..b7af66d610c2 100644 --- a/toolchain/semantics/semantics_context.h +++ b/toolchain/semantics/semantics_context.h @@ -76,6 +76,10 @@ class SemanticsContext { auto ImplicitAsRequired(ParseTree::Node parse_node, SemanticsNodeId value_id, SemanticsTypeId as_type_id) -> SemanticsNodeId; + // Runs ImplicitAsRequired for a conversion to `bool`. + auto ImplicitAsBool(ParseTree::Node parse_node, SemanticsNodeId value_id) + -> SemanticsNodeId; + // Canonicalizes a type which is tracked as a single node. // TODO: This should eventually return a type ID. auto CanonicalizeType(SemanticsNodeId node_id) -> SemanticsTypeId; diff --git a/toolchain/semantics/semantics_handle.cpp b/toolchain/semantics/semantics_handle.cpp index 13540eebce8a..a66da1208be5 100644 --- a/toolchain/semantics/semantics_handle.cpp +++ b/toolchain/semantics/semantics_handle.cpp @@ -193,21 +193,45 @@ auto SemanticsHandleInfixOperator(SemanticsContext& context, auto rhs_id = context.node_stack().Pop(); auto lhs_id = context.node_stack().Pop(); - // TODO: This should search for a compatible interface. For now, it's a very - // trivial check of validity on the operation. - lhs_id = context.ImplicitAsRequired( - parse_node, lhs_id, context.semantics_ir().GetNode(rhs_id).type_id()); - // Figure out the operator for the token. auto token = context.parse_tree().node_token(parse_node); switch (auto token_kind = context.tokens().GetKind(token)) { case TokenKind::Plus: + // TODO: This should search for a compatible interface. For now, it's a + // very trivial check of validity on the operation. + lhs_id = context.ImplicitAsRequired( + parse_node, lhs_id, context.semantics_ir().GetNode(rhs_id).type_id()); + context.AddNodeAndPush( parse_node, SemanticsNode::BinaryOperatorAdd::Make( parse_node, context.semantics_ir().GetNode(lhs_id).type_id(), lhs_id, rhs_id)); break; + + case TokenKind::And: + case TokenKind::Or: { + // The first operand is wrapped in a ShortCircuitOperand, which we + // already handled by creating a RHS block and a resumption block, which + // are the current block and its enclosing block. + rhs_id = context.ImplicitAsBool(parse_node, rhs_id); + + // When the second operand is evaluated, the result of `and` and `or` is + // its value. + auto rhs_block_id = context.node_block_stack().PopForAdd(); + auto resume_block_id = context.node_block_stack().PeekForAdd(); + context.AddNodeToBlock(rhs_block_id, + SemanticsNode::BranchWithArg::Make( + parse_node, resume_block_id, rhs_id)); + + // Collect the result from either the first or second operand. + context.AddNodeAndPush( + parse_node, + SemanticsNode::BlockArg::Make( + parse_node, context.semantics_ir().GetNode(rhs_id).type_id())); + break; + } + default: return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind)); } @@ -231,8 +255,8 @@ auto SemanticsHandleLiteral(SemanticsContext& context, SemanticsNode::BoolLiteral::Make( parse_node, context.CanonicalizeType(SemanticsNodeId::BuiltinBoolType), - token_kind == TokenKind::True ? SemanticsBoolValue(1) - : SemanticsBoolValue(0))); + token_kind == TokenKind::True ? SemanticsBoolValue::True + : SemanticsBoolValue::False)); break; } case TokenKind::IntegerLiteral: { @@ -410,7 +434,25 @@ auto SemanticsHandlePostfixOperator(SemanticsContext& context, auto SemanticsHandlePrefixOperator(SemanticsContext& context, ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandlePrefixOperator"); + auto value_id = context.node_stack().Pop(); + + // Figure out the operator for the token. + auto token = context.parse_tree().node_token(parse_node); + switch (auto token_kind = context.tokens().GetKind(token)) { + case TokenKind::Not: + value_id = context.ImplicitAsBool(parse_node, value_id); + context.AddNodeAndPush( + parse_node, + SemanticsNode::UnaryOperatorNot::Make( + parse_node, context.semantics_ir().GetNode(value_id).type_id(), + value_id)); + break; + + default: + return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind)); + } + + return true; } auto SemanticsHandleReturnStatement(SemanticsContext& context, @@ -492,6 +534,53 @@ auto SemanticsHandleSelfValueIdentifier(SemanticsContext& context, return context.TODO(parse_node, "HandleSelfValueIdentifier"); } +auto SemanticsHandleShortCircuitOperand(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + // Convert the condition to `bool`. + auto cond_value_id = context.node_stack().Pop(); + cond_value_id = context.ImplicitAsBool(parse_node, cond_value_id); + auto bool_type_id = context.semantics_ir().GetNode(cond_value_id).type_id(); + + // Compute the branch value: the condition for `and`, inverted for `or`. + auto token = context.parse_tree().node_token(parse_node); + SemanticsNodeId branch_value_id = SemanticsNodeId::Invalid; + auto short_circuit_result_id = SemanticsNodeId::Invalid; + switch (auto token_kind = context.tokens().GetKind(token)) { + case TokenKind::And: + branch_value_id = cond_value_id; + short_circuit_result_id = + context.AddNode(SemanticsNode::BoolLiteral::Make( + parse_node, bool_type_id, SemanticsBoolValue::False)); + break; + + case TokenKind::Or: + branch_value_id = context.AddNode(SemanticsNode::UnaryOperatorNot::Make( + parse_node, bool_type_id, cond_value_id)); + short_circuit_result_id = + context.AddNode(SemanticsNode::BoolLiteral::Make( + parse_node, bool_type_id, SemanticsBoolValue::True)); + break; + + default: + CARBON_FATAL() << "Unexpected short-circuiting operator " << parse_node; + } + + // Create a block for the right-hand side and for the continuation. + auto lhs_block_id = context.node_block_stack().PopForAdd(); + auto end_block_id = context.node_block_stack().PushForAdd(); + auto rhs_block_id = context.node_block_stack().PushForAdd(); + context.AddNodeToBlock( + lhs_block_id, + SemanticsNode::BranchIf::Make(parse_node, rhs_block_id, branch_value_id)); + context.AddNodeToBlock( + lhs_block_id, SemanticsNode::BranchWithArg::Make( + parse_node, end_block_id, short_circuit_result_id)); + + // Put the condition back on the stack for SemanticsHandleInfixOperator. + context.node_stack().Push(parse_node, cond_value_id); + return true; +} + auto SemanticsHandleTemplate(SemanticsContext& context, ParseTree::Node parse_node) -> bool { return context.TODO(parse_node, "HandleTemplate"); diff --git a/toolchain/semantics/semantics_handle_if_expression.cpp b/toolchain/semantics/semantics_handle_if_expression.cpp index 5294670c9aef..90dfe3ff47b6 100644 --- a/toolchain/semantics/semantics_handle_if_expression.cpp +++ b/toolchain/semantics/semantics_handle_if_expression.cpp @@ -13,9 +13,7 @@ auto SemanticsHandleIfExpressionIf(SemanticsContext& context, 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)); + cond_value_id = context.ImplicitAsBool(if_node, cond_value_id); // 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. diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index 93b06edc895b..fcb410b41112 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/toolchain/semantics/semantics_ir.cpp @@ -216,6 +216,7 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { case SemanticsNodeKind::StructMemberAccess: case SemanticsNodeKind::StructValue: case SemanticsNodeKind::StubReference: + case SemanticsNodeKind::UnaryOperatorNot: case SemanticsNodeKind::VarStorage: // We don't need to handle stringification for nodes that don't show up // in errors, but make it clear what's going on so that it's clearer diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index 11bf0cdbde95..6017ce5f76f6 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -70,6 +70,9 @@ struct SemanticsCrossReferenceIRId : public IndexBase { // A boolean value. struct SemanticsBoolValue : public IndexBase { + static const SemanticsBoolValue False; + static const SemanticsBoolValue True; + using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { switch (index) { @@ -85,6 +88,9 @@ struct SemanticsBoolValue : public IndexBase { } }; +constexpr SemanticsBoolValue SemanticsBoolValue::False = SemanticsBoolValue(0); +constexpr SemanticsBoolValue SemanticsBoolValue::True = SemanticsBoolValue(1); + // The ID of an integer literal. struct SemanticsIntegerLiteralId : public IndexBase { using IndexBase::IndexBase; @@ -368,6 +374,9 @@ class SemanticsNode { using StubReference = Factory; + using UnaryOperatorNot = Factory; + using VarStorage = Factory; SemanticsNode() diff --git a/toolchain/semantics/semantics_node_block_stack.h b/toolchain/semantics/semantics_node_block_stack.h index 3d850bca0c83..0615fc577ac7 100644 --- a/toolchain/semantics/semantics_node_block_stack.h +++ b/toolchain/semantics/semantics_node_block_stack.h @@ -44,6 +44,13 @@ class SemanticsNodeBlockStack { // SemanticsNodeBlockId::Empty is returned if one wasn't allocated. auto Pop() -> SemanticsNodeBlockId; + // Pops the top node block, ensuring that it is lazily allocated if it's + // empty. For use when more nodes will be added to the block later. + auto PopForAdd() -> SemanticsNodeBlockId { + PeekForAdd(); + return Pop(); + } + // Prints the stack for a stack dump. auto PrintForStackDump(llvm::raw_ostream& output) const -> void; diff --git a/toolchain/semantics/semantics_node_kind.def b/toolchain/semantics/semantics_node_kind.def index 9b458aace81e..5eef3c7c4361 100644 --- a/toolchain/semantics/semantics_node_kind.def +++ b/toolchain/semantics/semantics_node_kind.def @@ -42,6 +42,7 @@ CARBON_SEMANTICS_NODE_KIND(StructType) CARBON_SEMANTICS_NODE_KIND(StructTypeField) CARBON_SEMANTICS_NODE_KIND(StructValue) CARBON_SEMANTICS_NODE_KIND(StubReference) +CARBON_SEMANTICS_NODE_KIND(UnaryOperatorNot) CARBON_SEMANTICS_NODE_KIND(VarStorage) #undef CARBON_SEMANTICS_NODE_KIND diff --git a/toolchain/semantics/testdata/operators/and.carbon b/toolchain/semantics/testdata/operators/and.carbon new file mode 100644 index 000000000000..c2e6d51e95f2 --- /dev/null +++ b/toolchain/semantics/testdata/operators/and.carbon @@ -0,0 +1,78 @@ +// 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: str2, param_refs: block0, return_type: type0, body: block4}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: F, +// CHECK:STDOUT: G, +// CHECK:STDOUT: And, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeBoolType, +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: BoolLiteral, arg0: true, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+1, type: type0}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function1}, +// CHECK:STDOUT: {kind: BoolLiteral, arg0: true, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+4, type: type0}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function2}, +// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function0, type: type0}, +// CHECK:STDOUT: {kind: BoolLiteral, arg0: false, type: type0}, +// CHECK:STDOUT: {kind: BranchIf, arg0: block6, arg1: node+7}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block5, arg1: node+8}, +// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function1, type: type0}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block5, arg1: node+11}, +// CHECK:STDOUT: {kind: BlockArg, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+13, 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+6, +// 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+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: node+14, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn F() -> bool { return true; } +fn G() -> bool { return true; } + +fn And() -> bool { + return F() and G(); +} diff --git a/toolchain/semantics/testdata/operators/or.carbon b/toolchain/semantics/testdata/operators/or.carbon new file mode 100644 index 000000000000..35139c906032 --- /dev/null +++ b/toolchain/semantics/testdata/operators/or.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: str0, param_refs: block0, return_type: type0, body: block2}, +// CHECK:STDOUT: {name: str1, param_refs: block0, return_type: type0, body: block3}, +// CHECK:STDOUT: {name: str2, param_refs: block0, return_type: type0, body: block4}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: F, +// CHECK:STDOUT: G, +// CHECK:STDOUT: Or, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeBoolType, +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: BoolLiteral, arg0: true, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+1, type: type0}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function1}, +// CHECK:STDOUT: {kind: BoolLiteral, arg0: true, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+4, type: type0}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function2}, +// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function0, type: type0}, +// CHECK:STDOUT: {kind: UnaryOperatorNot, arg0: node+7, type: type0}, +// CHECK:STDOUT: {kind: BoolLiteral, arg0: true, type: type0}, +// CHECK:STDOUT: {kind: BranchIf, arg0: block6, arg1: node+8}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block5, arg1: node+9}, +// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function1, type: type0}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block5, arg1: node+12}, +// CHECK:STDOUT: {kind: BlockArg, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+14, 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+6, +// 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+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+14, +// CHECK:STDOUT: node+15, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn F() -> bool { return true; } +fn G() -> bool { return true; } + +fn Or() -> bool { + return F() or G(); +} diff --git a/toolchain/semantics/testdata/operators/unary_op.carbon b/toolchain/semantics/testdata/operators/unary_op.carbon new file mode 100644 index 000000000000..64ea7926f4d1 --- /dev/null +++ b/toolchain/semantics/testdata/operators/unary_op.carbon @@ -0,0 +1,49 @@ +// 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: str1, param_refs: block2, return_type: type0, body: block4}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: b, +// CHECK:STDOUT: Not, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeBoolType, +// 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: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: UnaryOperatorNot, arg0: node+0, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+3, type: type0}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn Not(b: bool) -> bool { + return not b; +}