[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:
Aneesh Durg
2023-11-21 01:32:00 +00:00
committed by GitHub
co-authored by Maan2003 Aleksei Ermakov
parent 9d369db8ea
commit f1ddded3a1
8 changed files with 197 additions and 12 deletions
+33 -4
View File
@@ -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
View File
@@ -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;
}
@@ -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;
}