mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Closes #2994 --------- Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user