From 1d0bb85d0c5b96b175fa0ff5efd8a602325ba446 Mon Sep 17 00:00:00 2001 From: Adrien Leravat Date: Tue, 6 Dec 2022 09:35:55 -0800 Subject: [PATCH] Explorer: basic `abstract` class support (#2441) Relates to #1881 Features: * Add basic support for `abstract` class * Allow extending an abstract class * Prevent direct instantiation of an abstract class Changes: * Check that class extensibility for `VariableDefinition` is not `Abstract` * Add corresponding set of lit tests --- explorer/interpreter/interpreter.cpp | 12 +++++++-- explorer/interpreter/type_checker.cpp | 5 ---- explorer/testdata/class/abstract_class.carbon | 27 +++++++++++++++++++ .../fail_abstract_class_subtyping.carbon | 25 +++++++++++++++++ ...tantiate_abstract_class_constructor.carbon | 23 ++++++++++++++++ ...l_instantiate_abstract_class_empty.carbon} | 5 ++-- ...l_instantiate_abstract_class_struct.carbon | 19 +++++++++++++ 7 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 explorer/testdata/class/abstract_class.carbon create mode 100644 explorer/testdata/class/fail_abstract_class_subtyping.carbon create mode 100644 explorer/testdata/class/fail_instantiate_abstract_class_constructor.carbon rename explorer/testdata/class/{fail_abstract_class.carbon => fail_instantiate_abstract_class_empty.carbon} (66%) create mode 100644 explorer/testdata/class/fail_instantiate_abstract_class_struct.carbon diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 853faaa97d02..5a556c3712f9 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -1777,6 +1777,15 @@ auto Interpreter::StepStmt() -> ErrorOr { } case StatementKind::VariableDefinition: { const auto& definition = cast(stmt); + const auto* dest_type = &definition.pattern().static_type(); + if (const auto* dest_class = dyn_cast(dest_type)) { + if (dest_class->declaration().extensibility() == + ClassExtensibility::Abstract) { + return ProgramError(stmt.source_loc()) + << "Cannot instantiate abstract class " + << dest_class->declaration().name(); + } + } if (act.pos() == 0 && definition.has_init()) { // { {(var x = e) :: C, E, F} :: S, H} // -> { {e :: (var x = []) :: C, E, F} :: S, H} @@ -1790,8 +1799,7 @@ auto Interpreter::StepStmt() -> ErrorOr { Nonnull v; if (definition.has_init()) { CARBON_ASSIGN_OR_RETURN( - v, Convert(act.results()[0], &definition.pattern().static_type(), - stmt.source_loc())); + v, Convert(act.results()[0], dest_type, stmt.source_loc())); } else { v = arena_->New(p); } diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index c716723889e3..dc65d77f88f9 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -4504,11 +4504,6 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull class_decl, ImplScope class_scope; class_scope.AddParent(scope_info.innermost_scope); - if (class_decl->extensibility() == ClassExtensibility::Abstract) { - return ProgramError(class_decl->source_loc()) - << "Class prefix `abstract` is not supported yet"; - } - std::optional> base_class; if (class_decl->base_expr().has_value()) { Nonnull base_class_expr = *class_decl->base_expr(); diff --git a/explorer/testdata/class/abstract_class.carbon b/explorer/testdata/class/abstract_class.carbon new file mode 100644 index 000000000000..a9e9715fedcd --- /dev/null +++ b/explorer/testdata/class/abstract_class.carbon @@ -0,0 +1,27 @@ +// 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 +// RUN: %{explorer-run} +// RUN: %{explorer-run-trace} +// CHECK:STDOUT: d.a=1 +// CHECK:STDOUT: d.b=2 +// CHECK:STDOUT: result: 0 + +package ExplorerTest api; + +abstract class C { + var a: i32; +} + +class D extends C { + var b: i32; +} + +fn Main() -> i32 { + var d: D = { .base = {.a = 1}, .b = 2 }; + Print("d.a={0}", d.a); + Print("d.b={0}", d.b); + return 0; +} diff --git a/explorer/testdata/class/fail_abstract_class_subtyping.carbon b/explorer/testdata/class/fail_abstract_class_subtyping.carbon new file mode 100644 index 000000000000..0789fc136b15 --- /dev/null +++ b/explorer/testdata/class/fail_abstract_class_subtyping.carbon @@ -0,0 +1,25 @@ +// 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 +// RUN: %{not} %{explorer-run} +// RUN: %{not} %{explorer-run-trace} + +package ExplorerTest api; + +abstract class C { + var a: i32; +} + +class D extends C { + var b: i32; +} + +fn Main() -> i32 { + var d: D = { .base = {.a = 1}, .b = 2 }; + // Not supported yet. + // CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_abstract_class_subtyping.carbon:[[@LINE+1]]: type error in name binding: 'class D*' is not implicitly convertible to 'class C*' + var c: C* = &d; + return 0; +} diff --git a/explorer/testdata/class/fail_instantiate_abstract_class_constructor.carbon b/explorer/testdata/class/fail_instantiate_abstract_class_constructor.carbon new file mode 100644 index 000000000000..6c32c5a5559b --- /dev/null +++ b/explorer/testdata/class/fail_instantiate_abstract_class_constructor.carbon @@ -0,0 +1,23 @@ +// 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 +// RUN: %{not} %{explorer-run} +// RUN: %{not} %{explorer-run-trace} + +package ExplorerTest api; + +abstract class C { + // TODO: Returning `Self` for an abstract class should error: `partial Self` should be used instead. This should be updated when `partial` is implemented. + fn Create() -> Self { + return { .a = 1 }; + } + var a: i32; +} + +fn Main() -> i32 { + // CHECK:STDERR: RUNTIME ERROR: {{.*}}/explorer/testdata/class/fail_instantiate_abstract_class_constructor.carbon:[[@LINE+1]]: Cannot instantiate abstract class C + var c: C = C.Create(); + return 0; +} diff --git a/explorer/testdata/class/fail_abstract_class.carbon b/explorer/testdata/class/fail_instantiate_abstract_class_empty.carbon similarity index 66% rename from explorer/testdata/class/fail_abstract_class.carbon rename to explorer/testdata/class/fail_instantiate_abstract_class_empty.carbon index 12e6514608be..4dc20eba4a2e 100644 --- a/explorer/testdata/class/fail_abstract_class.carbon +++ b/explorer/testdata/class/fail_instantiate_abstract_class_empty.carbon @@ -9,10 +9,11 @@ package ExplorerTest api; abstract class C { - fn F() {} -// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_abstract_class.carbon:[[@LINE+1]]: Class prefix `abstract` is not supported yet + var a: i32; } fn Main() -> i32 { + // CHECK:STDERR: RUNTIME ERROR: {{.*}}/explorer/testdata/class/fail_instantiate_abstract_class_empty.carbon:[[@LINE+1]]: Cannot instantiate abstract class C + var c: C; return 0; } diff --git a/explorer/testdata/class/fail_instantiate_abstract_class_struct.carbon b/explorer/testdata/class/fail_instantiate_abstract_class_struct.carbon new file mode 100644 index 000000000000..a8146a38e229 --- /dev/null +++ b/explorer/testdata/class/fail_instantiate_abstract_class_struct.carbon @@ -0,0 +1,19 @@ +// 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 +// RUN: %{not} %{explorer-run} +// RUN: %{not} %{explorer-run-trace} + +package ExplorerTest api; + +abstract class C { + var a: i32; +} + +fn Main() -> i32 { + // CHECK:STDERR: RUNTIME ERROR: {{.*}}/explorer/testdata/class/fail_instantiate_abstract_class_struct.carbon:[[@LINE+1]]: Cannot instantiate abstract class C + var c: C = { .a = 1 }; + return 0; +}