diff --git a/executable_semantics/interpreter/type_checker.cpp b/executable_semantics/interpreter/type_checker.cpp index 2b3cb31b9a79..88baa3acb37d 100644 --- a/executable_semantics/interpreter/type_checker.cpp +++ b/executable_semantics/interpreter/type_checker.cpp @@ -1056,12 +1056,12 @@ auto TypeChecker::TypeCheckExp(Nonnull e, case Value::Kind::TypeOfClassType: { // This case handles the application of a generic class to // a type argument, such as Point(i32). - const ClassDeclaration& class_decl = - cast(call.function().static_type()) - .class_type() - .declaration(); + const NominalClassType& class_type = + cast(call.function().static_type()).class_type(); + const ClassDeclaration& class_decl = class_type.declaration(); BindingMap generic_args; - if (class_decl.type_params().has_value()) { + if (class_decl.type_params().has_value() && + class_type.type_args().empty()) { if (trace_stream_) { **trace_stream_ << "pattern matching type params and args\n"; } @@ -1075,6 +1075,10 @@ auto TypeChecker::TypeCheckExp(Nonnull e, 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; @@ -1104,10 +1108,10 @@ auto TypeChecker::TypeCheckExp(Nonnull e, } } } - Nonnull class_type = + Nonnull inst_class_type = arena_->New(&class_decl, generic_args, impls); call.set_impls(impls); - call.set_static_type(arena_->New(class_type)); + call.set_static_type(arena_->New(inst_class_type)); call.set_value_category(ValueCategory::Let); return Success(); } diff --git a/executable_semantics/testdata/generic_class/fail_two_arg_lists.carbon b/executable_semantics/testdata/generic_class/fail_two_arg_lists.carbon new file mode 100644 index 000000000000..b76e8f851a5e --- /dev/null +++ b/executable_semantics/testdata/generic_class/fail_two_arg_lists.carbon @@ -0,0 +1,30 @@ +// 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 +// +// RUN: %{not} %{executable_semantics} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{executable_semantics} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{executable_semantics} %s +// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/generic_class/fail_two_arg_lists.carbon:15: attempt to instantiate an already instantiated generic class: Point(T)(T) + +package ExecutableSemanticsTest api; + +class Point(T:! Type) { + fn Origin(zero: T) -> Point(T)(T) { + return {.x = zero, .y = zero}; + } + + fn GetX[me: Point(T)(T)]() -> T { + return me.x; + } + + var x: T; + var y: T; +} + +fn Main() -> i32 { + var p: Point(i32) = Point(i32).Origin(0); + return p.GetX(); +}