Browse Source

Use a separate type for a parameterized entity name that is waiting for its arguments to arrive. (#1223)

Previously we modeled these as being class types or interface types, with special case checks to treat them as not actually being class or interface types.
Richard Smith 4 years ago
parent
commit
a2176e1e28

+ 31 - 34
explorer/interpreter/interpreter.cpp

@@ -479,6 +479,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
     case Value::Kind::NominalClassType:
     case Value::Kind::InterfaceType:
     case Value::Kind::Witness:
+    case Value::Kind::ParameterizedEntityName:
     case Value::Kind::ChoiceType:
     case Value::Kind::ContinuationType:
     case Value::Kind::VariableType:
@@ -490,6 +491,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
     case Value::Kind::TypeOfClassType:
     case Value::Kind::TypeOfInterfaceType:
     case Value::Kind::TypeOfChoiceType:
+    case Value::Kind::TypeOfParameterizedEntityName:
     case Value::Kind::StaticArrayType:
       // TODO: add `CHECK(TypeEqual(type, value->dynamic_type()))`, once we
       // have Value::dynamic_type.
@@ -628,42 +630,37 @@ auto Interpreter::CallFunction(const CallExpression& call,
       return todo_.Spawn(std::make_unique<StatementAction>(*method.body()),
                          std::move(method_scope));
     }
-    case Value::Kind::NominalClassType: {
-      const NominalClassType& class_type = cast<NominalClassType>(*fun);
-      const ClassDeclaration& class_decl = class_type.declaration();
-      RuntimeScope type_params_scope(&heap_);
-      BindingMap generic_args;
-      CHECK(class_decl.type_params().has_value())
-          << "instantiation of non-generic class " << class_type;
-      CHECK(PatternMatch(&(*class_decl.type_params())->value(), arg,
-                         call.source_loc(), &type_params_scope, generic_args,
-                         trace_stream_));
-      switch (phase()) {
-        case Phase::RunTime:
-          return todo_.FinishAction(arena_->New<NominalClassType>(
-              &class_decl, generic_args, witnesses));
-        case Phase::CompileTime:
-          return todo_.FinishAction(arena_->New<NominalClassType>(
-              &class_decl, generic_args, call.impls()));
-      }
-    }
-    case Value::Kind::InterfaceType: {
-      const InterfaceType& iface_type = cast<InterfaceType>(*fun);
-      const InterfaceDeclaration& iface_decl = iface_type.declaration();
+    case Value::Kind::ParameterizedEntityName: {
+      const auto& name = cast<ParameterizedEntityName>(*fun);
+      const Declaration& decl = name.declaration();
       RuntimeScope params_scope(&heap_);
       BindingMap generic_args;
-      CHECK(iface_decl.params().has_value())
-          << "call of unparameterized interface " << iface_type;
-      CHECK(PatternMatch(&(*iface_decl.params())->value(), arg,
-                         call.source_loc(), &params_scope, generic_args,
-                         trace_stream_));
-      switch (phase()) {
-        case Phase::RunTime:
-          return todo_.FinishAction(
-              arena_->New<InterfaceType>(&iface_decl, generic_args, witnesses));
-        case Phase::CompileTime:
-          return todo_.FinishAction(arena_->New<InterfaceType>(
-              &iface_decl, generic_args, call.impls()));
+      CHECK(PatternMatch(&name.params().value(), arg, call.source_loc(),
+                         &params_scope, generic_args, trace_stream_));
+      switch (decl.kind()) {
+        case DeclarationKind::ClassDeclaration: {
+          switch (phase()) {
+            case Phase::RunTime:
+              return todo_.FinishAction(arena_->New<NominalClassType>(
+                  &cast<ClassDeclaration>(decl), generic_args, witnesses));
+            case Phase::CompileTime:
+              return todo_.FinishAction(arena_->New<NominalClassType>(
+                  &cast<ClassDeclaration>(decl), generic_args, call.impls()));
+          }
+        }
+        case DeclarationKind::InterfaceDeclaration: {
+          switch (phase()) {
+            case Phase::RunTime:
+              return todo_.FinishAction(arena_->New<InterfaceType>(
+                  &cast<InterfaceDeclaration>(decl), generic_args, witnesses));
+            case Phase::CompileTime:
+              return todo_.FinishAction(
+                  arena_->New<InterfaceType>(&cast<InterfaceDeclaration>(decl),
+                                             generic_args, call.impls()));
+          }
+        }
+        default:
+          FATAL() << "unknown kind of ParameterizedEntityName " << decl;
       }
     }
     default:

+ 67 - 105
explorer/interpreter/type_checker.cpp

@@ -79,6 +79,7 @@ static auto IsType(Nonnull<const Value*> value) -> bool {
     case Value::Kind::ContinuationValue:
     case Value::Kind::StringValue:
     case Value::Kind::Witness:
+    case Value::Kind::ParameterizedEntityName:
       return false;
     case Value::Kind::IntType:
     case Value::Kind::BoolType:
@@ -86,6 +87,8 @@ static auto IsType(Nonnull<const Value*> value) -> bool {
     case Value::Kind::FunctionType:
     case Value::Kind::PointerType:
     case Value::Kind::StructType:
+    case Value::Kind::NominalClassType:
+    case Value::Kind::InterfaceType:
     case Value::Kind::ChoiceType:
     case Value::Kind::ContinuationType:
     case Value::Kind::VariableType:
@@ -93,6 +96,7 @@ static auto IsType(Nonnull<const Value*> value) -> bool {
     case Value::Kind::TypeOfClassType:
     case Value::Kind::TypeOfInterfaceType:
     case Value::Kind::TypeOfChoiceType:
+    case Value::Kind::TypeOfParameterizedEntityName:
     case Value::Kind::StaticArrayType:
     case Value::Kind::AutoType:
       return true;
@@ -104,10 +108,6 @@ static auto IsType(Nonnull<const Value*> value) -> bool {
       }
       return true;
     }
-    case Value::Kind::NominalClassType:
-      return !cast<NominalClassType>(*value).IsParameterized();
-    case Value::Kind::InterfaceType:
-      return !cast<InterfaceType>(*value).IsParameterized();
   }
 }
 
@@ -140,6 +140,7 @@ static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
     case Value::Kind::ContinuationValue:
     case Value::Kind::StringValue:
     case Value::Kind::Witness:
+    case Value::Kind::ParameterizedEntityName:
       return false;
     case Value::Kind::IntType:
     case Value::Kind::BoolType:
@@ -147,6 +148,8 @@ static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
     case Value::Kind::FunctionType:
     case Value::Kind::PointerType:
     case Value::Kind::StructType:
+    case Value::Kind::NominalClassType:
+    case Value::Kind::InterfaceType:
     case Value::Kind::ChoiceType:
     case Value::Kind::ContinuationType:
     case Value::Kind::VariableType:
@@ -154,6 +157,7 @@ static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
     case Value::Kind::TypeOfClassType:
     case Value::Kind::TypeOfInterfaceType:
     case Value::Kind::TypeOfChoiceType:
+    case Value::Kind::TypeOfParameterizedEntityName:
     case Value::Kind::StaticArrayType:
       return true;
     case Value::Kind::AutoType:
@@ -167,10 +171,6 @@ static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
       }
       return true;
     }
-    case Value::Kind::NominalClassType:
-      return !cast<NominalClassType>(*value).IsParameterized();
-    case Value::Kind::InterfaceType:
-      return !cast<InterfaceType>(*value).IsParameterized();
   }
 }
 
@@ -292,10 +292,8 @@ auto TypeChecker::IsImplicitlyConvertible(
       return destination->kind() == Value::Kind::InterfaceType;
     case Value::Kind::InterfaceType:
       return destination->kind() == Value::Kind::TypeType;
-    case Value::Kind::TypeOfClassType: {
-      return !cast<TypeOfClassType>(*source).class_type().IsParameterized() &&
-             destination->kind() == Value::Kind::TypeType;
-    }
+    case Value::Kind::TypeOfClassType:
+      return destination->kind() == Value::Kind::TypeType;
     default:
       return false;
   }
@@ -452,9 +450,11 @@ auto TypeChecker::ArgumentDeduction(
     case Value::Kind::TypeOfClassType:
     case Value::Kind::TypeOfInterfaceType:
     case Value::Kind::TypeOfChoiceType:
+    case Value::Kind::TypeOfParameterizedEntityName:
       return ExpectType(source_loc, "argument deduction", param_type, arg_type);
     // The rest of these cases should never happen.
     case Value::Kind::Witness:
+    case Value::Kind::ParameterizedEntityName:
     case Value::Kind::IntValue:
     case Value::Kind::BoolValue:
     case Value::Kind::FunctionValue:
@@ -551,9 +551,11 @@ auto TypeChecker::Substitute(
     case Value::Kind::TypeOfClassType:
     case Value::Kind::TypeOfInterfaceType:
     case Value::Kind::TypeOfChoiceType:
+    case Value::Kind::TypeOfParameterizedEntityName:
       return type;
     // The rest of these cases should never happen.
     case Value::Kind::Witness:
+    case Value::Kind::ParameterizedEntityName:
     case Value::Kind::IntValue:
     case Value::Kind::BoolValue:
     case Value::Kind::FunctionValue:
@@ -1056,36 +1058,26 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           call.set_value_category(ValueCategory::Let);
           return Success();
         }
-        case Value::Kind::TypeOfClassType: {
-          // This case handles the application of a generic class to
-          // a type argument, such as Point(i32).
-          const NominalClassType& class_type =
-              cast<TypeOfClassType>(call.function().static_type()).class_type();
-          const ClassDeclaration& class_decl = class_type.declaration();
+        case Value::Kind::TypeOfParameterizedEntityName: {
+          // This case handles the application of a parameterized class or
+          // interface to a set of arguments, such as Point(i32) or
+          // AddWith(i32).
+          const ParameterizedEntityName& param_name =
+              cast<TypeOfParameterizedEntityName>(call.function().static_type())
+                  .name();
           BindingMap generic_args;
-          if (class_type.IsParameterized()) {
-            if (trace_stream_) {
-              **trace_stream_ << "pattern matching type params and args\n";
-            }
-            RETURN_IF_ERROR(
-                ExpectType(call.source_loc(), "call",
-                           &(*class_decl.type_params())->static_type(),
-                           &call.argument().static_type()));
-            ASSIGN_OR_RETURN(
-                Nonnull<const Value*> arg,
-                InterpExp(&call.argument(), arena_, trace_stream_));
-            CHECK(PatternMatch(&(*class_decl.type_params())->value(), arg,
-                               call.source_loc(), std::nullopt, generic_args,
-                               trace_stream_));
-          } else if (!class_type.type_args().empty()) {
-            return CompilationError(call.source_loc())
-                   << "attempt to instantiate an already instantiated "
-                   << "generic class: " << *e;
-          } else {
-            return CompilationError(call.source_loc())
-                   << "attempt to instantiate a non-generic class: " << *e;
+          if (trace_stream_) {
+            **trace_stream_ << "pattern matching type params and args\n";
           }
-          // Find impls for all the impl bindings of the class.
+          RETURN_IF_ERROR(ExpectType(call.source_loc(), "call",
+                                     &param_name.params().static_type(),
+                                     &call.argument().static_type()));
+          ASSIGN_OR_RETURN(Nonnull<const Value*> arg,
+                           InterpExp(&call.argument(), arena_, trace_stream_));
+          CHECK(PatternMatch(&param_name.params().value(), arg,
+                             call.source_loc(), std::nullopt, generic_args,
+                             trace_stream_));
+          // Find impls for all the impl bindings.
           ImplExpMap impls;
           for (const auto& [binding, val] : generic_args) {
             if (binding->impl_binding().has_value()) {
@@ -1098,64 +1090,30 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
               impls.emplace(impl_binding, impl);
             }
           }
-          Nonnull<NominalClassType*> inst_class_type =
-              arena_->New<NominalClassType>(&class_decl, generic_args, impls);
           call.set_impls(impls);
-          call.set_static_type(arena_->New<TypeOfClassType>(inst_class_type));
           call.set_value_category(ValueCategory::Let);
-          return Success();
-        }
-        case Value::Kind::TypeOfInterfaceType: {
-          // This case handles the application of a parameterized class to
-          // its arguments, such as OrderedWith(i32).
-          // FIXME: This is very repetitive with the corresponding case for
-          // parameterized classes.
-          const InterfaceType& iface_type =
-              cast<TypeOfInterfaceType>(call.function().static_type())
-                  .interface_type();
-          const InterfaceDeclaration& iface_decl = iface_type.declaration();
-          BindingMap generic_args;
-          if (iface_type.IsParameterized()) {
-            if (trace_stream_) {
-              **trace_stream_ << "pattern matching interface params and args\n";
+
+          const Declaration& decl = param_name.declaration();
+          switch (decl.kind()) {
+            case DeclarationKind::ClassDeclaration: {
+              Nonnull<NominalClassType*> inst_class_type =
+                  arena_->New<NominalClassType>(&cast<ClassDeclaration>(decl),
+                                                generic_args, impls);
+              call.set_static_type(
+                  arena_->New<TypeOfClassType>(inst_class_type));
+              break;
             }
-            RETURN_IF_ERROR(ExpectType(call.source_loc(), "call",
-                                       &(*iface_decl.params())->static_type(),
-                                       &call.argument().static_type()));
-            ASSIGN_OR_RETURN(
-                Nonnull<const Value*> arg,
-                InterpExp(&call.argument(), arena_, trace_stream_));
-            CHECK(PatternMatch(&(*iface_decl.params())->value(), arg,
-                               call.source_loc(), std::nullopt, generic_args,
-                               trace_stream_));
-          } else if (!iface_type.args().empty()) {
-            return CompilationError(call.source_loc())
-                   << "attempt to provide arguments to parameterized interface "
-                   << "twice: " << *e;
-          } else {
-            return CompilationError(call.source_loc())
-                   << "attempt to provide arguments to non-parameterized "
-                   << "interface: " << *e;
-          }
-          // Find impls for all the impl bindings of the interface.
-          ImplExpMap impls;
-          for (const auto& [binding, val] : generic_args) {
-            if (binding->impl_binding().has_value()) {
-              Nonnull<const ImplBinding*> impl_binding =
-                  *binding->impl_binding();
-              ASSIGN_OR_RETURN(Nonnull<Expression*> impl,
-                               impl_scope.Resolve(impl_binding->interface(),
-                                                  generic_args[binding],
-                                                  call.source_loc(), *this));
-              impls.emplace(impl_binding, impl);
+            case DeclarationKind::InterfaceDeclaration: {
+              Nonnull<InterfaceType*> inst_iface_type =
+                  arena_->New<InterfaceType>(&cast<InterfaceDeclaration>(decl),
+                                             generic_args, impls);
+              call.set_static_type(
+                  arena_->New<TypeOfInterfaceType>(inst_iface_type));
+              break;
             }
+            default:
+              FATAL() << "unknown type of ParameterizedEntityName for " << decl;
           }
-          Nonnull<InterfaceType*> inst_iface_type =
-              arena_->New<InterfaceType>(&iface_decl, generic_args, impls);
-          call.set_impls(impls);
-          call.set_static_type(
-              arena_->New<TypeOfInterfaceType>(inst_iface_type));
-          call.set_value_category(ValueCategory::Let);
           return Success();
         }
         default: {
@@ -1808,10 +1766,12 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
       **trace_stream_ << class_scope;
     }
 
-    Nonnull<NominalClassType*> class_type =
-        arena_->New<NominalClassType>(class_decl);
-    SetConstantValue(class_decl, class_type);
-    class_decl->set_static_type(arena_->New<TypeOfClassType>(class_type));
+    Nonnull<ParameterizedEntityName*> param_name =
+        arena_->New<ParameterizedEntityName>(class_decl,
+                                             *class_decl->type_params());
+    SetConstantValue(class_decl, param_name);
+    class_decl->set_static_type(
+        arena_->New<TypeOfParameterizedEntityName>(param_name));
 
     for (Nonnull<Declaration*> m : class_decl->members()) {
       RETURN_IF_ERROR(DeclareDeclaration(m, class_scope));
@@ -1875,11 +1835,17 @@ auto TypeChecker::DeclareInterfaceDeclaration(
     if (trace_stream_) {
       **trace_stream_ << iface_scope;
     }
-  }
 
-  Nonnull<InterfaceType*> iface_type = arena_->New<InterfaceType>(iface_decl);
-  SetConstantValue(iface_decl, iface_type);
-  iface_decl->set_static_type(arena_->New<TypeOfInterfaceType>(iface_type));
+    Nonnull<ParameterizedEntityName*> param_name =
+        arena_->New<ParameterizedEntityName>(iface_decl, *iface_decl->params());
+    SetConstantValue(iface_decl, param_name);
+    iface_decl->set_static_type(
+        arena_->New<TypeOfParameterizedEntityName>(param_name));
+  } else {
+    Nonnull<InterfaceType*> iface_type = arena_->New<InterfaceType>(iface_decl);
+    SetConstantValue(iface_decl, iface_type);
+    iface_decl->set_static_type(arena_->New<TypeOfInterfaceType>(iface_type));
+  }
 
   // Process the Self parameter.
   RETURN_IF_ERROR(TypeCheckPattern(iface_decl->self(), std::nullopt,
@@ -1934,10 +1900,6 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
            << "expected constraint after `as`, found value of type "
            << *written_iface_type;
   }
-  if (iface_type->IsParameterized()) {
-    return CompilationError(impl_decl->interface().source_loc())
-           << "missing arguments for parameterized interface";
-  }
 
   const auto& iface_decl = iface_type->declaration();
   impl_decl->set_interface_type(iface_type);

+ 22 - 0
explorer/interpreter/value.cpp

@@ -350,6 +350,9 @@ void Value::Print(llvm::raw_ostream& out) const {
           << witness.declaration().interface();
       break;
     }
+    case Value::Kind::ParameterizedEntityName:
+      out << *GetName(cast<ParameterizedEntityName>(*this).declaration());
+      break;
     case Value::Kind::ChoiceType:
       out << "choice " << cast<ChoiceType>(*this).name();
       break;
@@ -383,6 +386,10 @@ void Value::Print(llvm::raw_ostream& out) const {
       out << "typeof(" << cast<TypeOfChoiceType>(*this).choice_type().name()
           << ")";
       break;
+    case Value::Kind::TypeOfParameterizedEntityName:
+      out << "typeof(" << cast<TypeOfParameterizedEntityName>(*this).name()
+          << ")";
+      break;
     case Value::Kind::StaticArrayType: {
       const auto& array_type = cast<StaticArrayType>(*this);
       out << "[" << array_type.element_type() << "; " << array_type.size()
@@ -514,6 +521,10 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
     case Value::Kind::TypeOfChoiceType:
       return TypeEqual(&cast<TypeOfChoiceType>(*t1).choice_type(),
                        &cast<TypeOfChoiceType>(*t2).choice_type());
+    case Value::Kind::TypeOfParameterizedEntityName: {
+      return ValueEqual(&cast<TypeOfParameterizedEntityName>(*t1).name(),
+                        &cast<TypeOfParameterizedEntityName>(*t2).name());
+    }
     case Value::Kind::StaticArrayType: {
       const auto& array1 = cast<StaticArrayType>(*t1);
       const auto& array2 = cast<StaticArrayType>(*t2);
@@ -533,6 +544,7 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
     case Value::Kind::LValue:
     case Value::Kind::BindingPlaceholderValue:
     case Value::Kind::ContinuationValue:
+    case Value::Kind::ParameterizedEntityName:
       FATAL() << "TypeEqual used to compare non-type values\n"
               << *t1 << "\n"
               << *t2;
@@ -604,6 +616,15 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2) -> bool {
     }
     case Value::Kind::StringValue:
       return cast<StringValue>(*v1).value() == cast<StringValue>(*v2).value();
+    case Value::Kind::ParameterizedEntityName: {
+      std::optional<std::string> name1 =
+          GetName(cast<ParameterizedEntityName>(v1)->declaration());
+      std::optional<std::string> name2 =
+          GetName(cast<ParameterizedEntityName>(v2)->declaration());
+      CHECK(name1.has_value() && name2.has_value())
+          << "parameterized name refers to unnamed declaration";
+      return *name1 == *name2;
+    }
     case Value::Kind::IntType:
     case Value::Kind::BoolType:
     case Value::Kind::TypeType:
@@ -621,6 +642,7 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2) -> bool {
     case Value::Kind::TypeOfClassType:
     case Value::Kind::TypeOfInterfaceType:
     case Value::Kind::TypeOfChoiceType:
+    case Value::Kind::TypeOfParameterizedEntityName:
     case Value::Kind::StaticArrayType:
       return TypeEqual(v1, v2);
     case Value::Kind::NominalClassValue:

+ 53 - 8
explorer/interpreter/value.h

@@ -57,6 +57,7 @@ class Value {
     ChoiceType,
     ContinuationType,  // The type of a continuation.
     VariableType,      // e.g., generic type parameters.
+    ParameterizedEntityName,
     BindingPlaceholderValue,
     AlternativeConstructorValue,
     ContinuationValue,  // A first-class continuation value.
@@ -65,6 +66,7 @@ class Value {
     TypeOfClassType,
     TypeOfInterfaceType,
     TypeOfChoiceType,
+    TypeOfParameterizedEntityName,
     StaticArrayType,
   };
 
@@ -506,10 +508,12 @@ class StructType : public Value {
 // TODO: Consider splitting this class into several classes.
 class NominalClassType : public Value {
  public:
-  // Construct a non-generic class type or a generic class type that has
-  // not yet been applied to type arguments.
+  // Construct a non-generic class type.
   explicit NominalClassType(Nonnull<const ClassDeclaration*> declaration)
-      : Value(Kind::NominalClassType), declaration_(declaration) {}
+      : Value(Kind::NominalClassType), declaration_(declaration) {
+    CHECK(!declaration->type_params().has_value())
+        << "missing arguments for parameterized class type";
+  }
 
   // Construct a class type that represents the result of applying the
   // given generic class to the `type_args`.
@@ -587,7 +591,10 @@ auto FindMember(const std::string& name,
 class InterfaceType : public Value {
  public:
   explicit InterfaceType(Nonnull<const InterfaceDeclaration*> declaration)
-      : Value(Kind::InterfaceType), declaration_(declaration) {}
+      : Value(Kind::InterfaceType), declaration_(declaration) {
+    CHECK(!declaration->params().has_value())
+        << "missing arguments for parameterized interface type";
+  }
   explicit InterfaceType(Nonnull<const InterfaceDeclaration*> declaration,
                          const BindingMap& args)
       : Value(Kind::InterfaceType), declaration_(declaration), args_(args) {}
@@ -617,10 +624,6 @@ class InterfaceType : public Value {
   auto impls() const -> const ImplExpMap& { return impls_; }
   auto witnesses() const -> const ImplWitnessMap& { return witnesses_; }
 
-  auto IsParameterized() const -> bool {
-    return declaration_->params().has_value() && args_.empty();
-  }
-
  private:
   Nonnull<const InterfaceDeclaration*> declaration_;
   BindingMap args_;
@@ -711,6 +714,29 @@ class VariableType : public Value {
   Nonnull<const GenericBinding*> binding_;
 };
 
+// A name of an entity that has explicit parameters, such as a parameterized
+// class or interface. When arguments for those parameters are provided in a
+// call, the result will be a class type or interface type.
+class ParameterizedEntityName : public Value {
+ public:
+  explicit ParameterizedEntityName(Nonnull<const Declaration*> declaration,
+                                   Nonnull<const TuplePattern*> params)
+      : Value(Kind::ParameterizedEntityName),
+        declaration_(declaration),
+        params_(params) {}
+
+  static auto classof(const Value* value) -> bool {
+    return value->kind() == Kind::ParameterizedEntityName;
+  }
+
+  auto declaration() const -> const Declaration& { return *declaration_; }
+  auto params() const -> const TuplePattern& { return *params_; }
+
+ private:
+  Nonnull<const Declaration*> declaration_;
+  Nonnull<const TuplePattern*> params_;
+};
+
 // A first-class continuation representation of a fragment of the stack.
 // A continuation value behaves like a pointer to the underlying stack
 // fragment, which is exposed by `Stack()`.
@@ -844,6 +870,25 @@ class TypeOfChoiceType : public Value {
   Nonnull<const ChoiceType*> choice_type_;
 };
 
+// The type of an expression whose value is the name of a parameterized entity.
+// Such an expression can only be used as the operand of a call expression that
+// provides arguments for the parameters.
+class TypeOfParameterizedEntityName : public Value {
+ public:
+  explicit TypeOfParameterizedEntityName(
+      Nonnull<const ParameterizedEntityName*> name)
+      : Value(Kind::TypeOfParameterizedEntityName), name_(name) {}
+
+  static auto classof(const Value* value) -> bool {
+    return value->kind() == Kind::TypeOfParameterizedEntityName;
+  }
+
+  auto name() const -> const ParameterizedEntityName& { return *name_; }
+
+ private:
+  Nonnull<const ParameterizedEntityName*> name_;
+};
+
 // The type of a statically-sized array.
 //
 // Note that values of this type are represented as tuples.

+ 4 - 1
explorer/testdata/generic_class/fail_instantiate_non_generic.carbon

@@ -19,7 +19,10 @@ class Point {
 }
 
 fn Main() -> i32 {
-  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_instantiate_non_generic.carbon:[[@LINE+1]]: attempt to instantiate a non-generic class: Point(i32)
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_instantiate_non_generic.carbon:[[@LINE+4]]: in call, expected a function
+  // CHECK: Point(i32)
+  // CHECK: not an operator of type typeof(class Point)
+  // CHECK-EMPTY:
   var p: Point(i32) = Point.Origin();
   return 0;
 }

+ 1 - 1
explorer/testdata/generic_class/fail_return_type_is_type.carbon

@@ -14,7 +14,7 @@ class Point(T:! Type) {
   // The return type should be Point(T). Point by itself is not a type.
   fn Create(x: T, y: T) -> Point {
     return {.x = x, .y = y};
-  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_return_type_is_type.carbon:[[@LINE+1]]: Expected a type, but got class Point
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_return_type_is_type.carbon:[[@LINE+1]]: Expected a type, but got Point
   }
 
   var x: T;

+ 4 - 1
explorer/testdata/generic_class/fail_two_arg_lists.carbon

@@ -11,7 +11,10 @@
 package ExplorerTest api;
 
 class Point(T:! Type) {
-  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_two_arg_lists.carbon:[[@LINE+1]]: attempt to instantiate an already instantiated generic class: Point(T)(T)
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_two_arg_lists.carbon:[[@LINE+4]]: in call, expected a function
+  // CHECK: Point(T)(T)
+  // CHECK: not an operator of type typeof(class Point(T = T:! Type))
+  // CHECK-EMPTY:
   fn Origin(zero: T) -> Point(T)(T) {
     return {.x = zero, .y = zero};
   }

+ 1 - 1
explorer/testdata/impl/fail_impl_as_parameterized.carbon

@@ -13,7 +13,7 @@ package ExplorerTest api;
 interface Vector(Scalar:! Type) {
 }
 
-// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/impl/fail_impl_as_parameterized.carbon:[[@LINE+1]]: missing arguments for parameterized interface
+// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/impl/fail_impl_as_parameterized.carbon:[[@LINE+1]]: expected constraint after `as`, found value of type Vector
 external impl i32 as Vector {}
 
 fn Main() -> i32 {

+ 1 - 1
explorer/update_checks.py

@@ -92,7 +92,7 @@ class SimpleCheckLine(CheckLine):
         if self.expected:
             return f"{self.indent}// CHECK: {self.expected}\n"
         else:
-            return f"{self.indent}// CHECK-EMPTY\n"
+            return f"{self.indent}// CHECK-EMPTY:\n"
 
 
 class CheckLineWithLineNumber(CheckLine):