Handle extend impl in function body (#4924)

Crash bug found by fuzzer.

---------

Co-authored-by: Josh L <josh11b@users.noreply.github.com>
This commit is contained in:
josh11b
2025-02-11 21:19:55 +00:00
committed by GitHub
co-authored by Josh L
parent a9c1bc4f0f
commit 50b3c825e4
3 changed files with 126 additions and 67 deletions
+13 -3
View File
@@ -125,6 +125,14 @@ auto HandleParseNode(Context& context, Parse::DefaultSelfImplAsId node_id)
return true;
}
static auto DiagnoseExtendImplOutsideClass(Context& context,
Parse::AnyImplDeclId node_id)
-> void {
CARBON_DIAGNOSTIC(ExtendImplOutsideClass, Error,
"`extend impl` can only be used in a class");
context.emitter().Emit(node_id, ExtendImplOutsideClass);
}
// Process an `extend impl` declaration by extending the impl scope with the
// `impl`'s scope.
static auto ExtendImpl(Context& context, Parse::NodeId extend_node,
@@ -134,13 +142,15 @@ static auto ExtendImpl(Context& context, Parse::NodeId extend_node,
SemIR::InstId constraint_inst_id,
SemIR::TypeId constraint_id) -> void {
auto parent_scope_id = context.decl_name_stack().PeekParentScopeId();
if (!parent_scope_id.has_value()) {
DiagnoseExtendImplOutsideClass(context, node_id);
return;
}
auto& parent_scope = context.name_scopes().Get(parent_scope_id);
// TODO: This is also valid in a mixin.
if (!TryAsClassScope(context, parent_scope_id)) {
CARBON_DIAGNOSTIC(ExtendImplOutsideClass, Error,
"`extend impl` can only be used in a class");
context.emitter().Emit(node_id, ExtendImplOutsideClass);
DiagnoseExtendImplOutsideClass(context, node_id);
return;
}
@@ -1,64 +0,0 @@
// 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
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_extend_impl_scope.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_extend_impl_scope.carbon
interface I {}
// CHECK:STDERR: fail_extend_impl_scope.carbon:[[@LINE+4]]:1: error: `extend impl` can only be used in a class [ExtendImplOutsideClass]
// CHECK:STDERR: extend impl i32 as I {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
extend impl i32 as I {}
// CHECK:STDOUT: --- fail_extend_impl_scope.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %I.type: type = facet_type <@I> [template]
// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [template]
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness () [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [template] {
// CHECK:STDOUT: .Int = %Core.Int
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .I = %I.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%I.type] {} {}
// CHECK:STDOUT: impl_decl @impl [template] {} {
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template = constants.%int_32]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32]
// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%I.type]
// CHECK:STDOUT: }
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness () [template = constants.%impl_witness]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @I {
// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = %Self
// CHECK:STDOUT: witness = ()
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @impl: %i32 as %I.ref {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: witness = file.%impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -0,0 +1,113 @@
// 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
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/no_prelude/fail_extend_impl_scope.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/no_prelude/fail_extend_impl_scope.carbon
// --- fail_extend_impl_file_scope.carbon
library "[[@TEST_NAME]]";
interface I {}
// CHECK:STDERR: fail_extend_impl_file_scope.carbon:[[@LINE+4]]:1: error: `extend impl` can only be used in a class [ExtendImplOutsideClass]
// CHECK:STDERR: extend impl () as I {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
extend impl () as I {}
// --- fail_extend_impl_function_scope.carbon
library "[[@TEST_NAME]]";
interface J {}
fn F() {
// CHECK:STDERR: fail_extend_impl_function_scope.carbon:[[@LINE+4]]:3: error: `extend impl` can only be used in a class [ExtendImplOutsideClass]
// CHECK:STDERR: extend impl {} as J {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
extend impl {} as J {}
}
// CHECK:STDOUT: --- fail_extend_impl_file_scope.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %I.type: type = facet_type <@I> [template]
// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [template]
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness () [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .I = %I.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%I.type] {} {}
// CHECK:STDOUT: impl_decl @impl [template] {} {
// CHECK:STDOUT: %.loc9_14.1: %empty_tuple.type = tuple_literal ()
// CHECK:STDOUT: %.loc9_14.2: type = converted %.loc9_14.1, constants.%empty_tuple.type [template = constants.%empty_tuple.type]
// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%I.type]
// CHECK:STDOUT: }
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness () [template = constants.%impl_witness]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @I {
// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = %Self
// CHECK:STDOUT: witness = ()
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @impl: %.loc9_14.2 as %I.ref {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: witness = file.%impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_extend_impl_function_scope.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %J.type: type = facet_type <@J> [template]
// CHECK:STDOUT: %Self: %J.type = bind_symbolic_name Self, 0 [symbolic]
// CHECK:STDOUT: %F.type: type = fn_type @F [template]
// CHECK:STDOUT: %F: %F.type = struct_value () [template]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template]
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness () [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .J = %J.decl
// CHECK:STDOUT: .F = %F.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %J.decl: type = interface_decl @J [template = constants.%J.type] {} {}
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @J {
// CHECK:STDOUT: %Self: %J.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = %Self
// CHECK:STDOUT: witness = ()
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @impl: %.loc10_16.2 as %J.ref {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: witness = @F.%impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: impl_decl @impl [template] {} {
// CHECK:STDOUT: %.loc10_16.1: %empty_struct_type = struct_literal ()
// CHECK:STDOUT: %.loc10_16.2: type = converted %.loc10_16.1, constants.%empty_struct_type [template = constants.%empty_struct_type]
// CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [template = constants.%J.type]
// CHECK:STDOUT: }
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness () [template = constants.%impl_witness]
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT: