瀏覽代碼

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

Closes #2994

---------

Co-authored-by: Geoff Romer <gromer@google.com>
josh11b 2 年之前
父節點
當前提交
cf2ddc6072

+ 44 - 43
explorer/interpreter/type_checker.cpp

@@ -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) {
-          return ProgramError(m->source_loc())
-                 << "Unsupported base class type for class `"
-                 << class_decl->name()
-                 << "`. Only simple classes are currently supported as base "
-                    "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) {
+        } else {
           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";
+                 << "`extend base:` declarations after the first declaration "
+                    "in the class are not yet supported";
         }
-        class_decl->set_base_type(base_class);
-        break;
       }
-      default:
-        break;
     }
   }
 

+ 1 - 1
explorer/testdata/class/fail_extend_after_var.carbon

@@ -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 - 0
explorer/testdata/class/fail_extend_member_class.carbon

@@ -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;
+}