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

Split out and/or operator handling from infix. (#3480)

This is also doing the parse node split, allowing lower reliance in
formatter on the tokenized buffer (something that I may be touching more
due to import handling).
Jon Ross-Perkins 2 лет назад
Родитель
Сommit
c4864aa2ff

+ 58 - 45
toolchain/check/handle_operator.cpp

@@ -14,28 +14,6 @@ auto HandleInfixOperator(Context& context, Parse::NodeId parse_node) -> bool {
   // 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 Lex::TokenKind::And:
-    case Lex::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 = ConvertToBoolValue(context, parse_node, rhs_id);
-
-      // When the second operand is evaluated, the result of `and` and `or` is
-      // its value.
-      auto resume_block_id = context.inst_block_stack().PeekOrAdd(/*depth=*/1);
-      context.AddInst(
-          SemIR::BranchWithArg{parse_node, resume_block_id, rhs_id});
-      context.inst_block_stack().Pop();
-      context.AddCurrentCodeBlockToFunction();
-
-      // Collect the result from either the first or second operand.
-      context.AddInstAndPush(
-          parse_node,
-          SemIR::BlockArg{parse_node, context.insts().Get(rhs_id).type_id(),
-                          resume_block_id});
-      return true;
-    }
     case Lex::TokenKind::As: {
       auto rhs_type_id = ExprAsType(context, rhs_node, rhs_id);
       context.node_stack().Push(
@@ -181,7 +159,9 @@ auto HandlePrefixOperator(Context& context, Parse::NodeId parse_node) -> bool {
   }
 }
 
-auto HandleShortCircuitOperand(Context& context, Parse::NodeId parse_node)
+// Adds the branch for a short circuit operand.
+static auto HandleShortCircuitOperand(Context& context,
+                                      Parse::NodeId parse_node, bool is_or)
     -> bool {
   // Convert the condition to `bool`.
   auto cond_value_id = context.node_stack().PopExpr();
@@ -189,26 +169,13 @@ auto HandleShortCircuitOperand(Context& context, Parse::NodeId parse_node)
   auto bool_type_id = context.insts().Get(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);
-  SemIR::InstId branch_value_id = SemIR::InstId::Invalid;
-  auto short_circuit_result_id = SemIR::InstId::Invalid;
-  switch (auto token_kind = context.tokens().GetKind(token)) {
-    case Lex::TokenKind::And:
-      branch_value_id = cond_value_id;
-      short_circuit_result_id = context.AddInst(SemIR::BoolLiteral{
-          parse_node, bool_type_id, SemIR::BoolValue::False});
-      break;
-
-    case Lex::TokenKind::Or:
-      branch_value_id = context.AddInst(
-          SemIR::UnaryOperatorNot{parse_node, bool_type_id, cond_value_id});
-      short_circuit_result_id = context.AddInst(
-          SemIR::BoolLiteral{parse_node, bool_type_id, SemIR::BoolValue::True});
-      break;
-
-    default:
-      CARBON_FATAL() << "Unexpected short-circuiting operator " << token_kind;
-  }
+  SemIR::InstId branch_value_id =
+      is_or ? context.AddInst(SemIR::UnaryOperatorNot{parse_node, bool_type_id,
+                                                      cond_value_id})
+            : cond_value_id;
+  auto short_circuit_result_id = context.AddInst(SemIR::BoolLiteral{
+      parse_node, bool_type_id,
+      is_or ? SemIR::BoolValue::True : SemIR::BoolValue::False});
 
   // Create a block for the right-hand side and for the continuation.
   auto rhs_block_id =
@@ -223,9 +190,55 @@ auto HandleShortCircuitOperand(Context& context, Parse::NodeId parse_node)
   context.inst_block_stack().Push(rhs_block_id);
   context.AddCurrentCodeBlockToFunction();
 
-  // Put the condition back on the stack for HandleInfixOperator.
-  context.node_stack().Push(parse_node, cond_value_id);
+  // HandleShortCircuitOperator will follow, and doesn't need the operand on the
+  // node stack.
+  return true;
+}
+
+auto HandleShortCircuitOperandAnd(Context& context, Parse::NodeId parse_node)
+    -> bool {
+  return HandleShortCircuitOperand(context, parse_node, /*is_or=*/false);
+}
+
+auto HandleShortCircuitOperandOr(Context& context, Parse::NodeId parse_node)
+    -> bool {
+  return HandleShortCircuitOperand(context, parse_node, /*is_or=*/true);
+}
+
+// Short circuit operator handling is uniform because the branching logic
+// occurs during operand handling.
+static auto HandleShortCircuitOperator(Context& context,
+                                       Parse::NodeId parse_node) -> bool {
+  auto [rhs_node, rhs_id] = context.node_stack().PopExprWithParseNode();
+
+  // 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 = ConvertToBoolValue(context, parse_node, rhs_id);
+
+  // When the second operand is evaluated, the result of `and` and `or` is
+  // its value.
+  auto resume_block_id = context.inst_block_stack().PeekOrAdd(/*depth=*/1);
+  context.AddInst(SemIR::BranchWithArg{parse_node, resume_block_id, rhs_id});
+  context.inst_block_stack().Pop();
+  context.AddCurrentCodeBlockToFunction();
+
+  // Collect the result from either the first or second operand.
+  context.AddInstAndPush(
+      parse_node,
+      SemIR::BlockArg{parse_node, context.insts().Get(rhs_id).type_id(),
+                      resume_block_id});
   return true;
 }
 
+auto HandleShortCircuitOperatorAnd(Context& context, Parse::NodeId parse_node)
+    -> bool {
+  return HandleShortCircuitOperator(context, parse_node);
+}
+
+auto HandleShortCircuitOperatorOr(Context& context, Parse::NodeId parse_node)
+    -> bool {
+  return HandleShortCircuitOperator(context, parse_node);
+}
+
 }  // namespace Carbon::Check

+ 4 - 1
toolchain/check/node_stack.h

@@ -339,7 +339,10 @@ class NodeStack {
       case Parse::NodeKind::ReturnType:
       case Parse::NodeKind::SelfTypeNameExpr:
       case Parse::NodeKind::SelfValueNameExpr:
-      case Parse::NodeKind::ShortCircuitOperand:
+      case Parse::NodeKind::ShortCircuitOperandAnd:
+      case Parse::NodeKind::ShortCircuitOperandOr:
+      case Parse::NodeKind::ShortCircuitOperatorAnd:
+      case Parse::NodeKind::ShortCircuitOperatorOr:
       case Parse::NodeKind::StructFieldValue:
       case Parse::NodeKind::StructLiteral:
       case Parse::NodeKind::StructFieldType:

+ 33 - 14
toolchain/parse/handle_expr.cpp

@@ -264,15 +264,26 @@ auto HandleExprLoop(Context& context) -> void {
   state.lhs_precedence = operator_precedence;
 
   if (is_binary) {
-    if (operator_kind == Lex::TokenKind::And ||
-        operator_kind == Lex::TokenKind::Or) {
+    switch (operator_kind) {
       // For `and` and `or`, wrap the first operand in a virtual parse tree
-      // node so that semantics can insert control flow here.
-      context.AddNode(NodeKind::ShortCircuitOperand, state.token,
-                      state.subtree_start, state.has_error);
+      // node so that checking can insert control flow here.
+      case Lex::TokenKind::And:
+        context.AddNode(NodeKind::ShortCircuitOperandAnd, state.token,
+                        state.subtree_start, state.has_error);
+        state.state = State::ExprLoopForShortCircuitOperatorAsAnd;
+        break;
+      case Lex::TokenKind::Or:
+        context.AddNode(NodeKind::ShortCircuitOperandOr, state.token,
+                        state.subtree_start, state.has_error);
+        state.state = State::ExprLoopForShortCircuitOperatorAsOr;
+        break;
+
+      default:
+        state.state = State::ExprLoopForBinary;
+        break;
     }
 
-    context.PushState(state, State::ExprLoopForBinary);
+    context.PushState(state);
     context.PushStateForExpr(operator_precedence);
   } else {
     context.AddNode(NodeKind::PostfixOperator, state.token, state.subtree_start,
@@ -282,22 +293,30 @@ auto HandleExprLoop(Context& context) -> void {
   }
 }
 
-auto HandleExprLoopForBinary(Context& context) -> void {
+// Adds the operator node and returns the the main expression loop.
+static auto HandleExprLoopForOperator(Context& context, NodeKind node_kind)
+    -> void {
   auto state = context.PopState();
 
-  context.AddNode(NodeKind::InfixOperator, state.token, state.subtree_start,
-                  state.has_error);
+  context.AddNode(node_kind, state.token, state.subtree_start, state.has_error);
   state.has_error = false;
   context.PushState(state, State::ExprLoop);
 }
 
+auto HandleExprLoopForBinary(Context& context) -> void {
+  HandleExprLoopForOperator(context, NodeKind::InfixOperator);
+}
+
 auto HandleExprLoopForPrefix(Context& context) -> void {
-  auto state = context.PopState();
+  HandleExprLoopForOperator(context, NodeKind::PrefixOperator);
+}
 
-  context.AddNode(NodeKind::PrefixOperator, state.token, state.subtree_start,
-                  state.has_error);
-  state.has_error = false;
-  context.PushState(state, State::ExprLoop);
+auto HandleExprLoopForShortCircuitOperatorAsAnd(Context& context) -> void {
+  HandleExprLoopForOperator(context, NodeKind::ShortCircuitOperatorAnd);
+}
+
+auto HandleExprLoopForShortCircuitOperatorAsOr(Context& context) -> void {
+  HandleExprLoopForOperator(context, NodeKind::ShortCircuitOperatorOr);
 }
 
 auto HandleIfExprFinishCondition(Context& context) -> void {

+ 7 - 7
toolchain/parse/node_kind.def

@@ -530,7 +530,6 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(PrefixOperator, 1,
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(InfixOperator, 2,
                                    CARBON_TOKEN(Amp)
                                    CARBON_TOKEN(AmpEqual)
-                                   CARBON_TOKEN(And)
                                    CARBON_TOKEN(As)
                                    CARBON_TOKEN(Caret)
                                    CARBON_TOKEN(CaretEqual)
@@ -547,7 +546,6 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(InfixOperator, 2,
                                    CARBON_TOKEN(LessLessEqual)
                                    CARBON_TOKEN(Minus)
                                    CARBON_TOKEN(MinusEqual)
-                                   CARBON_TOKEN(Or)
                                    CARBON_TOKEN(Percent)
                                    CARBON_TOKEN(PercentEqual)
                                    CARBON_TOKEN(Pipe)
@@ -561,13 +559,15 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(InfixOperator, 2,
 
 // clang-format on
 
-// The first operand of a short-circuiting infix operator:
+// A short-circuiting infix operator:
 //     _external_: expression
-//   ShortCircuitOperand
+//   ShortCircuitOperand(And|Or)
 //   _external_: expression
-// _external_: InfixOperator
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(ShortCircuitOperand, 1,
-                                   CARBON_TOKEN(And) CARBON_TOKEN(Or))
+// ShortCircuitOperand(And|Or)
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(ShortCircuitOperandAnd, 1, CARBON_TOKEN(And))
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(ShortCircuitOperandOr, 1, CARBON_TOKEN(Or))
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(ShortCircuitOperatorAnd, 2, CARBON_TOKEN(And))
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(ShortCircuitOperatorOr, 2, CARBON_TOKEN(Or))
 
 // A postfix operator:
 //   _external_: expression

+ 17 - 4
toolchain/parse/state.def

@@ -471,8 +471,8 @@ CARBON_PARSE_STATE(ExprInPostfixLoop)
 
 // Handles processing of an expression.
 //
-// expr <binary operator> ...
-//      ^~~~~~~~~~~~~~~~~
+// expr <infix operator> ...
+//      ^~~~~~~~~~~~~~~~
 //   1. Expr
 //   2. ExprLoopForBinary
 //
@@ -480,6 +480,11 @@ CARBON_PARSE_STATE(ExprInPostfixLoop)
 //      ^~~~~~~~~~~~~~~~~~
 //   1. ExprLoop
 //
+// expr <short circuit operator> ...
+//      ^~~~~~~~~~~~~~~~~~~~~~~~
+//   1. Expr
+//   2. ExprLoopForShortCircuitOperator
+//
 // expr ...
 //     ^
 //   (state done)
@@ -488,8 +493,8 @@ CARBON_PARSE_STATE(ExprLoop)
 // Completes an ExprLoop pass by adding an infix operator, then goes back
 // to ExprLoop.
 //
-// expr <binary operator> expr ...
-//                            ^
+// expr <infix operator> expr ...
+//                           ^
 //   1. ExprLoop
 CARBON_PARSE_STATE(ExprLoopForBinary)
 
@@ -501,6 +506,14 @@ CARBON_PARSE_STATE(ExprLoopForBinary)
 //   1. ExprLoop
 CARBON_PARSE_STATE(ExprLoopForPrefix)
 
+// Completes an ExprLoop pass by adding a short circuit operator, then goes back
+// to ExprLoop.
+//
+// expr <short circuit operator> expr ...
+//                                   ^
+//   1. ExprLoop
+CARBON_PARSE_STATE_VARIANTS2(ExprLoopForShortCircuitOperator, And, Or)
+
 // Completes the condition of an `if` expression and handles the `then` token.
 //
 // if expr then ...

+ 6 - 6
toolchain/parse/testdata/if_expr/precedence.carbon

@@ -23,19 +23,19 @@ fn F(b: bool) -> bool {
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 10},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
 // CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'b'},
-// CHECK:STDOUT:               {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 2},
+// CHECK:STDOUT:               {kind: 'ShortCircuitOperandAnd', text: 'and', subtree_size: 2},
 // CHECK:STDOUT:               {kind: 'IdentifierNameExpr', text: 'b'},
-// CHECK:STDOUT:             {kind: 'InfixOperator', text: 'and', subtree_size: 4},
+// CHECK:STDOUT:             {kind: 'ShortCircuitOperatorAnd', text: 'and', subtree_size: 4},
 // CHECK:STDOUT:           {kind: 'IfExprIf', text: 'if', subtree_size: 5},
 // CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'b'},
-// CHECK:STDOUT:               {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 2},
+// CHECK:STDOUT:               {kind: 'ShortCircuitOperandAnd', text: 'and', subtree_size: 2},
 // CHECK:STDOUT:               {kind: 'IdentifierNameExpr', text: 'b'},
-// CHECK:STDOUT:             {kind: 'InfixOperator', text: 'and', subtree_size: 4},
+// CHECK:STDOUT:             {kind: 'ShortCircuitOperatorAnd', text: 'and', subtree_size: 4},
 // CHECK:STDOUT:           {kind: 'IfExprThen', text: 'then', subtree_size: 5},
 // CHECK:STDOUT:               {kind: 'IdentifierNameExpr', text: 'b'},
-// CHECK:STDOUT:             {kind: 'ShortCircuitOperand', text: 'or', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'ShortCircuitOperandOr', text: 'or', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'b'},
-// CHECK:STDOUT:           {kind: 'InfixOperator', text: 'or', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'ShortCircuitOperatorOr', text: 'or', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'IfExprElse', text: 'else', subtree_size: 15},
 // CHECK:STDOUT:       {kind: 'ReturnStatement', text: ';', subtree_size: 17},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 28},

+ 4 - 4
toolchain/parse/testdata/operators/associative.carbon

@@ -17,12 +17,12 @@ fn F() {
 // CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'a'},
-// CHECK:STDOUT:               {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 2},
+// CHECK:STDOUT:               {kind: 'ShortCircuitOperandAnd', text: 'and', subtree_size: 2},
 // CHECK:STDOUT:               {kind: 'IdentifierNameExpr', text: 'b'},
-// CHECK:STDOUT:             {kind: 'InfixOperator', text: 'and', subtree_size: 4},
-// CHECK:STDOUT:           {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 5},
+// CHECK:STDOUT:             {kind: 'ShortCircuitOperatorAnd', text: 'and', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'ShortCircuitOperandAnd', text: 'and', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'c'},
-// CHECK:STDOUT:         {kind: 'InfixOperator', text: 'and', subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'ShortCircuitOperatorAnd', text: 'and', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 14},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/operators/fail_precedence_and_or.carbon

@@ -20,12 +20,12 @@ fn F() {
 // CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'a'},
-// CHECK:STDOUT:               {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 2},
+// CHECK:STDOUT:               {kind: 'ShortCircuitOperandAnd', text: 'and', subtree_size: 2},
 // CHECK:STDOUT:               {kind: 'IdentifierNameExpr', 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: 'ShortCircuitOperatorAnd', text: 'and', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'ShortCircuitOperandOr', text: 'or', has_error: yes, subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'c'},
-// CHECK:STDOUT:         {kind: 'InfixOperator', text: 'or', has_error: yes, subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'ShortCircuitOperatorOr', text: 'or', has_error: yes, subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 14},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/operators/fail_precedence_or_and.carbon

@@ -20,12 +20,12 @@ fn F() {
 // CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'a'},
-// CHECK:STDOUT:               {kind: 'ShortCircuitOperand', text: 'or', subtree_size: 2},
+// CHECK:STDOUT:               {kind: 'ShortCircuitOperandOr', text: 'or', subtree_size: 2},
 // CHECK:STDOUT:               {kind: 'IdentifierNameExpr', 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: 'ShortCircuitOperatorOr', text: 'or', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'ShortCircuitOperandAnd', text: 'and', has_error: yes, subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'c'},
-// CHECK:STDOUT:         {kind: 'InfixOperator', text: 'and', has_error: yes, subtree_size: 7},
+// CHECK:STDOUT:         {kind: 'ShortCircuitOperatorAnd', text: 'and', has_error: yes, subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 14},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/operators/precedence_as.carbon

@@ -45,16 +45,16 @@ fn F(n: i32) {
 // CHECK:STDOUT:                       {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:                     {kind: 'InfixOperator', text: 'as', subtree_size: 3},
 // CHECK:STDOUT:                   {kind: 'InfixOperator', text: '<', subtree_size: 7},
-// CHECK:STDOUT:                 {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 8},
+// CHECK:STDOUT:                 {kind: 'ShortCircuitOperandAnd', text: 'and', subtree_size: 8},
 // CHECK:STDOUT:                   {kind: 'BoolLiteralTrue', text: 'true'},
 // CHECK:STDOUT:                   {kind: 'BoolTypeLiteral', text: 'bool'},
 // CHECK:STDOUT:                 {kind: 'InfixOperator', text: 'as', subtree_size: 3},
-// CHECK:STDOUT:               {kind: 'InfixOperator', text: 'and', subtree_size: 12},
-// CHECK:STDOUT:             {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 13},
+// CHECK:STDOUT:               {kind: 'ShortCircuitOperatorAnd', text: 'and', subtree_size: 12},
+// CHECK:STDOUT:             {kind: 'ShortCircuitOperandAnd', text: 'and', subtree_size: 13},
 // CHECK:STDOUT:               {kind: 'BoolLiteralFalse', text: 'false'},
 // CHECK:STDOUT:               {kind: 'BoolTypeLiteral', text: 'bool'},
 // CHECK:STDOUT:             {kind: 'InfixOperator', text: 'as', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'InfixOperator', text: 'and', subtree_size: 17},
+// CHECK:STDOUT:           {kind: 'ShortCircuitOperatorAnd', text: 'and', subtree_size: 17},
 // CHECK:STDOUT:         {kind: 'IfCondition', text: ')', subtree_size: 19},
 // CHECK:STDOUT:           {kind: 'CodeBlockStart', text: '{'},
 // CHECK:STDOUT:         {kind: 'CodeBlock', text: '}', subtree_size: 2},

+ 4 - 4
toolchain/parse/testdata/operators/precedence_not.carbon

@@ -18,14 +18,14 @@ fn F() {
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'a'},
 // CHECK:STDOUT:                 {kind: 'PrefixOperator', text: 'not', subtree_size: 2},
-// CHECK:STDOUT:               {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 3},
+// CHECK:STDOUT:               {kind: 'ShortCircuitOperandAnd', text: 'and', subtree_size: 3},
 // CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', 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: 'ShortCircuitOperatorAnd', text: 'and', subtree_size: 6},
+// CHECK:STDOUT:           {kind: 'ShortCircuitOperandAnd', text: 'and', subtree_size: 7},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'c'},
 // CHECK:STDOUT:           {kind: 'PrefixOperator', text: 'not', subtree_size: 2},
-// CHECK:STDOUT:         {kind: 'InfixOperator', text: 'and', subtree_size: 10},
+// CHECK:STDOUT:         {kind: 'ShortCircuitOperatorAnd', text: 'and', subtree_size: 10},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 11},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 17},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 5 - 7
toolchain/sem_ir/formatter.cpp

@@ -388,14 +388,12 @@ class InstNamer {
         name = "if.done";
         break;
 
-      case Parse::NodeKind::ShortCircuitOperand: {
-        bool is_rhs = inst.Is<BranchIf>();
-        bool is_and = tokenized_buffer_.GetKind(parse_tree_.node_token(
-                          inst.parse_node())) == Lex::TokenKind::And;
-        name = is_and ? (is_rhs ? "and.rhs" : "and.result")
-                      : (is_rhs ? "or.rhs" : "or.result");
+      case Parse::NodeKind::ShortCircuitOperandAnd:
+        name = inst.Is<BranchIf>() ? "and.rhs" : "and.result";
+        break;
+      case Parse::NodeKind::ShortCircuitOperandOr:
+        name = inst.Is<BranchIf>() ? "or.rhs" : "or.result";
         break;
-      }
 
       case Parse::NodeKind::WhileConditionStart:
         name = "while.cond";