Selaa lähdekoodia

Scaffolding for checking impls. (#3672)

Consume the components of the `impl` declaration, and set up scopes for
the child elements. We don't yet build a representation for the impl
itself.

Also, add an interface type value. This is necessary so that we have a
value for the expression on the right-hand side of `as` in an `impl`.
Richard Smith 2 vuotta sitten
vanhempi
sitoutus
9e7a17b1a1

+ 4 - 0
toolchain/check/context.cpp

@@ -1015,6 +1015,10 @@ class TypeCompleter {
                 .object_repr_id,
             SemIR::ValueRepr::ObjectAggregate);
 
+      case SemIR::InterfaceType::Kind:
+        // TODO: Should we model the value representation as a witness?
+        return MakeEmptyValueRepr();
+
       case SemIR::Builtin::Kind:
         CARBON_FATAL() << "Builtins should be named as ImportRefUsed";
 

+ 1 - 0
toolchain/check/decl_state.h

@@ -56,6 +56,7 @@ struct DeclState {
     Class,
     Constraint,
     Fn,
+    Impl,
     Interface,
     Let,
     Namespace,

+ 2 - 1
toolchain/check/eval.cpp

@@ -367,7 +367,8 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
     // These cases are always template constants.
     case SemIR::Builtin::Kind:
     case SemIR::ClassType::Kind:
-      // TODO: Once classes have generic arguments, handle them.
+    case SemIR::InterfaceType::Kind:
+      // TODO: Once classes and interfaces have generic arguments, handle them.
       return MakeConstantResult(context, inst, Phase::Template);
 
     // These cases are treated as being the unique canonical definition of the

+ 58 - 9
toolchain/check/handle_impl.cpp

@@ -8,31 +8,80 @@ namespace Carbon::Check {
 
 auto HandleImplIntroducer(Context& context, Parse::ImplIntroducerId parse_node)
     -> bool {
-  return context.TODO(parse_node, "HandleImplIntroducer");
+  // Create an instruction block to hold the instructions created for the type
+  // and interface.
+  context.inst_block_stack().Push();
+
+  // Push the bracketing node.
+  context.node_stack().Push(parse_node);
+
+  // Optional modifiers follow.
+  context.decl_state_stack().Push(DeclState::Impl);
+
+  // Create a scope for implicit parameters. We may not use it, but it's simpler
+  // to create it unconditionally than to track whether it exists.
+  context.PushScope();
+  return true;
 }
 
 auto HandleImplForall(Context& context, Parse::ImplForallId parse_node)
     -> bool {
-  return context.TODO(parse_node, "HandleImplForall");
+  auto params_id =
+      context.node_stack().Pop<Parse::NodeKind::ImplicitParamList>();
+  context.node_stack().Push(parse_node, params_id);
+  return true;
+}
+
+auto HandleImplAs(Context& /*context*/, Parse::ImplAsId /*parse_node*/)
+    -> bool {
+  return true;
 }
 
-auto HandleImplAs(Context& context, Parse::ImplAsId parse_node) -> bool {
-  return context.TODO(parse_node, "HandleImplAs");
+static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId /*parse_node*/)
+    -> SemIR::InstId {
+  auto interface_id = context.node_stack().PopExpr();
+  auto self_id = SemIR::InstId::Invalid;
+  if (!context.node_stack().PeekIs<Parse::NodeKind::ImplForall>() &&
+      !context.node_stack().PeekIs<Parse::NodeKind::ImplIntroducer>()) {
+    self_id = context.node_stack().PopExpr();
+  }
+
+  auto params_id = context.node_stack().PopIf<Parse::NodeKind::ImplForall>();
+  auto decl_block_id = context.inst_block_stack().Pop();
+  context.node_stack()
+      .PopAndDiscardSoloParseNode<Parse::NodeKind::ImplIntroducer>();
+
+  // TODO: Build an `Impl` object.
+  static_cast<void>(decl_block_id);
+  static_cast<void>(params_id);
+  static_cast<void>(self_id);
+  static_cast<void>(interface_id);
+
+  return SemIR::InstId::Invalid;
 }
 
 auto HandleImplDecl(Context& context, Parse::ImplDeclId parse_node) -> bool {
-  return context.TODO(parse_node, "HandleImplDecl");
+  BuildImplDecl(context, parse_node);
+  context.PopScope();
+  return true;
 }
 
 auto HandleImplDefinitionStart(Context& context,
                                Parse::ImplDefinitionStartId parse_node)
     -> bool {
-  return context.TODO(parse_node, "HandleImplDefinitionStart");
+  auto impl_decl_id = BuildImplDecl(context, parse_node);
+  auto enclosing_scope_id = SemIR::NameScopeId::Invalid;
+  auto scope_id = context.name_scopes().Add(
+      impl_decl_id, SemIR::NameId::Invalid, enclosing_scope_id);
+  context.PushScope(impl_decl_id, scope_id);
+  return true;
 }
 
-auto HandleImplDefinition(Context& context, Parse::ImplDefinitionId parse_node)
-    -> bool {
-  return context.TODO(parse_node, "HandleImplDefinition");
+auto HandleImplDefinition(Context& context,
+                          Parse::ImplDefinitionId /*parse_node*/) -> bool {
+  context.PopScope();
+  context.PopScope();
+  return true;
 }
 
 }  // namespace Carbon::Check

+ 5 - 8
toolchain/check/handle_name.cpp

@@ -5,6 +5,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
+#include "toolchain/check/eval.h"
 #include "toolchain/lex/token_kind.h"
 #include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/typed_insts.h"
@@ -53,8 +54,10 @@ static auto GetExprValueForLookupResult(Context& context,
         context.classes().Get(class_decl->class_id).self_type_id);
   }
   if (auto interface_decl = lookup_result.TryAs<SemIR::InterfaceDecl>()) {
-    // TODO: unimplemented
-    return SemIR::InstId::Invalid;
+    return TryEvalInst(context, SemIR::InstId::Invalid,
+                       SemIR::InterfaceType{SemIR::TypeId::TypeType,
+                                            interface_decl->interface_id})
+        .inst_id();
   }
 
   // Anything else should be a typed value already.
@@ -106,9 +109,6 @@ auto HandleMemberAccessExpr(Context& context,
             ? context.LookupQualifiedName(parse_node, name_id, *name_scope_id)
             : SemIR::InstId::BuiltinError;
     inst_id = GetExprValueForLookupResult(context, inst_id);
-    if (!inst_id.is_valid()) {
-      return context.TODO(parse_node, "Unimplemented use of interface");
-    }
     auto inst = context.insts().Get(inst_id);
     // TODO: Track that this instruction was named within `base_id`.
     context.AddInstAndPush(
@@ -272,9 +272,6 @@ static auto HandleNameAsExpr(Context& context, Parse::NodeId parse_node,
                              SemIR::NameId name_id) -> bool {
   auto value_id = context.LookupUnqualifiedName(parse_node, name_id);
   value_id = GetExprValueForLookupResult(context, value_id);
-  if (!value_id.is_valid()) {
-    return context.TODO(parse_node, "Unimplemented use of interface");
-  }
   auto value = context.insts().Get(value_id);
   context.AddInstAndPush(
       {parse_node, SemIR::NameRef{value.type_id(), name_id, value_id}});

+ 2 - 0
toolchain/check/node_stack.h

@@ -492,6 +492,7 @@ class NodeStack {
           return IdKind::InstId;
         case Parse::NodeKind::IfCondition:
         case Parse::NodeKind::IfExprIf:
+        case Parse::NodeKind::ImplForall:
         case Parse::NodeKind::ImplicitParamList:
         case Parse::NodeKind::TuplePattern:
         case Parse::NodeKind::WhileCondition:
@@ -513,6 +514,7 @@ class NodeStack {
         case Parse::NodeKind::FunctionIntroducer:
         case Parse::NodeKind::IfStatementElse:
         case Parse::NodeKind::ImplicitParamListStart:
+        case Parse::NodeKind::ImplIntroducer:
         case Parse::NodeKind::InterfaceIntroducer:
         case Parse::NodeKind::LetIntroducer:
         case Parse::NodeKind::QualifiedName:

+ 40 - 0
toolchain/check/testdata/impl/basic.carbon

@@ -0,0 +1,40 @@
+// 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
+
+interface Simple {
+  fn F();
+}
+
+impl i32 as Simple {
+  fn F() {}
+}
+
+// CHECK:STDOUT: --- basic.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Simple [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.Simple = %Simple.decl} [template]
+// CHECK:STDOUT:   %Simple.decl = interface_decl @Simple, ()
+// CHECK:STDOUT:   %F: <function> = fn_decl @F.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Simple {
+// CHECK:STDOUT:   %F: <function> = fn_decl @F.1 [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = %F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.1();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.2() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 28 - 0
toolchain/check/testdata/impl/empty.carbon

@@ -0,0 +1,28 @@
+// 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
+
+interface Empty {
+}
+
+impl i32 as Empty {
+}
+
+// CHECK:STDOUT: --- empty.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Empty [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.Empty = %Empty.decl} [template]
+// CHECK:STDOUT:   %Empty.decl = interface_decl @Empty, ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Empty {
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 50 - 0
toolchain/check/testdata/impl/impl_as.carbon

@@ -0,0 +1,50 @@
+// 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
+
+interface Simple {
+  fn F();
+}
+
+class C {
+  impl as Simple {
+    fn F() {}
+  }
+}
+
+// CHECK:STDOUT: --- impl_as.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = interface_type @Simple [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.Simple = %Simple.decl, .C = %C.decl} [template]
+// CHECK:STDOUT:   %Simple.decl = interface_decl @Simple, ()
+// CHECK:STDOUT:   %C.decl = class_decl @C, ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Simple {
+// CHECK:STDOUT:   %F: <function> = fn_decl @F.1 [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = %F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %F: <function> = fn_decl @F.2 [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.1();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.2() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 40 - 0
toolchain/check/testdata/impl/impl_forall.carbon

@@ -0,0 +1,40 @@
+// 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
+
+interface Simple {
+  fn F();
+}
+
+impl forall [T:! type] T as Simple {
+  fn F() {}
+}
+
+// CHECK:STDOUT: --- impl_forall.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Simple [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.Simple = %Simple.decl} [template]
+// CHECK:STDOUT:   %Simple.decl = interface_decl @Simple, ()
+// CHECK:STDOUT:   %F: <function> = fn_decl @F.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Simple {
+// CHECK:STDOUT:   %F: <function> = fn_decl @F.1 [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = %F
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.1();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.2() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 33 - 0
toolchain/check/testdata/interface/as_type.carbon

@@ -0,0 +1,33 @@
+// 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
+
+interface Empty {}
+
+fn F(e: Empty) {}
+
+// CHECK:STDOUT: --- as_type.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Empty [template]
+// CHECK:STDOUT:   %.2: type = tuple_type () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.Empty = %Empty.decl, .F = %F} [template]
+// CHECK:STDOUT:   %Empty.decl = interface_decl @Empty, ()
+// CHECK:STDOUT:   %F: <function> = fn_decl @F [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Empty {
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%e: Empty) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 42 - 0
toolchain/check/testdata/interface/fail_as_type_of_type.carbon

@@ -0,0 +1,42 @@
+// 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
+
+interface Empty {}
+
+fn F(T:! Empty) {
+  // TODO: Support conversion from `T` to `type`.
+  // CHECK:STDERR: fail_as_type_of_type.carbon:[[@LINE+3]]:10: ERROR: Cannot implicitly convert from `Empty` to `type`.
+  // CHECK:STDERR:   var x: T;
+  // CHECK:STDERR:          ^
+  var x: T;
+}
+
+// CHECK:STDOUT: --- fail_as_type_of_type.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @Empty [template]
+// CHECK:STDOUT:   %.2: type = tuple_type () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace {.Empty = %Empty.decl, .F = %F} [template]
+// CHECK:STDOUT:   %Empty.decl = interface_decl @Empty, ()
+// CHECK:STDOUT:   %F: <function> = fn_decl @F [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Empty {
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%T: Empty) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %T.ref: Empty = name_ref T, %T [symbolic = %T]
+// CHECK:STDOUT:   %x.var: ref <error> = var x
+// CHECK:STDOUT:   %x: ref <error> = bind_name x, %x.var
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 4 - 0
toolchain/lower/file_context.cpp

@@ -285,6 +285,10 @@ auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* {
     }
     case SemIR::ConstType::Kind:
       return GetType(inst.As<SemIR::ConstType>().inner_id);
+    case SemIR::InterfaceType::Kind:
+      // Return an empty struct as a placeholder.
+      // TODO: Should we model an interface as a witness table?
+      return llvm::StructType::get(*llvm_context_);
     case SemIR::PointerType::Kind:
       return llvm::PointerType::get(*llvm_context_, /*AddressSpace=*/0);
     case SemIR::StructType::Kind: {

+ 5 - 0
toolchain/lower/handle_type.cpp

@@ -21,6 +21,11 @@ auto HandleConstType(FunctionContext& context, SemIR::InstId inst_id,
   context.SetLocal(inst_id, context.GetTypeAsValue());
 }
 
+auto HandleInterfaceType(FunctionContext& context, SemIR::InstId inst_id,
+                         SemIR::InterfaceType /*inst*/) -> void {
+  context.SetLocal(inst_id, context.GetTypeAsValue());
+}
+
 auto HandlePointerType(FunctionContext& context, SemIR::InstId inst_id,
                        SemIR::PointerType /*inst*/) -> void {
   context.SetLocal(inst_id, context.GetTypeAsValue());

+ 1 - 0
toolchain/parse/node_ids.h

@@ -87,6 +87,7 @@ struct NodeIdOneOf : public NodeId {
 using AnyClassDeclId = NodeIdOneOf<ClassDeclId, ClassDefinitionStartId>;
 using AnyFunctionDeclId =
     NodeIdOneOf<FunctionDeclId, FunctionDefinitionStartId>;
+using AnyImplDeclId = NodeIdOneOf<ImplDeclId, ImplDefinitionStartId>;
 using AnyInterfaceDeclId =
     NodeIdOneOf<InterfaceDeclId, InterfaceDefinitionStartId>;
 

+ 8 - 0
toolchain/sem_ir/file.cpp

@@ -203,6 +203,7 @@ static auto GetTypePrecedence(InstKind kind) -> int {
     case Builtin::Kind:
     case ClassType::Kind:
     case ImportRefUsed::Kind:
+    case InterfaceType::Kind:
     case NameRef::Kind:
     case StructType::Kind:
     case TupleType::Kind:
@@ -347,6 +348,12 @@ auto File::StringifyTypeExpr(InstId outer_inst_id) const -> std::string {
       case ImportRefUsed::Kind:
         out << "<TODO: ImportRefUsed " << step.inst_id << ">";
         break;
+      case InterfaceType::Kind: {
+        auto interface_name_id =
+            interfaces().Get(inst.As<InterfaceType>().interface_id).name_id;
+        out << names().GetFormatted(interface_name_id);
+        break;
+      }
       case NameRef::Kind: {
         out << names().GetFormatted(inst.As<NameRef>().name_id);
         break;
@@ -533,6 +540,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
       case BoundMethod::Kind:
       case ClassType::Kind:
       case ConstType::Kind:
+      case InterfaceType::Kind:
       case IntLiteral::Kind:
       case Param::Kind:
       case PointerType::Kind:

+ 1 - 2
toolchain/sem_ir/file.h

@@ -158,9 +158,8 @@ struct Interface : public Printable<Interface> {
   NameId name_id;
   // The enclosing scope.
   NameScopeId enclosing_scope_id;
-  // TODO: TypeId self_type_id;
   // The first declaration of the interface. This is a InterfaceDecl.
-  InstId decl_id = InstId::Invalid;
+  InstId decl_id;
 
   // The following members are set at the `{` of the interface definition.
 

+ 1 - 0
toolchain/sem_ir/inst_kind.def

@@ -49,6 +49,7 @@ CARBON_SEM_IR_INST_KIND(ImportRefUnused)
 CARBON_SEM_IR_INST_KIND(ImportRefUsed)
 CARBON_SEM_IR_INST_KIND(InitializeFrom)
 CARBON_SEM_IR_INST_KIND(InterfaceDecl)
+CARBON_SEM_IR_INST_KIND(InterfaceType)
 CARBON_SEM_IR_INST_KIND(IntLiteral)
 CARBON_SEM_IR_INST_KIND(NameRef)
 CARBON_SEM_IR_INST_KIND(Namespace)

+ 10 - 0
toolchain/sem_ir/typed_insts.h

@@ -464,6 +464,16 @@ struct InterfaceDecl {
   InstBlockId decl_block_id;
 };
 
+struct InterfaceType {
+  static constexpr auto Kind =
+      InstKind::InterfaceType.Define<Parse::NodeId>("interface_type");
+
+  TypeId type_id;
+  InterfaceId interface_id;
+  // TODO: Once we support generic interfaces, include the interface's arguments
+  // here.
+};
+
 struct IntLiteral {
   // TODO: Make Parse::NodeId more specific.
   static constexpr auto Kind =