Просмотр исходного кода

Fix crash when exporting a poisoned name (#5074)

Also document the constraint on `prev_inst_id`

Crash was fuzzer-found.
Jon Ross-Perkins 1 год назад
Родитель
Сommit
79a86074b1

+ 2 - 1
toolchain/check/decl_name_stack.h

@@ -116,7 +116,8 @@ class DeclNameStack {
       };
     }
 
-    // Returns any name collision found, or `None`.
+    // Returns any name collision found, or `None`. Requires a non-poisoned
+    // value.
     auto prev_inst_id() -> SemIR::InstId;
 
     // Returns the name_id for a new instruction. This is `None` when the name

+ 6 - 1
toolchain/check/handle_export.cpp

@@ -37,7 +37,12 @@ auto HandleParseNode(Context& context, Parse::ExportDeclId node_id) -> bool {
     return true;
   }
 
-  auto inst_id = name_context.prev_inst_id();
+  // Exporting uses the decl name primarily for lookup, so treat poisoning the
+  // same as "not found".
+  auto inst_id =
+      name_context.state == DeclNameStack::NameContext::State::Poisoned
+          ? SemIR::InstId::None
+          : name_context.prev_inst_id();
   if (!inst_id.has_value()) {
     DiagnoseNameNotFound(context, node_id, name_context.name_id_for_new_inst());
     return true;

+ 39 - 0
toolchain/check/testdata/packages/no_prelude/export_import.carbon

@@ -171,6 +171,21 @@ import library "use_non_export_then_base";
 
 var indirect_c: C = {.x = ()};
 
+// --- fail_export_poisoned.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_export_poisoned.carbon:[[@LINE+4]]:11: error: name `Poison` not found [NameNotFound]
+// CHECK:STDERR: fn F() -> Poison;
+// CHECK:STDERR:           ^~~~~~
+// CHECK:STDERR:
+fn F() -> Poison;
+// CHECK:STDERR: fail_export_poisoned.carbon:[[@LINE+4]]:1: error: name `Poison` not found [NameNotFound]
+// CHECK:STDERR: export Poison;
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR:
+export Poison;
+
 // CHECK:STDOUT: --- base.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -832,3 +847,27 @@ var indirect_c: C = {.x = ()};
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_export_poisoned.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Poison = <poisoned>
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
+// CHECK:STDOUT:     %return.patt: <error> = return_slot_pattern
+// CHECK:STDOUT:     %return.param_patt: <error> = out_param_pattern %return.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %Poison.ref: <error> = name_ref Poison, <error> [concrete = <error>]
+// CHECK:STDOUT:     %return.param: ref <error> = out_param call_param0
+// CHECK:STDOUT:     %return: ref <error> = return_slot %return.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> <error>;
+// CHECK:STDOUT: