Temporary restriction that extend base is first in a class to avoid #2994 (#2995)

Closes #2994

---------

Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
josh11b
2023-07-24 18:11:40 +00:00
committed by GitHub
co-authored by Geoff Romer
parent 9edfa9cad3
commit cf2ddc6072
3 changed files with 67 additions and 44 deletions
+44 -43
View File
@@ -5291,57 +5291,58 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
class_decl->set_static_type(&self->static_type());
}
// Find base class declaration, if any. Verify that is before any data member
// declarations, and there is at most one.
// Find base class declaration, if any. Right now, verify that it is first in
// the class. This avoids the problem identified in
// https://github.com/carbon-language/carbon-lang/issues/2994 where the base
// class expression could reference an earlier declaration in the class that
// hasn't been typechecked yet and therefore doesn't have its `static_type`
// set.
// TODO: Verify just that is before any data member declarations, and there is
// at most one, and delay remaining work (type checking, base class
// evaluation, etc.) until the `extend base` declaration is processed in
// order.
std::optional<Nonnull<const NominalClassType*>> base_class;
bool after_data_member = false;
for (Nonnull<Declaration*> m : class_decl->members()) {
switch (m->kind()) {
case DeclarationKind::VariableDeclaration:
case DeclarationKind::MixDeclaration: {
after_data_member = true;
break;
if (!class_decl->members().empty()) {
Nonnull<Declaration*> m = class_decl->members()[0];
if (m->kind() == DeclarationKind::ExtendBaseDeclaration) {
Nonnull<Expression*> base_class_expr =
cast<ExtendBaseDeclaration>(*m).base_class();
CARBON_ASSIGN_OR_RETURN(const auto base_type,
TypeCheckTypeExp(base_class_expr, class_scope));
if (base_type->kind() != Value::Kind::NominalClassType) {
return ProgramError(m->source_loc())
<< "Unsupported base class type for class `"
<< class_decl->name()
<< "`. Only simple classes are currently supported as base "
"class.";
}
case DeclarationKind::ExtendBaseDeclaration: {
if (after_data_member) {
return ProgramError(m->source_loc())
<< "`extend base:` declaration must not be after `var` or "
"`mix` declarations in a class.";
}
CARBON_RETURN_IF_ERROR(ExpectCompleteType(
base_class_expr->source_loc(), "base class declaration", base_type));
base_class = cast<NominalClassType>(base_type);
if (base_class.value()->declaration().extensibility() ==
ClassExtensibility::None) {
return ProgramError(m->source_loc())
<< "Base class `" << base_class.value()->declaration().name()
<< "` is `final` and cannot be inherited. Add the `base` or "
"`abstract` class prefix to `"
<< base_class.value()->declaration().name()
<< "` to allow it to be inherited";
}
class_decl->set_base_type(base_class);
}
for (Nonnull<Declaration*> m : class_decl->members().drop_front()) {
if (m->kind() == DeclarationKind::ExtendBaseDeclaration) {
if (base_class.has_value()) {
return ProgramError(m->source_loc())
<< "At most one `extend base:` declaration in a class.";
}
Nonnull<Expression*> base_class_expr =
cast<ExtendBaseDeclaration>(*m).base_class();
CARBON_ASSIGN_OR_RETURN(const auto base_type,
TypeCheckTypeExp(base_class_expr, class_scope));
if (base_type->kind() != Value::Kind::NominalClassType) {
} else {
return ProgramError(m->source_loc())
<< "Unsupported base class type for class `"
<< class_decl->name()
<< "`. Only simple classes are currently supported as base "
"class.";
<< "`extend base:` declarations after the first declaration "
"in the class are not yet supported";
}
CARBON_RETURN_IF_ERROR(ExpectCompleteType(base_class_expr->source_loc(),
"base class declaration",
base_type));
base_class = cast<NominalClassType>(base_type);
if (base_class.value()->declaration().extensibility() ==
ClassExtensibility::None) {
return ProgramError(m->source_loc())
<< "Base class `" << base_class.value()->declaration().name()
<< "` is `final` and cannot be inherited. Add the `base` or "
"`abstract` class prefix to `"
<< base_class.value()->declaration().name()
<< "` to allow it to be inherited";
}
class_decl->set_base_type(base_class);
break;
}
default:
break;
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ base class C {
class E {
var x: i32;
// CHECK:STDERR: COMPILATION ERROR: fail_extend_after_var.carbon:[[@LINE+1]]: `extend base:` declaration must not be after `var` or `mix` declarations in a class.
// CHECK:STDERR: COMPILATION ERROR: fail_extend_after_var.carbon:[[@LINE+1]]: `extend base:` declarations after the first declaration in the class are not yet supported
extend base: C;
}
+22
View File
@@ -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
package ExplorerTest api;
class E {
base class C {
}
// This should be allowed, but Explorer forbids it until the problem from
// https://github.com/carbon-language/carbon-lang/issues/2994 can be fixed.
// CHECK:STDERR: COMPILATION ERROR: fail_extend_member_class.carbon:[[@LINE+1]]: `extend base:` declarations after the first declaration in the class are not yet supported
extend base: C;
}
fn Main() -> i32 {
return 0;
}