diff --git a/toolchain/lexer/token_kind.def b/toolchain/lexer/token_kind.def index 686de1ef4a57..54add7ea6932 100644 --- a/toolchain/lexer/token_kind.def +++ b/toolchain/lexer/token_kind.def @@ -131,6 +131,7 @@ CARBON_KEYWORD_TOKEN(Default, "default") CARBON_KEYWORD_TOKEN(Else, "else") CARBON_KEYWORD_TOKEN(Extends, "extends") CARBON_KEYWORD_TOKEN(External, "external") +CARBON_KEYWORD_TOKEN(False, "false") CARBON_KEYWORD_TOKEN(Final, "final") CARBON_KEYWORD_TOKEN(Fn, "fn") CARBON_KEYWORD_TOKEN(For, "for") @@ -162,6 +163,7 @@ CARBON_KEYWORD_TOKEN(SelfValueIdentifier, "self") CARBON_KEYWORD_TOKEN(StringTypeLiteral, "String") CARBON_KEYWORD_TOKEN(Template, "template") CARBON_KEYWORD_TOKEN(Then, "then") +CARBON_KEYWORD_TOKEN(True, "true") // Underscore is tokenized as a keyword because it's part of identifiers. CARBON_KEYWORD_TOKEN(Underscore, "_") CARBON_KEYWORD_TOKEN(Var, "var") diff --git a/toolchain/lowering/lowering_context.cpp b/toolchain/lowering/lowering_context.cpp index 230d6da5e51a..e8ba2d5a4f4c 100644 --- a/toolchain/lowering/lowering_context.cpp +++ b/toolchain/lowering/lowering_context.cpp @@ -141,6 +141,10 @@ auto LoweringContext::BuildType(SemanticsNodeId node_id) -> llvm::Type* { case SemanticsBuiltinKind::IntegerType.AsInt(): // TODO: Handle different sizes. return builder_.getInt32Ty(); + case SemanticsBuiltinKind::BoolType.AsInt(): + // TODO: We may want to have different representations for `bool` storage + // (`i8`) versus for `bool` values (`i1`). + return builder_.getInt1Ty(); } auto node = semantics_ir_->GetNode(node_id); diff --git a/toolchain/lowering/lowering_handle.cpp b/toolchain/lowering/lowering_handle.cpp index 0e692dde2444..4843f14c0f65 100644 --- a/toolchain/lowering/lowering_handle.cpp +++ b/toolchain/lowering/lowering_handle.cpp @@ -43,6 +43,14 @@ auto LoweringHandleBlockArg(LoweringContext& /*context*/, CARBON_FATAL() << "TODO: Add support: " << node; } +auto LoweringHandleBoolLiteral(LoweringContext& context, + SemanticsNodeId node_id, SemanticsNode node) + -> void { + llvm::Value* v = llvm::ConstantInt::get(context.builder().getInt1Ty(), + node.GetAsBoolLiteral().index); + context.SetLocal(node_id, v); +} + auto LoweringHandleBranch(LoweringContext& /*context*/, SemanticsNodeId /*node_id*/, SemanticsNode node) -> void { diff --git a/toolchain/lowering/testdata/basics/false_true.carbon b/toolchain/lowering/testdata/basics/false_true.carbon new file mode 100644 index 000000000000..27e4f0f3d534 --- /dev/null +++ b/toolchain/lowering/testdata/basics/false_true.carbon @@ -0,0 +1,25 @@ +// 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 = 'false_true.carbon' +// CHECK:STDOUT: source_filename = "false_true.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @F() { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i1 false +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @T() { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i1 true +// CHECK:STDOUT: } + +fn F() -> bool { + return false; +} + +fn T() -> bool { + return true; +} diff --git a/toolchain/parser/parser_handle_expression.cpp b/toolchain/parser/parser_handle_expression.cpp index e2bba4182303..14fe8aab9975 100644 --- a/toolchain/parser/parser_handle_expression.cpp +++ b/toolchain/parser/parser_handle_expression.cpp @@ -61,6 +61,8 @@ auto ParserHandleExpressionInPostfix(ParserContext& context) -> void { context.PushState(state); break; } + case TokenKind::False: + case TokenKind::True: case TokenKind::IntegerLiteral: case TokenKind::RealLiteral: case TokenKind::StringLiteral: 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 0b817742042d..b87bf1b355e2 100644 --- a/toolchain/parser/testdata/if_expression/fail_else_expr_missing.carbon +++ b/toolchain/parser/testdata/if_expression/fail_else_expr_missing.carbon @@ -14,7 +14,7 @@ // 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: 'Literal', 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}, diff --git a/toolchain/parser/testdata/if_expression/fail_else_missing.carbon b/toolchain/parser/testdata/if_expression/fail_else_missing.carbon index 336663d19ed5..fe1a2fb2aedc 100644 --- a/toolchain/parser/testdata/if_expression/fail_else_missing.carbon +++ b/toolchain/parser/testdata/if_expression/fail_else_missing.carbon @@ -14,7 +14,7 @@ // 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: 'Literal', 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}, 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 ec136930c384..bebc9cfd3cd5 100644 --- a/toolchain/parser/testdata/if_expression/fail_then_expr_missing.carbon +++ b/toolchain/parser/testdata/if_expression/fail_then_expr_missing.carbon @@ -14,7 +14,7 @@ // 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: 'Literal', 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}, diff --git a/toolchain/parser/testdata/if_expression/fail_then_missing.carbon b/toolchain/parser/testdata/if_expression/fail_then_missing.carbon index e26ef9042753..c1f4f4649992 100644 --- a/toolchain/parser/testdata/if_expression/fail_then_missing.carbon +++ b/toolchain/parser/testdata/if_expression/fail_then_missing.carbon @@ -14,7 +14,7 @@ // 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: 'Literal', 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}, diff --git a/toolchain/parser/testdata/if_expression/fail_top_level_if.carbon b/toolchain/parser/testdata/if_expression/fail_top_level_if.carbon index 11421c6c9870..24d94280590e 100644 --- a/toolchain/parser/testdata/if_expression/fail_top_level_if.carbon +++ b/toolchain/parser/testdata/if_expression/fail_top_level_if.carbon @@ -10,7 +10,7 @@ // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, // CHECK:STDOUT: {kind: 'IfConditionStart', text: 'if', has_error: yes}, -// CHECK:STDOUT: {kind: 'NameReference', text: 'true'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'true'}, // CHECK:STDOUT: {kind: 'IfCondition', text: 'if', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'CodeBlockStart', text: 'then', has_error: yes}, // CHECK:STDOUT: {kind: 'InvalidParse', text: 'then', has_error: yes}, diff --git a/toolchain/semantics/semantics_handle.cpp b/toolchain/semantics/semantics_handle.cpp index eafc8cfa6697..13540eebce8a 100644 --- a/toolchain/semantics/semantics_handle.cpp +++ b/toolchain/semantics/semantics_handle.cpp @@ -224,6 +224,17 @@ auto SemanticsHandleLiteral(SemanticsContext& context, ParseTree::Node parse_node) -> bool { auto token = context.parse_tree().node_token(parse_node); switch (auto token_kind = context.tokens().GetKind(token)) { + case TokenKind::False: + case TokenKind::True: { + context.AddNodeAndPush( + parse_node, + SemanticsNode::BoolLiteral::Make( + parse_node, + context.CanonicalizeType(SemanticsNodeId::BuiltinBoolType), + token_kind == TokenKind::True ? SemanticsBoolValue(1) + : SemanticsBoolValue(0))); + break; + } case TokenKind::IntegerLiteral: { auto id = context.semantics_ir().AddIntegerLiteral( context.tokens().GetIntegerLiteral(token)); diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index 2d2a33d0d181..93b06edc895b 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/toolchain/semantics/semantics_ir.cpp @@ -199,6 +199,7 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { case SemanticsNodeKind::BinaryOperatorAdd: case SemanticsNodeKind::BindName: case SemanticsNodeKind::BlockArg: + case SemanticsNodeKind::BoolLiteral: case SemanticsNodeKind::Branch: case SemanticsNodeKind::BranchIf: case SemanticsNodeKind::BranchWithArg: diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index 521ac631040c..11bf0cdbde95 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -68,6 +68,23 @@ struct SemanticsCrossReferenceIRId : public IndexBase { } }; +// A boolean value. +struct SemanticsBoolValue : public IndexBase { + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + switch (index) { + case 0: + out << "false"; + break; + case 1: + out << "true"; + break; + default: + CARBON_FATAL() << "Invalid bool value " << index; + } + } +}; + // The ID of an integer literal. struct SemanticsIntegerLiteralId : public IndexBase { using IndexBase::IndexBase; @@ -264,6 +281,9 @@ class SemanticsNode { using BlockArg = Factory; + using BoolLiteral = + Factory; + using Branch = FactoryNoType; diff --git a/toolchain/semantics/semantics_node_kind.def b/toolchain/semantics/semantics_node_kind.def index b70903dbfd23..9b458aace81e 100644 --- a/toolchain/semantics/semantics_node_kind.def +++ b/toolchain/semantics/semantics_node_kind.def @@ -24,6 +24,7 @@ CARBON_SEMANTICS_NODE_KIND(Assign) CARBON_SEMANTICS_NODE_KIND(BinaryOperatorAdd) CARBON_SEMANTICS_NODE_KIND(BindName) CARBON_SEMANTICS_NODE_KIND(BlockArg) +CARBON_SEMANTICS_NODE_KIND(BoolLiteral) CARBON_SEMANTICS_NODE_KIND(Branch) CARBON_SEMANTICS_NODE_KIND(BranchIf) CARBON_SEMANTICS_NODE_KIND(BranchWithArg) diff --git a/toolchain/semantics/testdata/if_expression/constant_condition.carbon b/toolchain/semantics/testdata/if_expression/constant_condition.carbon new file mode 100644 index 000000000000..e2163dfe53ba --- /dev/null +++ b/toolchain/semantics/testdata/if_expression/constant_condition.carbon @@ -0,0 +1,119 @@ +// 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: {name: str3, param_refs: block0, return_type: type0, body: block8}, +// 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: F, +// CHECK:STDOUT: G, +// 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: FunctionDeclaration, arg0: function2}, +// CHECK:STDOUT: {kind: BoolLiteral, arg0: true, type: type1}, +// CHECK:STDOUT: {kind: BranchIf, arg0: block6, arg1: node+7}, +// CHECK:STDOUT: {kind: Branch, arg0: block5}, +// 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: block7, arg1: node+10}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block7, arg1: node+11}, +// CHECK:STDOUT: {kind: BlockArg, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+14, type: type0}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function3}, +// CHECK:STDOUT: {kind: BoolLiteral, arg0: false, type: type1}, +// CHECK:STDOUT: {kind: BranchIf, arg0: block10, arg1: node+17}, +// CHECK:STDOUT: {kind: Branch, arg0: block9}, +// 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: block11, arg1: node+20}, +// CHECK:STDOUT: {kind: BranchWithArg, arg0: block11, arg1: node+21}, +// CHECK:STDOUT: {kind: BlockArg, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+24, 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: node+16, +// 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: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+14, +// CHECK:STDOUT: node+15, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+17, +// CHECK:STDOUT: node+18, +// CHECK:STDOUT: node+19, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+21, +// CHECK:STDOUT: node+23, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+20, +// CHECK:STDOUT: node+22, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+24, +// CHECK:STDOUT: node+25, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn A() -> i32 { return 1; } +fn B() -> i32 { return 2; } + +fn F() -> i32 { + return if true then A() else B(); +} + +fn G() -> i32 { + return if false then A() else B(); +}