diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 9c2ad2e5b7d3..7e405881e71f 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -479,6 +479,7 @@ auto Interpreter::Convert(Nonnull 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 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(*method.body()), std::move(method_scope)); } - case Value::Kind::NominalClassType: { - const NominalClassType& class_type = cast(*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( - &class_decl, generic_args, witnesses)); - case Phase::CompileTime: - return todo_.FinishAction(arena_->New( - &class_decl, generic_args, call.impls())); - } - } - case Value::Kind::InterfaceType: { - const InterfaceType& iface_type = cast(*fun); - const InterfaceDeclaration& iface_decl = iface_type.declaration(); + case Value::Kind::ParameterizedEntityName: { + const auto& name = cast(*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(), ¶ms_scope, generic_args, - trace_stream_)); - switch (phase()) { - case Phase::RunTime: - return todo_.FinishAction( - arena_->New(&iface_decl, generic_args, witnesses)); - case Phase::CompileTime: - return todo_.FinishAction(arena_->New( - &iface_decl, generic_args, call.impls())); + CHECK(PatternMatch(&name.params().value(), arg, call.source_loc(), + ¶ms_scope, generic_args, trace_stream_)); + switch (decl.kind()) { + case DeclarationKind::ClassDeclaration: { + switch (phase()) { + case Phase::RunTime: + return todo_.FinishAction(arena_->New( + &cast(decl), generic_args, witnesses)); + case Phase::CompileTime: + return todo_.FinishAction(arena_->New( + &cast(decl), generic_args, call.impls())); + } + } + case DeclarationKind::InterfaceDeclaration: { + switch (phase()) { + case Phase::RunTime: + return todo_.FinishAction(arena_->New( + &cast(decl), generic_args, witnesses)); + case Phase::CompileTime: + return todo_.FinishAction( + arena_->New(&cast(decl), + generic_args, call.impls())); + } + } + default: + FATAL() << "unknown kind of ParameterizedEntityName " << decl; } } default: diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index d4f95b8898a3..2aaebe0e9144 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -79,6 +79,7 @@ static auto IsType(Nonnull 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 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 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 value) -> bool { } return true; } - case Value::Kind::NominalClassType: - return !cast(*value).IsParameterized(); - case Value::Kind::InterfaceType: - return !cast(*value).IsParameterized(); } } @@ -140,6 +140,7 @@ static auto IsConcreteType(Nonnull 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 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 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 value) -> bool { } return true; } - case Value::Kind::NominalClassType: - return !cast(*value).IsParameterized(); - case Value::Kind::InterfaceType: - return !cast(*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(*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 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(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(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 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", + ¶m_name.params().static_type(), + &call.argument().static_type())); + ASSIGN_OR_RETURN(Nonnull arg, + InterpExp(&call.argument(), arena_, trace_stream_)); + CHECK(PatternMatch(¶m_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 e, impls.emplace(impl_binding, impl); } } - Nonnull inst_class_type = - arena_->New(&class_decl, generic_args, impls); call.set_impls(impls); - call.set_static_type(arena_->New(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(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 inst_class_type = + arena_->New(&cast(decl), + generic_args, impls); + call.set_static_type( + arena_->New(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 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 impl_binding = - *binding->impl_binding(); - ASSIGN_OR_RETURN(Nonnull impl, - impl_scope.Resolve(impl_binding->interface(), - generic_args[binding], - call.source_loc(), *this)); - impls.emplace(impl_binding, impl); + case DeclarationKind::InterfaceDeclaration: { + Nonnull inst_iface_type = + arena_->New(&cast(decl), + generic_args, impls); + call.set_static_type( + arena_->New(inst_iface_type)); + break; } + default: + FATAL() << "unknown type of ParameterizedEntityName for " << decl; } - Nonnull inst_iface_type = - arena_->New(&iface_decl, generic_args, impls); - call.set_impls(impls); - call.set_static_type( - arena_->New(inst_iface_type)); - call.set_value_category(ValueCategory::Let); return Success(); } default: { @@ -1808,10 +1766,12 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull class_decl, **trace_stream_ << class_scope; } - Nonnull class_type = - arena_->New(class_decl); - SetConstantValue(class_decl, class_type); - class_decl->set_static_type(arena_->New(class_type)); + Nonnull param_name = + arena_->New(class_decl, + *class_decl->type_params()); + SetConstantValue(class_decl, param_name); + class_decl->set_static_type( + arena_->New(param_name)); for (Nonnull 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 iface_type = arena_->New(iface_decl); - SetConstantValue(iface_decl, iface_type); - iface_decl->set_static_type(arena_->New(iface_type)); + Nonnull param_name = + arena_->New(iface_decl, *iface_decl->params()); + SetConstantValue(iface_decl, param_name); + iface_decl->set_static_type( + arena_->New(param_name)); + } else { + Nonnull iface_type = arena_->New(iface_decl); + SetConstantValue(iface_decl, iface_type); + iface_decl->set_static_type(arena_->New(iface_type)); + } // Process the Self parameter. RETURN_IF_ERROR(TypeCheckPattern(iface_decl->self(), std::nullopt, @@ -1934,10 +1900,6 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull 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); diff --git a/explorer/interpreter/value.cpp b/explorer/interpreter/value.cpp index 424b3420ddd6..6f201998b38b 100644 --- a/explorer/interpreter/value.cpp +++ b/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(*this).declaration()); + break; case Value::Kind::ChoiceType: out << "choice " << cast(*this).name(); break; @@ -383,6 +386,10 @@ void Value::Print(llvm::raw_ostream& out) const { out << "typeof(" << cast(*this).choice_type().name() << ")"; break; + case Value::Kind::TypeOfParameterizedEntityName: + out << "typeof(" << cast(*this).name() + << ")"; + break; case Value::Kind::StaticArrayType: { const auto& array_type = cast(*this); out << "[" << array_type.element_type() << "; " << array_type.size() @@ -514,6 +521,10 @@ auto TypeEqual(Nonnull t1, Nonnull t2) -> bool { case Value::Kind::TypeOfChoiceType: return TypeEqual(&cast(*t1).choice_type(), &cast(*t2).choice_type()); + case Value::Kind::TypeOfParameterizedEntityName: { + return ValueEqual(&cast(*t1).name(), + &cast(*t2).name()); + } case Value::Kind::StaticArrayType: { const auto& array1 = cast(*t1); const auto& array2 = cast(*t2); @@ -533,6 +544,7 @@ auto TypeEqual(Nonnull t1, Nonnull 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 v1, Nonnull v2) -> bool { } case Value::Kind::StringValue: return cast(*v1).value() == cast(*v2).value(); + case Value::Kind::ParameterizedEntityName: { + std::optional name1 = + GetName(cast(v1)->declaration()); + std::optional name2 = + GetName(cast(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 v1, Nonnull 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: diff --git a/explorer/interpreter/value.h b/explorer/interpreter/value.h index 3d610fc3f09e..6cff29043a67 100644 --- a/explorer/interpreter/value.h +++ b/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 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 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 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 declaration_; BindingMap args_; @@ -711,6 +714,29 @@ class VariableType : public Value { Nonnull 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 declaration, + Nonnull 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 declaration_; + Nonnull 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 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 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 name_; +}; + // The type of a statically-sized array. // // Note that values of this type are represented as tuples. diff --git a/explorer/testdata/generic_class/fail_instantiate_non_generic.carbon b/explorer/testdata/generic_class/fail_instantiate_non_generic.carbon index ae914c3224da..54b53521208a 100644 --- a/explorer/testdata/generic_class/fail_instantiate_non_generic.carbon +++ b/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; } diff --git a/explorer/testdata/generic_class/fail_return_type_is_type.carbon b/explorer/testdata/generic_class/fail_return_type_is_type.carbon index 5fb74c8b1c8d..7232ddcda347 100644 --- a/explorer/testdata/generic_class/fail_return_type_is_type.carbon +++ b/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; diff --git a/explorer/testdata/generic_class/fail_two_arg_lists.carbon b/explorer/testdata/generic_class/fail_two_arg_lists.carbon index 440f026fb4fa..ab46bfffa21d 100644 --- a/explorer/testdata/generic_class/fail_two_arg_lists.carbon +++ b/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}; } diff --git a/explorer/testdata/impl/fail_impl_as_parameterized.carbon b/explorer/testdata/impl/fail_impl_as_parameterized.carbon index bf5991277e09..b799e8e43c2c 100644 --- a/explorer/testdata/impl/fail_impl_as_parameterized.carbon +++ b/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 { diff --git a/explorer/update_checks.py b/explorer/update_checks.py index baf69f5be793..90cdb574c195 100755 --- a/explorer/update_checks.py +++ b/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):