Просмотр исходного кода

`true` and `false` support, and lowering for `bool` type. (#2896)

Richard Smith 2 лет назад
Родитель
Сommit
aa40e2b8a9

+ 2 - 0
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")

+ 4 - 0
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);

+ 8 - 0
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 {

+ 25 - 0
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;
+}

+ 2 - 0
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:

+ 1 - 1
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},

+ 1 - 1
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},

+ 1 - 1
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},

+ 1 - 1
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},

+ 1 - 1
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},

+ 11 - 0
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));

+ 1 - 0
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:

+ 20 - 0
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<SemanticsNodeKind::BlockArg>;
 
+  using BoolLiteral =
+      Factory<SemanticsNodeKind::BoolLiteral, SemanticsBoolValue /*value*/>;
+
   using Branch = FactoryNoType<SemanticsNodeKind::Branch,
                                SemanticsNodeBlockId /*target_id*/>;
 

+ 1 - 0
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)

+ 119 - 0
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();
+}