mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:20:10 +01:00
[explorer] Add support for abstract virtual methods (#3411)
Completing the work from #2761 by adding additional test cases. Closes #2512 --------- Co-authored-by: Maan2003 <manmeetmann2003@gmail.com> Co-authored-by: Aleksei Ermakov <alexey@emkv.me>
This commit is contained in:
co-authored by
Maan2003
Aleksei Ermakov
parent
9d369db8ea
commit
f1ddded3a1
@@ -5167,16 +5167,27 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
|
||||
}
|
||||
CARBON_CHECK(!fun->name().is_qualified())
|
||||
<< "qualified function name not permitted in class scope";
|
||||
|
||||
if (fun->virt_override() == VirtualOverride::Abstract &&
|
||||
fun->body().has_value()) {
|
||||
return ProgramError(fun->source_loc())
|
||||
<< "Error declaring `" << fun->name() << "`"
|
||||
<< ": abstract method cannot have a body.";
|
||||
}
|
||||
|
||||
bool has_vtable_entry =
|
||||
class_vtable.find(fun->name().inner_name()) != class_vtable.end();
|
||||
// TODO: Implement complete declaration logic from
|
||||
// `/docs/design/classes.md#virtual-methods`.
|
||||
switch (fun->virt_override()) {
|
||||
case VirtualOverride::Abstract:
|
||||
// Not supported yet.
|
||||
return ProgramError(fun->source_loc())
|
||||
<< "Error declaring `" << fun->name() << "`"
|
||||
<< ": `abstract` methods are not yet supported.";
|
||||
if (class_decl->extensibility() != ClassExtensibility::Abstract) {
|
||||
return ProgramError(fun->source_loc())
|
||||
<< "Error declaring `" << fun->name() << "`"
|
||||
<< ": `abstract` methods are allowed only in abstract "
|
||||
"classes.";
|
||||
}
|
||||
break;
|
||||
case VirtualOverride::None:
|
||||
case VirtualOverride::Virtual:
|
||||
if (has_vtable_entry) {
|
||||
@@ -5237,6 +5248,24 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
|
||||
}
|
||||
}
|
||||
|
||||
if (class_decl->extensibility() != ClassExtensibility::Abstract) {
|
||||
auto abstract_method_it = std::find_if(
|
||||
class_vtable.begin(), class_vtable.end(), [](const auto& vt) {
|
||||
const auto* const fun = vt.getValue().first;
|
||||
return fun->is_method() &&
|
||||
fun->virt_override() == VirtualOverride::Abstract;
|
||||
});
|
||||
|
||||
if (abstract_method_it != class_vtable.end()) {
|
||||
auto fun_name = GetName(*abstract_method_it->getValue().first);
|
||||
CARBON_CHECK(fun_name.has_value());
|
||||
return ProgramError(class_decl->source_loc())
|
||||
<< "Error declaring `" << class_decl->name() << "`"
|
||||
<< ": non abstract class should implement abstract method `"
|
||||
<< *fun_name << "`.";
|
||||
}
|
||||
}
|
||||
|
||||
// For class declaration `class MyType(T:! type, U:! AnInterface)`, `Self`
|
||||
// should have the value `MyType(T, U)`.
|
||||
const auto* self_type = arena_->New<NominalClassType>(
|
||||
|
||||
+19
@@ -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: %{explorer-run}
|
||||
// RUN: %{explorer-run-trace}
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
abstract class A {
|
||||
abstract fn Foo[self: Self]();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: result: 0
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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}
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
abstract class A {
|
||||
abstract fn Foo[self: Self]();
|
||||
}
|
||||
|
||||
base class B {
|
||||
extend base: A;
|
||||
impl fn Foo[self: Self]() {
|
||||
Print("B::Foo called!");
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
extend base: A;
|
||||
impl fn Foo[self: Self]() {
|
||||
Print("C::Foo called!");
|
||||
}
|
||||
}
|
||||
|
||||
abstract class D {
|
||||
extend base: A;
|
||||
}
|
||||
|
||||
class E {
|
||||
extend base: D;
|
||||
impl fn Foo[self: Self]() {
|
||||
Print("E::Foo called!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn Main() -> i32 {
|
||||
var b: B = { .base = {} };
|
||||
b.Foo();
|
||||
var c: C = { .base = {} };
|
||||
c.Foo();
|
||||
var e: E = { .base = { .base = {} } };
|
||||
e.Foo();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: B::Foo called!
|
||||
// CHECK:STDOUT: C::Foo called!
|
||||
// CHECK:STDOUT: E::Foo called!
|
||||
// CHECK:STDOUT: result: 0
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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 A {
|
||||
abstract fn Foo[self: Self]();
|
||||
}
|
||||
|
||||
abstract class B {
|
||||
extend base: A;
|
||||
}
|
||||
|
||||
class C {
|
||||
extend base: B;
|
||||
// CHECK:STDERR: COMPILATION ERROR: fail_abstract_method_abstract_class.carbon:[[@LINE+1]]: Error declaring `C`: non abstract class should implement abstract method `Foo`.
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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 A {
|
||||
abstract fn Foo[self: Self]();
|
||||
}
|
||||
|
||||
base class B {
|
||||
extend base: A;
|
||||
// CHECK:STDERR: COMPILATION ERROR: fail_abstract_method_base_class.carbon:[[@LINE+1]]: Error declaring `B`: non abstract class should implement abstract method `Foo`.
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
@@ -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 A {
|
||||
// CHECK:STDERR: COMPILATION ERROR: fail_abstract_method_body.carbon:[[@LINE+1]]: Error declaring `Foo`: abstract method cannot have a body.
|
||||
abstract fn Foo[self: Self]() {}
|
||||
}
|
||||
|
||||
|
||||
fn Main() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
+5
-8
@@ -6,18 +6,15 @@
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class C {
|
||||
abstract class A {
|
||||
abstract fn Foo[self: Self]();
|
||||
}
|
||||
|
||||
class D {
|
||||
extend base: C;
|
||||
abstract fn Foo[self:Self]() -> i32 {
|
||||
return 1;
|
||||
// CHECK:STDERR: COMPILATION ERROR: fail_abstract_method_not_supported.carbon:[[@LINE+1]]: Error declaring `Foo`: `abstract` methods are not yet supported.
|
||||
}
|
||||
class B {
|
||||
extend base: A;
|
||||
// CHECK:STDERR: COMPILATION ERROR: fail_abstract_method_final_impl.carbon:[[@LINE+1]]: Error declaring `B`: non abstract class should implement abstract method `Foo`.
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
let d: D = {};
|
||||
return 0;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
class A {
|
||||
// CHECK:STDERR: COMPILATION ERROR: fail_abstract_method_non_abstract_class.carbon:[[@LINE+1]]: Error declaring `Foo`: `abstract` methods are allowed only in abstract classes.
|
||||
abstract fn Foo[self: Self]();
|
||||
}
|
||||
|
||||
|
||||
fn Main() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user