Pārlūkot izejas kodu

C++ interop: Don't crash when looking up names inside an incomplete C++ class/struct/union (#6096)

Only do the lookup when the class is complete.

Before this change we crash in `Sema::LookupQualifiedName()` on
`Declaration context must already be complete!`.
Boaz Brickner 7 mēneši atpakaļ
vecāks
revīzija
868c4b768c

+ 9 - 0
toolchain/check/cpp/import.cpp

@@ -2143,6 +2143,15 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
         builder.Note(loc_id, InCppNameLookup, name_id);
       });
 
+  if (auto class_decl = context.insts().TryGetAs<SemIR::ClassDecl>(
+          context.name_scopes().Get(scope_id).inst_id());
+      class_decl.has_value()) {
+    if (!context.types().IsComplete(
+            context.classes().Get(class_decl->class_id).self_type_id)) {
+      return SemIR::ScopeLookupResult::MakeError();
+    }
+  }
+
   auto lookup = ClangLookupName(context, scope_id, name_id);
   if (!lookup) {
     SemIR::InstId builtin_inst_id =

+ 26 - 0
toolchain/check/testdata/interop/cpp/class/constructor.carbon

@@ -203,6 +203,32 @@ fn F() {
   //@dump-sem-ir-end
 }
 
+// ============================================================================
+// Trying to call a constructor of an incomplete class
+// ============================================================================
+
+// --- incomplete.h
+
+class C;
+
+// --- fail_import_incomplete.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "incomplete.h";
+
+fn F() {
+   // CHECK:STDERR: fail_import_incomplete.carbon:[[@LINE+8]]:4: error: member access into incomplete class `Cpp.C` [QualifiedExprInIncompleteClassScope]
+   // CHECK:STDERR:    Cpp.C.C();
+   // CHECK:STDERR:    ^~~~~~~
+   // CHECK:STDERR: fail_import_incomplete.carbon:[[@LINE-6]]:10: in file included here [InCppInclude]
+   // CHECK:STDERR: ./incomplete.h:2:7: note: class was forward declared here [ClassForwardDeclaredHere]
+   // CHECK:STDERR: class C;
+   // CHECK:STDERR:       ^
+   // CHECK:STDERR:
+   Cpp.C.C();
+}
+
 // CHECK:STDOUT: --- import_default.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {

+ 26 - 0
toolchain/check/testdata/interop/cpp/function/class.carbon

@@ -341,6 +341,32 @@ fn F() {
   //@dump-sem-ir-end
 }
 
+// ============================================================================
+// Incomplete class
+// ============================================================================
+
+// --- incomplete.h
+
+class Incomplete;
+
+// --- fail_import_incomplete.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "incomplete.h";
+
+fn F() {
+  // CHECK:STDERR: fail_import_incomplete.carbon:[[@LINE+8]]:3: error: member access into incomplete class `Cpp.Incomplete` [QualifiedExprInIncompleteClassScope]
+  // CHECK:STDERR:   Cpp.Incomplete.foo();
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_import_incomplete.carbon:[[@LINE-6]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./incomplete.h:2:7: note: class was forward declared here [ClassForwardDeclaredHere]
+  // CHECK:STDERR: class Incomplete;
+  // CHECK:STDERR:       ^
+  // CHECK:STDERR:
+  Cpp.Incomplete.foo();
+}
+
 // ============================================================================
 // Pointer to forward-declared class as parameter type
 // ============================================================================

+ 26 - 0
toolchain/check/testdata/interop/cpp/function/struct.carbon

@@ -339,6 +339,32 @@ fn F() {
   //@dump-sem-ir-end
 }
 
+// ============================================================================
+// Incomplete struct
+// ============================================================================
+
+// --- incomplete.h
+
+struct Incomplete;
+
+// --- fail_import_incomplete.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "incomplete.h";
+
+fn F() {
+  // CHECK:STDERR: fail_import_incomplete.carbon:[[@LINE+8]]:3: error: member access into incomplete class `Cpp.Incomplete` [QualifiedExprInIncompleteClassScope]
+  // CHECK:STDERR:   Cpp.Incomplete.foo();
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_import_incomplete.carbon:[[@LINE-6]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./incomplete.h:2:8: note: class was forward declared here [ClassForwardDeclaredHere]
+  // CHECK:STDERR: struct Incomplete;
+  // CHECK:STDERR:        ^
+  // CHECK:STDERR:
+  Cpp.Incomplete.foo();
+}
+
 // ============================================================================
 // Pointer to forward-declared struct as parameter type
 // ============================================================================

+ 26 - 0
toolchain/check/testdata/interop/cpp/function/union.carbon

@@ -302,6 +302,32 @@ fn F() {
   //@dump-sem-ir-end
 }
 
+// ============================================================================
+// Incomplete union
+// ============================================================================
+
+// --- incomplete.h
+
+union Incomplete;
+
+// --- fail_import_incomplete.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "incomplete.h";
+
+fn F() {
+  // CHECK:STDERR: fail_import_incomplete.carbon:[[@LINE+8]]:3: error: member access into incomplete class `Cpp.Incomplete` [QualifiedExprInIncompleteClassScope]
+  // CHECK:STDERR:   Cpp.Incomplete.foo();
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_import_incomplete.carbon:[[@LINE-6]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./incomplete.h:2:7: note: class was forward declared here [ClassForwardDeclaredHere]
+  // CHECK:STDERR: union Incomplete;
+  // CHECK:STDERR:       ^
+  // CHECK:STDERR:
+  Cpp.Incomplete.foo();
+}
+
 // ============================================================================
 // Pointer to forward-declared union as parameter type
 // ============================================================================