Jelajahi Sumber

Make choice work for alternatives without parameters (#4815)

This adds support for choice types at a similar level to that of a C
enum, where each alternative has a name but no additional
data/parameters attached to it. We generate a TODO diagnostic if
parameters are specified.

Because there's no extra data, the storage is a simple unsigned integer
discriminant of the smallest possible size.

A choice without any alternatives is not constructible. A choice with a
single alternative is, and has an empty tuple in place of a discriminant
since it has only one state. The empty tuple is used to make the class
non-constructible. This can be improved.

Each alternative is turned into a let binding on the choice that is a
value of the choice with that alternative set as the active one in the
discriminant. This isn't possible to write in user code with a class
right now, since the let binding has the same type as the choice
(which is a class) it is within. It's possible to generate it in semir
however by adding the binding after the class is marked complete.
Dana Jansens 1 tahun lalu
induk
melakukan
3f01310039

+ 20 - 0
toolchain/check/context.h

@@ -262,6 +262,18 @@ class Context {
   // End of SemIR::File members.
   // --------------------------------------------------------------------------
 
+  // During Choice typechecking, each alternative turns into a name binding on
+  // the Choice type, but this can't be done until the full Choice type is
+  // known. This represents each binding to be done at the end of checking the
+  // Choice type.
+  struct ChoiceDeferredBinding {
+    Parse::NodeId node_id;
+    NameComponent name_component;
+  };
+  auto choice_deferred_bindings() -> llvm::SmallVector<ChoiceDeferredBinding>& {
+    return choice_deferred_bindings_;
+  }
+
  private:
   // Handles diagnostics.
   DiagnosticEmitter* emitter_;
@@ -353,6 +365,14 @@ class Context {
   // processing the enclosing full-pattern.
   Map<SemIR::InstId, SemIR::InstId> var_storage_map_;
 
+  // Each alternative in a Choice gets an entry here, they are stored in
+  // declaration order. The vector is consumed and emptied at the end of the
+  // Choice definition.
+  //
+  // TODO: This may need to be a stack of vectors if it becomes possible to
+  // define a Choice type inside an alternative's parameter set.
+  llvm::SmallVector<ChoiceDeferredBinding> choice_deferred_bindings_;
+
   // Stack of single-entry regions being built.
   RegionStack region_stack_;
 

+ 1 - 1
toolchain/check/decl_name_stack.h

@@ -95,7 +95,7 @@ class DeclNameStack {
     // construction.
     auto MakeEntityWithParamsBase(const NameComponent& name,
                                   SemIR::InstId decl_id, bool is_extern,
-                                  SemIR::LibraryNameId extern_library)
+                                  SemIR::LibraryNameId extern_library) const
         -> SemIR::EntityWithParamsBase {
       return {
           .name_id = name_id_for_new_inst(),

+ 13 - 0
toolchain/check/handle_binding_pattern.cpp

@@ -210,6 +210,19 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
           }
           break;
         }
+        case Lex::TokenKind::Choice:
+          if (context.scope_stack().PeekInstId().has_value()) {
+            // We are building a pattern for a choice alternative, not the
+            // choice type itself.
+
+            // Implicit param lists are prevented during parse.
+            CARBON_CHECK(context.full_pattern_stack().CurrentKind() !=
+                             FullPatternStack::Kind::ImplicitParamList,
+                         "choice alternative with implicit parameters");
+            // Don't fall through to the `Class` logic for choice alternatives.
+            break;
+          }
+          [[fallthrough]];
         case Lex::TokenKind::Class:
         case Lex::TokenKind::Impl:
         case Lex::TokenKind::Interface: {

+ 308 - 7
toolchain/check/handle_choice.cpp

@@ -3,28 +3,329 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 #include "toolchain/check/context.h"
+#include "toolchain/check/convert.h"
+#include "toolchain/check/decl_name_stack.h"
+#include "toolchain/check/eval.h"
+#include "toolchain/check/generic.h"
 #include "toolchain/check/handle.h"
+#include "toolchain/check/inst.h"
+#include "toolchain/check/literal.h"
+#include "toolchain/check/name_component.h"
+#include "toolchain/check/type.h"
+#include "toolchain/diagnostics/diagnostic.h"
+#include "toolchain/lex/token_kind.h"
+#include "toolchain/sem_ir/ids.h"
+#include "toolchain/sem_ir/inst.h"
+#include "toolchain/sem_ir/name_scope.h"
+#include "toolchain/sem_ir/typed_insts.h"
 
 namespace Carbon::Check {
 
-auto HandleParseNode(Context& context, Parse::ChoiceDefinitionId node_id)
+auto HandleParseNode(Context& context, Parse::ChoiceIntroducerId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleChoiceDefinition");
+  // Create an instruction block to hold the instructions created as part of the
+  // choice signature, such as generic parameters.
+  context.inst_block_stack().Push();
+  // Push the bracketing node.
+  context.node_stack().Push(node_id);
+  // The choice's name follows.
+  context.decl_name_stack().PushScopeAndStartName();
+  // There's no modifiers on a choice, but this informs how to typecheck any
+  // generic binding pattern.
+  context.decl_introducer_state_stack().Push<Lex::TokenKind::Choice>();
+  // This choice is potentially generic.
+  StartGenericDefinition(context);
+  return true;
 }
 
-auto HandleParseNode(Context& context, Parse::ChoiceIntroducerId node_id)
+auto HandleParseNode(Context& context, Parse::ChoiceDefinitionStartId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleChoiceIntroducer");
+  auto name = PopNameComponent(context);
+  auto name_context = context.decl_name_stack().FinishName(name);
+  context.node_stack()
+      .PopAndDiscardSoloNodeId<Parse::NodeKind::ChoiceIntroducer>();
+  context.decl_introducer_state_stack().Pop<Lex::TokenKind::Choice>();
+
+  auto decl_block_id = context.inst_block_stack().Pop();
+
+  // Choices create a ClassId, since they ultimately turn into a class with
+  // methods and some builtin impls.
+  auto class_decl =
+      SemIR::ClassDecl{.type_id = SemIR::TypeType::SingletonTypeId,
+                       .class_id = SemIR::ClassId::None,
+                       .decl_block_id = decl_block_id};
+  auto class_decl_id =
+      AddPlaceholderInst(context, SemIR::LocIdAndInst(node_id, class_decl));
+
+  context.decl_name_stack().AddNameOrDiagnose(name_context, class_decl_id,
+                                              SemIR::AccessKind::Public);
+
+  // An inst block for the body of the choice.
+  context.inst_block_stack().Push();
+  auto body_block_id = context.inst_block_stack().PeekOrAdd();
+
+  SemIR::Class class_info = {
+      name_context.MakeEntityWithParamsBase(name, class_decl_id,
+                                            /*is_extern=*/false,
+                                            SemIR::LibraryNameId::None),
+      {// `.self_type_id` depends on the ClassType, so is set below.
+       .self_type_id = SemIR::TypeId::None,
+       .inheritance_kind = SemIR::ClassFields::Final,
+       // TODO: Handle the case where there's control flow in the alternatives.
+       // For example:
+       //
+       //   choice C {
+       //     Alt(x: if true then i32 else f64),
+       //   }
+       //
+       // We may need to track a list of instruction blocks here, as we do for a
+       // function.
+       .body_block_id = body_block_id}};
+
+  // This call finishes the GenericDecl, after which we can use the `Self`
+  // specific.
+  class_info.generic_id = BuildGenericDecl(context, class_decl_id);
+  auto self_specific_id =
+      context.generics().GetSelfSpecific(class_info.generic_id);
+
+  class_info.definition_id = class_decl_id;
+  class_info.scope_id = context.name_scopes().Add(
+      class_decl_id, SemIR::NameId::None, class_info.parent_scope_id);
+  class_decl.class_id = context.classes().Add(class_info);
+  if (class_info.has_parameters()) {
+    class_decl.type_id = GetGenericClassType(
+        context, class_decl.class_id, context.scope_stack().PeekSpecificId());
+  }
+
+  ReplaceInstBeforeConstantUse(context, class_decl_id, class_decl);
+
+  // We had to construct the `ClassId` from `Class` in order to build the `Self`
+  // type below. But it needs to be written back to the `Class` in the
+  // ValueStore, not the local variable. This gives a mutable reference to the
+  // `Class` in the ValueStore.
+  SemIR::Class& mut_class = context.classes().Get(class_decl.class_id);
+  // Build the `Self` type using the resulting type constant.
+  auto self_type_id = context.types().GetTypeIdForTypeConstantId(
+      TryEvalInst(context, SemIR::InstId::None,
+                  SemIR::ClassType{.type_id = SemIR::TypeType::SingletonTypeId,
+                                   .class_id = class_decl.class_id,
+                                   .specific_id = self_specific_id}));
+  mut_class.self_type_id = self_type_id;
+
+  // Enter the choice scope.
+  context.scope_stack().Push(class_decl_id, class_info.scope_id,
+                             self_specific_id);
+  // Checking the binding pattern for an alternative requires a non-empty stack.
+  // We reuse the Choice token even though we're now checking an alternative
+  // inside the Choice, since there's no better token to use.
+  //
+  //  TODO: The token here is _not_ `Choice` though, we shouldn't need to use
+  //  that here. Either remove the need for a token or find a token (a new
+  //  introducer?) for the alternative to name.
+  context.decl_introducer_state_stack().Push<Lex::TokenKind::Choice>();
+  StartGenericDefinition(context);
+
+  context.name_scopes().AddRequiredName(
+      class_info.scope_id, SemIR::NameId::SelfType,
+      context.types().GetInstId(self_type_id));
+
+  // Mark the beginning of the choice body.
+  context.node_stack().Push(node_id, class_decl.class_id);
+
+  CARBON_CHECK(context.choice_deferred_bindings().empty(),
+               "Alternatives left behind in choice_deferred_bindings: {0}",
+               context.choice_deferred_bindings().size());
+  return true;
 }
 
-auto HandleParseNode(Context& context, Parse::ChoiceDefinitionStartId node_id)
+static auto AddChoiceAlternative(Context& context, Parse::NodeId node_id)
+    -> void {
+  // Note, there is nothing like a ChoiceAlternativeIntroducer node, so no parse
+  // node to pop here.
+  auto name_component = PopNameComponent(context);
+  if (name_component.param_patterns_id == SemIR::InstBlockId::Empty) {
+    // Treat an empty parameter list the same as no parameter list.
+    //
+    // TODO: The current design suggests that we want Foo() to result in a
+    // member function `ChoiceType.Foo()`, and `Foo` to result in a member
+    // constant `ChoiceType.Foo`, but that only one of the two is allowed in a
+    // single choice type. See
+    // https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/sum_types.md#user-defined-sum-types.
+    // For now they are not treated differently and both resolve to a member
+    // constant.
+    context.TODO(name_component.params_loc_id,
+                 "empty parameter list should make a member function");
+    name_component.param_patterns_id = SemIR::InstBlockId::None;
+  }
+  if (name_component.param_patterns_id.has_value()) {
+    context.TODO(name_component.params_loc_id,
+                 "choice alternatives with parameters are not yet supported");
+    return;
+  }
+  context.choice_deferred_bindings().push_back({node_id, name_component});
+}
+
+// Info about the Choice type, used to construct each alternative member of the
+// class representing the Choice.
+struct ChoiceInfo {
+  // The `Self` type.
+  SemIR::TypeId self_type_id;
+  // The scope of the class for adding the alternatives to.
+  SemIR::NameScopeId name_scope_id;
+  // A struct type with the same fields as `Self`. Used to construct `Self`.
+  SemIR::TypeId self_struct_type_id;
+  // The type of the discriminant value.
+  SemIR::TypeId discriminant_type_id;
+  int num_alternative_bits;
+};
+
+// Builds a `let` binding for an alternative without parameters as a member of
+// the resulting class for the Choice definition. If the alternative was `Alt`
+// then the binding will be like:
+// ```
+//   let Alt: ChoiceType = <ChoiceType with Alt selected>;
+// ```
+static auto MakeLetBinding(Context& context, const ChoiceInfo& choice_info,
+                           int alternative_index,
+                           const Context::ChoiceDeferredBinding& binding)
+    -> void {
+  SemIR::InstId discriminant_value_id = [&] {
+    if (choice_info.num_alternative_bits == 0) {
+      return AddInst(context, SemIR::LocIdAndInst::UncheckedLoc(
+                                  binding.node_id,
+                                  SemIR::TupleLiteral{
+                                      .type_id = GetTupleType(context, {}),
+                                      .elements_id = SemIR::InstBlockId::Empty,
+                                  }));
+    } else {
+      return MakeIntLiteral(context, binding.node_id,
+                            context.ints().Add(alternative_index));
+    }
+  }();
+  discriminant_value_id =
+      ConvertToValueOfType(context, binding.node_id, discriminant_value_id,
+                           choice_info.discriminant_type_id);
+
+  auto self_value_id = ConvertToValueOfType(
+      context, binding.node_id,
+      AddInst(context, SemIR::LocIdAndInst::UncheckedLoc(
+                           binding.node_id,
+                           SemIR::StructLiteral{
+                               .type_id = choice_info.self_struct_type_id,
+                               .elements_id =
+                                   [&] {
+                                     context.inst_block_stack().Push();
+                                     context.inst_block_stack().AddInstId(
+                                         discriminant_value_id);
+                                     return context.inst_block_stack().Pop();
+                                   }(),
+                           })),
+      choice_info.self_type_id);
+
+  auto entity_name_id = context.entity_names().Add(
+      {.name_id = binding.name_component.name_id,
+       .parent_scope_id = choice_info.name_scope_id});
+  auto bind_name_id = AddInst(
+      context, SemIR::LocIdAndInst::UncheckedLoc(
+                   binding.node_id, SemIR::BindName{
+                                        .type_id = choice_info.self_type_id,
+                                        .entity_name_id = entity_name_id,
+                                        .value_id = self_value_id,
+                                    }));
+  context.name_scopes()
+      .Get(choice_info.name_scope_id)
+      .AddRequired({.name_id = binding.name_component.name_id,
+                    .result = SemIR::ScopeLookupResult::MakeFound(
+                        bind_name_id, SemIR::AccessKind::Public)});
+}
+
+auto HandleParseNode(Context& context, Parse::ChoiceDefinitionId node_id)
     -> bool {
-  return context.TODO(node_id, "HandleChoiceDefinitionStart");
+  // The last alternative may optionally not have a comma after it, in which
+  // case we get here after the last alternative.
+  if (!context.node_stack().PeekIs(Parse::NodeKind::ChoiceDefinitionStart)) {
+    AddChoiceAlternative(context, node_id);
+  }
+
+  auto class_id =
+      context.node_stack().Pop<Parse::NodeKind::ChoiceDefinitionStart>();
+
+  int num_alternatives = context.choice_deferred_bindings().size();
+  int num_alternative_bits = [&] {
+    if (num_alternatives > 1) {
+      return static_cast<int>(ceil(log2(num_alternatives)));
+    } else {
+      return 0;
+    }
+  }();
+
+  SemIR::TypeId discriminant_type_id = [&] {
+    if (num_alternative_bits == 0) {
+      // Even though there's no bits needed, we add an empty field. We want to
+      // prevent constructing the Choice from an empty struct literal instead of
+      // going through an alternative. And in the case there is no alternative,
+      // then there's no way to construct the Choice (which can be a useful
+      // type).
+      //
+      // TODO: Find a way to produce a better diagnostic, and not require an
+      // empty field.
+      return GetTupleType(context, {});
+    } else {
+      return MakeIntType(context, node_id, SemIR::IntKind::Unsigned,
+                         context.ints().Add(num_alternative_bits));
+    }
+  }();
+
+  llvm::SmallVector<SemIR::StructTypeField, 1> struct_type_fields;
+  struct_type_fields.push_back({
+      .name_id = SemIR::NameId::ChoiceDiscriminant,
+      .type_id = discriminant_type_id,
+  });
+  auto fields_id =
+      context.struct_type_fields().AddCanonical(struct_type_fields);
+  auto choice_witness_id =
+      AddInst(context, node_id,
+              SemIR::CompleteTypeWitness{
+                  .type_id = GetSingletonType(
+                      context, SemIR::WitnessType::SingletonInstId),
+                  .object_repr_id = GetStructType(context, fields_id)});
+  // Note: avoid storing a reference to the returned Class, since it may be
+  // invalidated by other type constructions.
+  context.classes().Get(class_id).complete_type_witness_id = choice_witness_id;
+
+  auto self_type_id = context.classes().Get(class_id).self_type_id;
+  auto name_scope_id = context.classes().Get(class_id).scope_id;
+
+  auto self_struct_type_id = GetStructType(
+      context, context.struct_type_fields().AddCanonical(struct_type_fields));
+
+  for (auto [i, deferred_binding] :
+       llvm::enumerate(context.choice_deferred_bindings())) {
+    MakeLetBinding(context,
+                   ChoiceInfo{.self_type_id = self_type_id,
+                              .name_scope_id = name_scope_id,
+                              .self_struct_type_id = self_struct_type_id,
+                              .discriminant_type_id = discriminant_type_id,
+                              .num_alternative_bits = num_alternative_bits},
+                   i, deferred_binding);
+  }
+
+  // The scopes and blocks for the choice itself.
+  context.inst_block_stack().Pop();
+  context.decl_introducer_state_stack().Pop<Lex::TokenKind::Choice>();
+  context.scope_stack().Pop();
+  context.decl_name_stack().PopScope();
+
+  FinishGenericDefinition(context, context.classes().Get(class_id).generic_id);
+
+  context.choice_deferred_bindings().clear();
+  return true;
 }
 
 auto HandleParseNode(Context& context,
                      Parse::ChoiceAlternativeListCommaId node_id) -> bool {
-  return context.TODO(node_id, "HandleChoiceAlternativeListComma");
+  AddChoiceAlternative(context, node_id);
+  return true;
 }
 
 }  // namespace Carbon::Check

+ 3 - 2
toolchain/check/node_stack.h

@@ -417,6 +417,8 @@ class NodeStack {
       case Parse::NodeKind::FunctionDefinitionStart:
       case Parse::NodeKind::BuiltinFunctionDefinitionStart:
         return Id::KindFor<SemIR::FunctionId>();
+      case Parse::NodeKind::ChoiceDefinitionStart:
+        // TODO: Should we have a separate SemIR::ChoiceId?
       case Parse::NodeKind::ClassDefinitionStart:
         return Id::KindFor<SemIR::ClassId>();
       case Parse::NodeKind::InterfaceDefinitionStart:
@@ -431,6 +433,7 @@ class NodeStack {
         return Id::KindFor<SemIR::LibraryNameId>();
       case Parse::NodeKind::ArrayExprSemi:
       case Parse::NodeKind::BuiltinName:
+      case Parse::NodeKind::ChoiceIntroducer:
       case Parse::NodeKind::ClassIntroducer:
       case Parse::NodeKind::CodeBlockStart:
       case Parse::NodeKind::FunctionIntroducer:
@@ -459,8 +462,6 @@ class NodeStack {
       case Parse::NodeKind::BreakStatementStart:
       case Parse::NodeKind::CallExprComma:
       case Parse::NodeKind::ChoiceAlternativeListComma:
-      case Parse::NodeKind::ChoiceDefinitionStart:
-      case Parse::NodeKind::ChoiceIntroducer:
       case Parse::NodeKind::CodeBlock:
       case Parse::NodeKind::ContinueStatementStart:
       case Parse::NodeKind::CorePackageName:

+ 390 - 0
toolchain/check/testdata/choice/basic.carbon

@@ -0,0 +1,390 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/choice/basic.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/choice/basic.carbon
+
+// --- no_alternative.carbon
+library "[[@TEST_NAME]]";
+
+choice Never {}
+
+// --- one_alternative.carbon
+library "[[@TEST_NAME]]";
+
+choice Always {
+  Sunny
+}
+
+fn G() {
+  let mood: Always = Always.Sunny;
+}
+
+// --- multiple_alternatives.carbon
+library "[[@TEST_NAME]]";
+
+choice Ordering {
+  Less,
+  Equivalent,
+  Greater,
+  Incomparable
+}
+
+fn H() {
+  let less: Ordering = Ordering.Less;
+  let equiv: Ordering = Ordering.Equivalent;
+  let greater: Ordering = Ordering.Greater;
+  let inc: Ordering = Ordering.Incomparable;
+}
+
+// --- fail_todo_empty_params.carbon
+library "[[@TEST_NAME]]";
+
+choice Always {
+  // CHECK:STDERR: fail_todo_empty_params.carbon:[[@LINE+4]]:8: error: semantics TODO: `empty parameter list should make a member function` [SemanticsTodo]
+  // CHECK:STDERR:   Sunny()
+  // CHECK:STDERR:        ^~
+  // CHECK:STDERR:
+  Sunny()
+}
+
+fn G() {
+  let mood: Always = Always.Sunny;
+}
+
+// CHECK:STDOUT: --- no_alternative.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Never: type = class_type @Never [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %struct_type.discriminant: type = struct_type {.discriminant: %empty_tuple.type} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .Never = %Never.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Never.decl: type = class_decl @Never [concrete = constants.%Never] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Never {
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Never
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- one_alternative.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Always: type = class_type @Always [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %struct_type.discriminant: type = struct_type {.discriminant: %empty_tuple.type} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete]
+// CHECK:STDOUT:   %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
+// CHECK:STDOUT:   %Always.val: %Always = struct_value (%empty_tuple) [concrete]
+// CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .Always = %Always.decl
+// CHECK:STDOUT:     .G = %G.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Always.decl: type = class_decl @Always [concrete = constants.%Always] {} {}
+// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Always {
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %.loc5_1.1: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:   %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   %.loc5_1.2: %empty_tuple.type = converted %.loc5_1.1, %empty_tuple [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   %.loc5_1.3: %struct_type.discriminant = struct_literal (%.loc5_1.2)
+// CHECK:STDOUT:   %.loc5_1.4: ref %Always = temporary_storage
+// CHECK:STDOUT:   %.loc5_1.5: ref %empty_tuple.type = class_element_access %.loc5_1.4, element0
+// CHECK:STDOUT:   %.loc5_1.6: init %empty_tuple.type = tuple_init () to %.loc5_1.5 [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   %.loc5_1.7: init %empty_tuple.type = converted %.loc5_1.2, %.loc5_1.6 [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   %.loc5_1.8: init %Always = class_init (%.loc5_1.7), %.loc5_1.4 [concrete = constants.%Always.val]
+// CHECK:STDOUT:   %.loc5_1.9: ref %Always = temporary %.loc5_1.4, %.loc5_1.8
+// CHECK:STDOUT:   %.loc5_1.10: ref %Always = converted %.loc5_1.3, %.loc5_1.9
+// CHECK:STDOUT:   %.loc5_1.11: %Always = bind_value %.loc5_1.10
+// CHECK:STDOUT:   %Sunny: %Always = bind_name Sunny, %.loc5_1.11
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Always
+// CHECK:STDOUT:   .Sunny = %Sunny
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %mood.patt: %Always = binding_pattern mood
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Always.ref.loc8_22: type = name_ref Always, file.%Always.decl [concrete = constants.%Always]
+// CHECK:STDOUT:   %Sunny.ref: %Always = name_ref Sunny, @Always.%Sunny
+// CHECK:STDOUT:   %Always.ref.loc8_13: type = name_ref Always, file.%Always.decl [concrete = constants.%Always]
+// CHECK:STDOUT:   %mood: %Always = bind_name mood, %Sunny.ref
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- multiple_alternatives.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Ordering: type = class_type @Ordering [concrete]
+// CHECK:STDOUT:   %int_2.ecc: Core.IntLiteral = int_value 2 [concrete]
+// CHECK:STDOUT:   %u2: type = class_type @UInt, @UInt(%int_2.ecc) [concrete]
+// CHECK:STDOUT:   %struct_type.discriminant: type = struct_type {.discriminant: %u2} [concrete]
+// CHECK:STDOUT:   %complete_type.de2: <witness> = complete_type_witness %struct_type.discriminant [concrete]
+// CHECK:STDOUT:   %int_0.5c6: Core.IntLiteral = int_value 0 [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.54b: type = facet_type <@ImplicitAs, @ImplicitAs(%u2)> [concrete]
+// CHECK:STDOUT:   %Convert.type.f0e: type = fn_type @Convert.1, @ImplicitAs(%u2) [concrete]
+// CHECK:STDOUT:   %impl_witness.f5e: <witness> = impl_witness (imports.%Core.import_ref.c3d), @impl.1(%int_2.ecc) [concrete]
+// CHECK:STDOUT:   %Convert.type.70b: type = fn_type @Convert.2, @impl.1(%int_2.ecc) [concrete]
+// CHECK:STDOUT:   %Convert.474: %Convert.type.70b = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet: %ImplicitAs.type.54b = facet_value Core.IntLiteral, %impl_witness.f5e [concrete]
+// CHECK:STDOUT:   %.89d: type = fn_type_with_self_type %Convert.type.f0e, %ImplicitAs.facet [concrete]
+// CHECK:STDOUT:   %Convert.bound.5bb: <bound method> = bound_method %int_0.5c6, %Convert.474 [concrete]
+// CHECK:STDOUT:   %Convert.specific_fn.dd9: <specific function> = specific_function %Convert.bound.5bb, @Convert.2(%int_2.ecc) [concrete]
+// CHECK:STDOUT:   %int_0.9fd: %u2 = int_value 0 [concrete]
+// CHECK:STDOUT:   %Ordering.val.a29: %Ordering = struct_value (%int_0.9fd) [concrete]
+// CHECK:STDOUT:   %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
+// CHECK:STDOUT:   %Convert.bound.0dd: <bound method> = bound_method %int_1.5b8, %Convert.474 [concrete]
+// CHECK:STDOUT:   %Convert.specific_fn.95d: <specific function> = specific_function %Convert.bound.0dd, @Convert.2(%int_2.ecc) [concrete]
+// CHECK:STDOUT:   %int_1.b2c: %u2 = int_value 1 [concrete]
+// CHECK:STDOUT:   %Ordering.val.927: %Ordering = struct_value (%int_1.b2c) [concrete]
+// CHECK:STDOUT:   %Convert.bound.122: <bound method> = bound_method %int_2.ecc, %Convert.474 [concrete]
+// CHECK:STDOUT:   %Convert.specific_fn.190: <specific function> = specific_function %Convert.bound.122, @Convert.2(%int_2.ecc) [concrete]
+// CHECK:STDOUT:   %int_2.788: %u2 = int_value 2 [concrete]
+// CHECK:STDOUT:   %Ordering.val.968: %Ordering = struct_value (%int_2.788) [concrete]
+// CHECK:STDOUT:   %int_3.1ba: Core.IntLiteral = int_value 3 [concrete]
+// CHECK:STDOUT:   %Convert.bound.a5c: <bound method> = bound_method %int_3.1ba, %Convert.474 [concrete]
+// CHECK:STDOUT:   %Convert.specific_fn.9bc: <specific function> = specific_function %Convert.bound.a5c, @Convert.2(%int_2.ecc) [concrete]
+// CHECK:STDOUT:   %int_3.975: %u2 = int_value 3 [concrete]
+// CHECK:STDOUT:   %Ordering.val.8a7: %Ordering = struct_value (%int_3.975) [concrete]
+// CHECK:STDOUT:   %H.type: type = fn_type @H [concrete]
+// CHECK:STDOUT:   %H: %H.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     .UInt = %Core.UInt
+// CHECK:STDOUT:     .ImplicitAs = %Core.ImplicitAs
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .Ordering = %Ordering.decl
+// CHECK:STDOUT:     .H = %H.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Ordering.decl: type = class_decl @Ordering [concrete = constants.%Ordering] {} {}
+// CHECK:STDOUT:   %H.decl: %H.type = fn_decl @H [concrete = constants.%H] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Ordering {
+// CHECK:STDOUT:   %int_2.loc8: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:   %u2: type = class_type @UInt, @UInt(constants.%int_2.ecc) [concrete = constants.%u2]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type.de2]
+// CHECK:STDOUT:   %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
+// CHECK:STDOUT:   %impl.elem0.loc4: %.89d = impl_witness_access constants.%impl_witness.f5e, element0 [concrete = constants.%Convert.474]
+// CHECK:STDOUT:   %bound_method.loc4: <bound method> = bound_method %int_0, %impl.elem0.loc4 [concrete = constants.%Convert.bound.5bb]
+// CHECK:STDOUT:   %specific_fn.loc4: <specific function> = specific_function %bound_method.loc4, @Convert.2(constants.%int_2.ecc) [concrete = constants.%Convert.specific_fn.dd9]
+// CHECK:STDOUT:   %int.convert_checked.loc4: init %u2 = call %specific_fn.loc4(%int_0) [concrete = constants.%int_0.9fd]
+// CHECK:STDOUT:   %.loc4_7.1: %u2 = value_of_initializer %int.convert_checked.loc4 [concrete = constants.%int_0.9fd]
+// CHECK:STDOUT:   %.loc4_7.2: %u2 = converted %int_0, %.loc4_7.1 [concrete = constants.%int_0.9fd]
+// CHECK:STDOUT:   %.loc4_7.3: %struct_type.discriminant = struct_literal (%.loc4_7.2)
+// CHECK:STDOUT:   %.loc4_7.4: ref %Ordering = temporary_storage
+// CHECK:STDOUT:   %.loc4_7.5: ref %u2 = class_element_access %.loc4_7.4, element0
+// CHECK:STDOUT:   %.loc4_7.6: init %u2 = initialize_from %.loc4_7.2 to %.loc4_7.5 [concrete = constants.%int_0.9fd]
+// CHECK:STDOUT:   %.loc4_7.7: init %Ordering = class_init (%.loc4_7.6), %.loc4_7.4 [concrete = constants.%Ordering.val.a29]
+// CHECK:STDOUT:   %.loc4_7.8: ref %Ordering = temporary %.loc4_7.4, %.loc4_7.7
+// CHECK:STDOUT:   %.loc4_7.9: ref %Ordering = converted %.loc4_7.3, %.loc4_7.8
+// CHECK:STDOUT:   %.loc4_7.10: %Ordering = bind_value %.loc4_7.9
+// CHECK:STDOUT:   %Less: %Ordering = bind_name Less, %.loc4_7.10
+// CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %impl.elem0.loc5: %.89d = impl_witness_access constants.%impl_witness.f5e, element0 [concrete = constants.%Convert.474]
+// CHECK:STDOUT:   %bound_method.loc5: <bound method> = bound_method %int_1, %impl.elem0.loc5 [concrete = constants.%Convert.bound.0dd]
+// CHECK:STDOUT:   %specific_fn.loc5: <specific function> = specific_function %bound_method.loc5, @Convert.2(constants.%int_2.ecc) [concrete = constants.%Convert.specific_fn.95d]
+// CHECK:STDOUT:   %int.convert_checked.loc5: init %u2 = call %specific_fn.loc5(%int_1) [concrete = constants.%int_1.b2c]
+// CHECK:STDOUT:   %.loc5_13.1: %u2 = value_of_initializer %int.convert_checked.loc5 [concrete = constants.%int_1.b2c]
+// CHECK:STDOUT:   %.loc5_13.2: %u2 = converted %int_1, %.loc5_13.1 [concrete = constants.%int_1.b2c]
+// CHECK:STDOUT:   %.loc5_13.3: %struct_type.discriminant = struct_literal (%.loc5_13.2)
+// CHECK:STDOUT:   %.loc5_13.4: ref %Ordering = temporary_storage
+// CHECK:STDOUT:   %.loc5_13.5: ref %u2 = class_element_access %.loc5_13.4, element0
+// CHECK:STDOUT:   %.loc5_13.6: init %u2 = initialize_from %.loc5_13.2 to %.loc5_13.5 [concrete = constants.%int_1.b2c]
+// CHECK:STDOUT:   %.loc5_13.7: init %Ordering = class_init (%.loc5_13.6), %.loc5_13.4 [concrete = constants.%Ordering.val.927]
+// CHECK:STDOUT:   %.loc5_13.8: ref %Ordering = temporary %.loc5_13.4, %.loc5_13.7
+// CHECK:STDOUT:   %.loc5_13.9: ref %Ordering = converted %.loc5_13.3, %.loc5_13.8
+// CHECK:STDOUT:   %.loc5_13.10: %Ordering = bind_value %.loc5_13.9
+// CHECK:STDOUT:   %Equivalent: %Ordering = bind_name Equivalent, %.loc5_13.10
+// CHECK:STDOUT:   %int_2.loc6: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:   %impl.elem0.loc6: %.89d = impl_witness_access constants.%impl_witness.f5e, element0 [concrete = constants.%Convert.474]
+// CHECK:STDOUT:   %bound_method.loc6: <bound method> = bound_method %int_2.loc6, %impl.elem0.loc6 [concrete = constants.%Convert.bound.122]
+// CHECK:STDOUT:   %specific_fn.loc6: <specific function> = specific_function %bound_method.loc6, @Convert.2(constants.%int_2.ecc) [concrete = constants.%Convert.specific_fn.190]
+// CHECK:STDOUT:   %int.convert_checked.loc6: init %u2 = call %specific_fn.loc6(%int_2.loc6) [concrete = constants.%int_2.788]
+// CHECK:STDOUT:   %.loc6_10.1: %u2 = value_of_initializer %int.convert_checked.loc6 [concrete = constants.%int_2.788]
+// CHECK:STDOUT:   %.loc6_10.2: %u2 = converted %int_2.loc6, %.loc6_10.1 [concrete = constants.%int_2.788]
+// CHECK:STDOUT:   %.loc6_10.3: %struct_type.discriminant = struct_literal (%.loc6_10.2)
+// CHECK:STDOUT:   %.loc6_10.4: ref %Ordering = temporary_storage
+// CHECK:STDOUT:   %.loc6_10.5: ref %u2 = class_element_access %.loc6_10.4, element0
+// CHECK:STDOUT:   %.loc6_10.6: init %u2 = initialize_from %.loc6_10.2 to %.loc6_10.5 [concrete = constants.%int_2.788]
+// CHECK:STDOUT:   %.loc6_10.7: init %Ordering = class_init (%.loc6_10.6), %.loc6_10.4 [concrete = constants.%Ordering.val.968]
+// CHECK:STDOUT:   %.loc6_10.8: ref %Ordering = temporary %.loc6_10.4, %.loc6_10.7
+// CHECK:STDOUT:   %.loc6_10.9: ref %Ordering = converted %.loc6_10.3, %.loc6_10.8
+// CHECK:STDOUT:   %.loc6_10.10: %Ordering = bind_value %.loc6_10.9
+// CHECK:STDOUT:   %Greater: %Ordering = bind_name Greater, %.loc6_10.10
+// CHECK:STDOUT:   %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba]
+// CHECK:STDOUT:   %impl.elem0.loc8: %.89d = impl_witness_access constants.%impl_witness.f5e, element0 [concrete = constants.%Convert.474]
+// CHECK:STDOUT:   %bound_method.loc8: <bound method> = bound_method %int_3, %impl.elem0.loc8 [concrete = constants.%Convert.bound.a5c]
+// CHECK:STDOUT:   %specific_fn.loc8: <specific function> = specific_function %bound_method.loc8, @Convert.2(constants.%int_2.ecc) [concrete = constants.%Convert.specific_fn.9bc]
+// CHECK:STDOUT:   %int.convert_checked.loc8: init %u2 = call %specific_fn.loc8(%int_3) [concrete = constants.%int_3.975]
+// CHECK:STDOUT:   %.loc8_1.1: %u2 = value_of_initializer %int.convert_checked.loc8 [concrete = constants.%int_3.975]
+// CHECK:STDOUT:   %.loc8_1.2: %u2 = converted %int_3, %.loc8_1.1 [concrete = constants.%int_3.975]
+// CHECK:STDOUT:   %.loc8_1.3: %struct_type.discriminant = struct_literal (%.loc8_1.2)
+// CHECK:STDOUT:   %.loc8_1.4: ref %Ordering = temporary_storage
+// CHECK:STDOUT:   %.loc8_1.5: ref %u2 = class_element_access %.loc8_1.4, element0
+// CHECK:STDOUT:   %.loc8_1.6: init %u2 = initialize_from %.loc8_1.2 to %.loc8_1.5 [concrete = constants.%int_3.975]
+// CHECK:STDOUT:   %.loc8_1.7: init %Ordering = class_init (%.loc8_1.6), %.loc8_1.4 [concrete = constants.%Ordering.val.8a7]
+// CHECK:STDOUT:   %.loc8_1.8: ref %Ordering = temporary %.loc8_1.4, %.loc8_1.7
+// CHECK:STDOUT:   %.loc8_1.9: ref %Ordering = converted %.loc8_1.3, %.loc8_1.8
+// CHECK:STDOUT:   %.loc8_1.10: %Ordering = bind_value %.loc8_1.9
+// CHECK:STDOUT:   %Incomparable: %Ordering = bind_name Incomparable, %.loc8_1.10
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Ordering
+// CHECK:STDOUT:   .Less = %Less
+// CHECK:STDOUT:   .Equivalent = %Equivalent
+// CHECK:STDOUT:   .Greater = %Greater
+// CHECK:STDOUT:   .Incomparable = %Incomparable
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @H() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %less.patt: %Ordering = binding_pattern less
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Ordering.ref.loc11_24: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering]
+// CHECK:STDOUT:   %Less.ref: %Ordering = name_ref Less, @Ordering.%Less
+// CHECK:STDOUT:   %Ordering.ref.loc11_13: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering]
+// CHECK:STDOUT:   %less: %Ordering = bind_name less, %Less.ref
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %equiv.patt: %Ordering = binding_pattern equiv
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Ordering.ref.loc12_25: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering]
+// CHECK:STDOUT:   %Equivalent.ref: %Ordering = name_ref Equivalent, @Ordering.%Equivalent
+// CHECK:STDOUT:   %Ordering.ref.loc12_14: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering]
+// CHECK:STDOUT:   %equiv: %Ordering = bind_name equiv, %Equivalent.ref
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %greater.patt: %Ordering = binding_pattern greater
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Ordering.ref.loc13_27: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering]
+// CHECK:STDOUT:   %Greater.ref: %Ordering = name_ref Greater, @Ordering.%Greater
+// CHECK:STDOUT:   %Ordering.ref.loc13_16: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering]
+// CHECK:STDOUT:   %greater: %Ordering = bind_name greater, %Greater.ref
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %inc.patt: %Ordering = binding_pattern inc
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Ordering.ref.loc14_23: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering]
+// CHECK:STDOUT:   %Incomparable.ref: %Ordering = name_ref Incomparable, @Ordering.%Incomparable
+// CHECK:STDOUT:   %Ordering.ref.loc14_12: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering]
+// CHECK:STDOUT:   %inc: %Ordering = bind_name inc, %Incomparable.ref
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_empty_params.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Always: type = class_type @Always [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %struct_type.discriminant: type = struct_type {.discriminant: %empty_tuple.type} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete]
+// CHECK:STDOUT:   %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
+// CHECK:STDOUT:   %Always.val: %Always = struct_value (%empty_tuple) [concrete]
+// CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .Always = %Always.decl
+// CHECK:STDOUT:     .G = %G.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Always.decl: type = class_decl @Always [concrete = constants.%Always] {} {}
+// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Always {
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %.loc9_1.1: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:   %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   %.loc9_1.2: %empty_tuple.type = converted %.loc9_1.1, %empty_tuple [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   %.loc9_1.3: %struct_type.discriminant = struct_literal (%.loc9_1.2)
+// CHECK:STDOUT:   %.loc9_1.4: ref %Always = temporary_storage
+// CHECK:STDOUT:   %.loc9_1.5: ref %empty_tuple.type = class_element_access %.loc9_1.4, element0
+// CHECK:STDOUT:   %.loc9_1.6: init %empty_tuple.type = tuple_init () to %.loc9_1.5 [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   %.loc9_1.7: init %empty_tuple.type = converted %.loc9_1.2, %.loc9_1.6 [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   %.loc9_1.8: init %Always = class_init (%.loc9_1.7), %.loc9_1.4 [concrete = constants.%Always.val]
+// CHECK:STDOUT:   %.loc9_1.9: ref %Always = temporary %.loc9_1.4, %.loc9_1.8
+// CHECK:STDOUT:   %.loc9_1.10: ref %Always = converted %.loc9_1.3, %.loc9_1.9
+// CHECK:STDOUT:   %.loc9_1.11: %Always = bind_value %.loc9_1.10
+// CHECK:STDOUT:   %Sunny: %Always = bind_name Sunny, %.loc9_1.11
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Always
+// CHECK:STDOUT:   .Sunny = %Sunny
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %mood.patt: %Always = binding_pattern mood
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Always.ref.loc12_22: type = name_ref Always, file.%Always.decl [concrete = constants.%Always]
+// CHECK:STDOUT:   %Sunny.ref: %Always = name_ref Sunny, @Always.%Sunny
+// CHECK:STDOUT:   %Always.ref.loc12_13: type = name_ref Always, file.%Always.decl [concrete = constants.%Always]
+// CHECK:STDOUT:   %mood: %Always = bind_name mood, %Sunny.ref
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 76 - 0
toolchain/check/testdata/choice/fail_invalid.carbon

@@ -0,0 +1,76 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/choice/fail_invalid.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/choice/fail_invalid.carbon
+
+// --- fail_no_alternative_construct.carbon
+library "[[@TEST_NAME]]";
+
+choice Never {}
+
+fn F() {
+  // TODO: Can we produce a better diagnostic?
+  // CHECK:STDERR: fail_no_alternative_construct.carbon:[[@LINE+4]]:22: error: cannot initialize class with 1 field from struct with 0 fields [StructInitElementCountMismatch]
+  // CHECK:STDERR:   let never: Never = {};
+  // CHECK:STDERR:                      ^~
+  // CHECK:STDERR:
+  let never: Never = {};
+}
+
+// CHECK:STDOUT: --- fail_no_alternative_construct.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Never: type = class_type @Never [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %struct_type.discriminant: type = struct_type {.discriminant: %empty_tuple.type} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .Never = %Never.decl
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Never.decl: type = class_decl @Never [concrete = constants.%Never] {} {}
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Never {
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Never
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %never.patt: %Never = binding_pattern never
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc11_23.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %Never.ref: type = name_ref Never, file.%Never.decl [concrete = constants.%Never]
+// CHECK:STDOUT:   %.loc11_23.2: ref %Never = temporary_storage
+// CHECK:STDOUT:   %.loc11_23.3: ref %Never = temporary %.loc11_23.2, <error>
+// CHECK:STDOUT:   %.loc11_23.4: ref %Never = converted %.loc11_23.1, %.loc11_23.3
+// CHECK:STDOUT:   %never: ref %Never = bind_name never, %.loc11_23.4
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 253 - 0
toolchain/check/testdata/choice/fail_todo_params.carbon

@@ -0,0 +1,253 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/choice/fail_todo_params.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/choice/fail_todo_params.carbon
+
+// --- fail_todo_params.carbon
+library "[[@TEST_NAME]]";
+
+choice C {
+  Alt1,
+  // CHECK:STDERR: fail_todo_params.carbon:[[@LINE+4]]:7: error: semantics TODO: `choice alternatives with parameters are not yet supported` [SemanticsTodo]
+  // CHECK:STDERR:   Alt2(a: i32, b: i64),
+  // CHECK:STDERR:       ^~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  Alt2(a: i32, b: i64),
+  Alt3,
+}
+
+// --- fail_todo_generic_params.carbon
+library "[[@TEST_NAME]]";
+
+choice C(T:! type) {
+  // CHECK:STDERR: fail_todo_generic_params.carbon:[[@LINE+4]]:6: error: semantics TODO: `choice alternatives with parameters are not yet supported` [SemanticsTodo]
+  // CHECK:STDERR:   Alt(a: T)
+  // CHECK:STDERR:      ^~~~~~
+  // CHECK:STDERR:
+  Alt(a: T)
+}
+
+// --- fail_todo_self_param.carbon
+library "[[@TEST_NAME]]";
+
+choice C {
+  // CHECK:STDERR: fail_todo_self_param.carbon:[[@LINE+4]]:6: error: semantics TODO: `choice alternatives with parameters are not yet supported` [SemanticsTodo]
+  // CHECK:STDERR:   Alt(a: Self*)
+  // CHECK:STDERR:      ^~~~~~~~~~
+  // CHECK:STDERR:
+  Alt(a: Self*)
+}
+
+// CHECK:STDOUT: --- fail_todo_params.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %int_64: Core.IntLiteral = int_value 64 [concrete]
+// CHECK:STDOUT:   %i64: type = class_type @Int, @Int(%int_64) [concrete]
+// CHECK:STDOUT:   %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
+// CHECK:STDOUT:   %u1: type = class_type @UInt, @UInt(%int_1.5b8) [concrete]
+// CHECK:STDOUT:   %struct_type.discriminant: type = struct_type {.discriminant: %u1} [concrete]
+// CHECK:STDOUT:   %complete_type.df6: <witness> = complete_type_witness %struct_type.discriminant [concrete]
+// CHECK:STDOUT:   %int_0.5c6: Core.IntLiteral = int_value 0 [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.766: type = facet_type <@ImplicitAs, @ImplicitAs(%u1)> [concrete]
+// CHECK:STDOUT:   %Convert.type.5a2: type = fn_type @Convert.1, @ImplicitAs(%u1) [concrete]
+// CHECK:STDOUT:   %impl_witness.514: <witness> = impl_witness (imports.%Core.import_ref.c3d), @impl.1(%int_1.5b8) [concrete]
+// CHECK:STDOUT:   %Convert.type.f9b: type = fn_type @Convert.2, @impl.1(%int_1.5b8) [concrete]
+// CHECK:STDOUT:   %Convert.84e: %Convert.type.f9b = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet: %ImplicitAs.type.766 = facet_value Core.IntLiteral, %impl_witness.514 [concrete]
+// CHECK:STDOUT:   %.996: type = fn_type_with_self_type %Convert.type.5a2, %ImplicitAs.facet [concrete]
+// CHECK:STDOUT:   %Convert.bound.db0: <bound method> = bound_method %int_0.5c6, %Convert.84e [concrete]
+// CHECK:STDOUT:   %Convert.specific_fn.e7f: <specific function> = specific_function %Convert.bound.db0, @Convert.2(%int_1.5b8) [concrete]
+// CHECK:STDOUT:   %int_0.cd3: %u1 = int_value 0 [concrete]
+// CHECK:STDOUT:   %C.val.6ef: %C = struct_value (%int_0.cd3) [concrete]
+// CHECK:STDOUT:   %Convert.bound.1f9: <bound method> = bound_method %int_1.5b8, %Convert.84e [concrete]
+// CHECK:STDOUT:   %Convert.specific_fn.9b2: <specific function> = specific_function %Convert.bound.1f9, @Convert.2(%int_1.5b8) [concrete]
+// CHECK:STDOUT:   %int_1.ae4: %u1 = int_value 1 [concrete]
+// CHECK:STDOUT:   %C.val.5bf: %C = struct_value (%int_1.ae4) [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     .Int = %Core.Int
+// CHECK:STDOUT:     .UInt = %Core.UInt
+// CHECK:STDOUT:     .ImplicitAs = %Core.ImplicitAs
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %a.param: %i32 = value_param runtime_param0
+// CHECK:STDOUT:   %.loc9_11: type = splice_block %i32 [concrete = constants.%i32] {
+// CHECK:STDOUT:     %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %a: %i32 = bind_name a, %a.param
+// CHECK:STDOUT:   %b.param: %i64 = value_param runtime_param1
+// CHECK:STDOUT:   %.loc9_19: type = splice_block %i64 [concrete = constants.%i64] {
+// CHECK:STDOUT:     %int_64: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
+// CHECK:STDOUT:     %i64: type = class_type @Int, @Int(constants.%int_64) [concrete = constants.%i64]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %b: %i64 = bind_name b, %b.param
+// CHECK:STDOUT:   %int_1.loc11: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %u1: type = class_type @UInt, @UInt(constants.%int_1.5b8) [concrete = constants.%u1]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type.df6]
+// CHECK:STDOUT:   %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
+// CHECK:STDOUT:   %impl.elem0.loc4: %.996 = impl_witness_access constants.%impl_witness.514, element0 [concrete = constants.%Convert.84e]
+// CHECK:STDOUT:   %bound_method.loc4: <bound method> = bound_method %int_0, %impl.elem0.loc4 [concrete = constants.%Convert.bound.db0]
+// CHECK:STDOUT:   %specific_fn.loc4: <specific function> = specific_function %bound_method.loc4, @Convert.2(constants.%int_1.5b8) [concrete = constants.%Convert.specific_fn.e7f]
+// CHECK:STDOUT:   %int.convert_checked.loc4: init %u1 = call %specific_fn.loc4(%int_0) [concrete = constants.%int_0.cd3]
+// CHECK:STDOUT:   %.loc4_7.1: %u1 = value_of_initializer %int.convert_checked.loc4 [concrete = constants.%int_0.cd3]
+// CHECK:STDOUT:   %.loc4_7.2: %u1 = converted %int_0, %.loc4_7.1 [concrete = constants.%int_0.cd3]
+// CHECK:STDOUT:   %.loc4_7.3: %struct_type.discriminant = struct_literal (%.loc4_7.2)
+// CHECK:STDOUT:   %.loc4_7.4: ref %C = temporary_storage
+// CHECK:STDOUT:   %.loc4_7.5: ref %u1 = class_element_access %.loc4_7.4, element0
+// CHECK:STDOUT:   %.loc4_7.6: init %u1 = initialize_from %.loc4_7.2 to %.loc4_7.5 [concrete = constants.%int_0.cd3]
+// CHECK:STDOUT:   %.loc4_7.7: init %C = class_init (%.loc4_7.6), %.loc4_7.4 [concrete = constants.%C.val.6ef]
+// CHECK:STDOUT:   %.loc4_7.8: ref %C = temporary %.loc4_7.4, %.loc4_7.7
+// CHECK:STDOUT:   %.loc4_7.9: ref %C = converted %.loc4_7.3, %.loc4_7.8
+// CHECK:STDOUT:   %.loc4_7.10: %C = bind_value %.loc4_7.9
+// CHECK:STDOUT:   %Alt1: %C = bind_name Alt1, %.loc4_7.10
+// CHECK:STDOUT:   %int_1.loc10: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %impl.elem0.loc10: %.996 = impl_witness_access constants.%impl_witness.514, element0 [concrete = constants.%Convert.84e]
+// CHECK:STDOUT:   %bound_method.loc10: <bound method> = bound_method %int_1.loc10, %impl.elem0.loc10 [concrete = constants.%Convert.bound.1f9]
+// CHECK:STDOUT:   %specific_fn.loc10: <specific function> = specific_function %bound_method.loc10, @Convert.2(constants.%int_1.5b8) [concrete = constants.%Convert.specific_fn.9b2]
+// CHECK:STDOUT:   %int.convert_checked.loc10: init %u1 = call %specific_fn.loc10(%int_1.loc10) [concrete = constants.%int_1.ae4]
+// CHECK:STDOUT:   %.loc10_7.1: %u1 = value_of_initializer %int.convert_checked.loc10 [concrete = constants.%int_1.ae4]
+// CHECK:STDOUT:   %.loc10_7.2: %u1 = converted %int_1.loc10, %.loc10_7.1 [concrete = constants.%int_1.ae4]
+// CHECK:STDOUT:   %.loc10_7.3: %struct_type.discriminant = struct_literal (%.loc10_7.2)
+// CHECK:STDOUT:   %.loc10_7.4: ref %C = temporary_storage
+// CHECK:STDOUT:   %.loc10_7.5: ref %u1 = class_element_access %.loc10_7.4, element0
+// CHECK:STDOUT:   %.loc10_7.6: init %u1 = initialize_from %.loc10_7.2 to %.loc10_7.5 [concrete = constants.%int_1.ae4]
+// CHECK:STDOUT:   %.loc10_7.7: init %C = class_init (%.loc10_7.6), %.loc10_7.4 [concrete = constants.%C.val.5bf]
+// CHECK:STDOUT:   %.loc10_7.8: ref %C = temporary %.loc10_7.4, %.loc10_7.7
+// CHECK:STDOUT:   %.loc10_7.9: ref %C = converted %.loc10_7.3, %.loc10_7.8
+// CHECK:STDOUT:   %.loc10_7.10: %C = bind_value %.loc10_7.9
+// CHECK:STDOUT:   %Alt3: %C = bind_name Alt3, %.loc10_7.10
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT:   .a = %a
+// CHECK:STDOUT:   .b = %b
+// CHECK:STDOUT:   .Alt1 = %Alt1
+// CHECK:STDOUT:   .Alt3 = %Alt3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_generic_params.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT:   %T.patt: type = symbolic_binding_pattern T, 0 [symbolic]
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %C.generic: %C.type = struct_value () [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C, @C(%T) [symbolic]
+// CHECK:STDOUT:   %struct_type.discriminant: type = struct_type {.discriminant: %empty_tuple.type} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] {
+// CHECK:STDOUT:     %T.patt.loc3_10.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc3_10.2 (constants.%T.patt)]
+// CHECK:STDOUT:     %T.param_patt: type = value_param_pattern %T.patt.loc3_10.1, runtime_param<none> [symbolic = %T.patt.loc3_10.2 (constants.%T.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %T.param: type = value_param runtime_param<none>
+// CHECK:STDOUT:     %T.loc3_10.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc3_10.2 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @C(%T.loc3_10.1: type) {
+// CHECK:STDOUT:   %T.loc3_10.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc3_10.2 (constants.%T)]
+// CHECK:STDOUT:   %T.patt.loc3_10.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc3_10.2 (constants.%T.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %a.param: @C.%T.loc3_10.2 (%T) = value_param runtime_param0
+// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc3_10.1 [symbolic = %T.loc3_10.2 (constants.%T)]
+// CHECK:STDOUT:     %a: @C.%T.loc3_10.2 (%T) = bind_name a, %a.param
+// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:     complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%C
+// CHECK:STDOUT:     .T = <poisoned>
+// CHECK:STDOUT:     .a = %a
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @C(constants.%T) {
+// CHECK:STDOUT:   %T.loc3_10.2 => constants.%T
+// CHECK:STDOUT:   %T.patt.loc3_10.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_self_param.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
+// CHECK:STDOUT:   %ptr: type = ptr_type %C [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %struct_type.discriminant: type = struct_type {.discriminant: %empty_tuple.type} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %a.param: %ptr = value_param runtime_param0
+// CHECK:STDOUT:   %.loc8: type = splice_block %ptr [concrete = constants.%ptr] {
+// CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C]
+// CHECK:STDOUT:     %ptr: type = ptr_type %C [concrete = constants.%ptr]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %a: %ptr = bind_name a, %a.param
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT:   .a = %a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 91 - 0
toolchain/check/testdata/choice/generic.carbon

@@ -0,0 +1,91 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/choice/generic.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/choice/generic.carbon
+
+choice Always(T:! type) {
+  Sunny
+}
+
+// CHECK:STDOUT: --- generic.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT:   %T.patt: type = symbolic_binding_pattern T, 0 [symbolic]
+// CHECK:STDOUT:   %Always.type: type = generic_class_type @Always [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %Always.generic: %Always.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Always: type = class_type @Always, @Always(%T) [symbolic]
+// CHECK:STDOUT:   %struct_type.discriminant: type = struct_type {.discriminant: %empty_tuple.type} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete]
+// CHECK:STDOUT:   %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %Always [symbolic]
+// CHECK:STDOUT:   %Always.val: %Always = struct_value (%empty_tuple) [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .Always = %Always.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Always.decl: %Always.type = class_decl @Always [concrete = constants.%Always.generic] {
+// CHECK:STDOUT:     %T.patt.loc11_15.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc11_15.2 (constants.%T.patt)]
+// CHECK:STDOUT:     %T.param_patt: type = value_param_pattern %T.patt.loc11_15.1, runtime_param<none> [symbolic = %T.patt.loc11_15.2 (constants.%T.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %T.param: type = value_param runtime_param<none>
+// CHECK:STDOUT:     %T.loc11_15.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc11_15.2 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @Always(%T.loc11_15.1: type) {
+// CHECK:STDOUT:   %T.loc11_15.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc11_15.2 (constants.%T)]
+// CHECK:STDOUT:   %T.patt.loc11_15.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc11_15.2 (constants.%T.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Always: type = class_type @Always, @Always(%T.loc11_15.2) [symbolic = %Always (constants.%Always)]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type @Always.%Always (%Always) [symbolic = %require_complete (constants.%require_complete)]
+// CHECK:STDOUT:   %Always.val: @Always.%Always (%Always) = struct_value (constants.%empty_tuple) [symbolic = %Always.val (constants.%Always.val)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:     %.loc13_1.1: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:     %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:     %.loc13_1.2: %empty_tuple.type = converted %.loc13_1.1, %empty_tuple [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:     %.loc13_1.3: %struct_type.discriminant = struct_literal (%.loc13_1.2)
+// CHECK:STDOUT:     %.loc13_1.4: ref @Always.%Always (%Always) = temporary_storage
+// CHECK:STDOUT:     %.loc13_1.5: ref %empty_tuple.type = class_element_access %.loc13_1.4, element0
+// CHECK:STDOUT:     %.loc13_1.6: init %empty_tuple.type = tuple_init () to %.loc13_1.5 [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:     %.loc13_1.7: init %empty_tuple.type = converted %.loc13_1.2, %.loc13_1.6 [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:     %.loc13_1.8: init @Always.%Always (%Always) = class_init (%.loc13_1.7), %.loc13_1.4 [symbolic = %Always.val (constants.%Always.val)]
+// CHECK:STDOUT:     %.loc13_1.9: ref @Always.%Always (%Always) = temporary %.loc13_1.4, %.loc13_1.8
+// CHECK:STDOUT:     %.loc13_1.10: ref @Always.%Always (%Always) = converted %.loc13_1.3, %.loc13_1.9
+// CHECK:STDOUT:     %.loc13_1.11: @Always.%Always (%Always) = bind_value %.loc13_1.10
+// CHECK:STDOUT:     %Sunny: @Always.%Always (%Always) = bind_name Sunny, %.loc13_1.11
+// CHECK:STDOUT:     complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%Always
+// CHECK:STDOUT:     .Sunny = %Sunny
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Always(constants.%T) {
+// CHECK:STDOUT:   %T.loc11_15.2 => constants.%T
+// CHECK:STDOUT:   %T.patt.loc11_15.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Always(%T.loc11_15.2) {}
+// CHECK:STDOUT:

+ 1 - 1
toolchain/lex/token_kind.def

@@ -150,6 +150,7 @@ CARBON_CLOSING_GROUP_SYMBOL_TOKEN(CloseSquareBracket, "]", OpenSquareBracket)
 CARBON_DECL_INTRODUCER_TOKEN(Adapt,       "adapt")
 CARBON_DECL_INTRODUCER_TOKEN(Alias,       "alias")
 CARBON_DECL_INTRODUCER_TOKEN(Base,        "base")
+CARBON_DECL_INTRODUCER_TOKEN(Choice,      "choice")
 CARBON_DECL_INTRODUCER_TOKEN(Class,       "class")
 CARBON_DECL_INTRODUCER_TOKEN(Constraint,  "constraint")
 CARBON_DECL_INTRODUCER_TOKEN(Export,      "export")
@@ -173,7 +174,6 @@ CARBON_KEYWORD_TOKEN(Auto,                "auto")
 CARBON_KEYWORD_TOKEN(Bool,                "bool")
 CARBON_KEYWORD_TOKEN(Break,               "break")
 CARBON_KEYWORD_TOKEN(Case,                "case")
-CARBON_KEYWORD_TOKEN(Choice,              "choice")
 CARBON_KEYWORD_TOKEN(Const,               "const")
 CARBON_KEYWORD_TOKEN(Continue,            "continue")
 CARBON_KEYWORD_TOKEN(Core,                "Core")

+ 6 - 1
toolchain/parse/node_ids.h

@@ -97,7 +97,12 @@ struct NodeIdOneOf : public NodeId {
   constexpr NodeIdOneOf(NoneNodeId /*none*/) : NodeId(NoneIndex) {}
 };
 
-using AnyClassDeclId = NodeIdOneOf<ClassDeclId, ClassDefinitionStartId>;
+using AnyClassDeclId =
+    NodeIdOneOf<ClassDeclId, ClassDefinitionStartId,
+                // TODO: This may be wrong? But we have choice types produce a
+                // class, so they are a form of class decls. This avoids
+                // duplicating all of SemIR::ClassDecl.
+                ChoiceDefinitionStartId>;
 using AnyFunctionDeclId = NodeIdOneOf<FunctionDeclId, FunctionDefinitionStartId,
                                       BuiltinFunctionDefinitionStartId>;
 using AnyImplDeclId = NodeIdOneOf<ImplDeclId, ImplDefinitionStartId>;

+ 1 - 1
toolchain/parse/state.def

@@ -1432,7 +1432,7 @@ CARBON_PARSE_STATE(LetFinish)
 // choice ...
 // ^~~~~~
 //   1. DeclNameAndParams
-//   2. ChoiceAlternativeListStart
+//   2. ChoiceDefinitionStart
 //   3. ChoiceDefinitionFinish
 CARBON_PARSE_STATE(ChoiceIntroducer)
 

+ 5 - 2
toolchain/sem_ir/ids.h

@@ -492,6 +492,8 @@ struct NameId : public IdBase<NameId> {
   static const NameId SelfValue;
   // The name of `vptr`.
   static const NameId Vptr;
+  // The name of the discriminant field (if any) in a choice.
+  static const NameId ChoiceDiscriminant;
 
   // The number of non-index (<0) that exist, and will need storage in name
   // lookup.
@@ -524,9 +526,10 @@ constexpr NameId NameId::ReturnSlot = NameId(NoneIndex - 5);
 constexpr NameId NameId::SelfType = NameId(NoneIndex - 6);
 constexpr NameId NameId::SelfValue = NameId(NoneIndex - 7);
 constexpr NameId NameId::Vptr = NameId(NoneIndex - 8);
-constexpr int NameId::NonIndexValueCount = 9;
+constexpr NameId NameId::ChoiceDiscriminant = NameId(NoneIndex - 9);
+constexpr int NameId::NonIndexValueCount = 10;
 // Enforce the link between SpecialValueCount and the last special value.
-static_assert(NameId::NonIndexValueCount == -NameId::Vptr.index);
+static_assert(NameId::NonIndexValueCount == -NameId::ChoiceDiscriminant.index);
 
 // The ID of a name scope.
 struct NameScopeId : public IdBase<NameScopeId> {

+ 2 - 0
toolchain/sem_ir/name.cpp

@@ -29,6 +29,8 @@ static auto GetSpecialName(NameId name_id, bool for_ir) -> llvm::StringRef {
       return "self";
     case NameId::Vptr.index:
       return for_ir ? "vptr" : "<vptr>";
+    case NameId::ChoiceDiscriminant.index:
+      return "discriminant";
     default:
       CARBON_FATAL("Unknown special name");
   }