diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index bf31d3f95949..61f0947987c0 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -10,6 +10,7 @@ #include "common/error.h" #include "common/variant_helpers.h" #include "common/vlog.h" +#include "toolchain/base/kind_switch.h" #include "toolchain/base/pretty_stack_trace_function.h" #include "toolchain/check/context.h" #include "toolchain/check/diagnostic_helpers.h" @@ -656,6 +657,49 @@ auto NodeIdTraversal::Next() -> std::optional { } } +// Emits a diagnostic for each declaration in context.definitions_required() +// that doesn't have a definition. +static auto DiagnoseMissingDefinitions(Context& context, + Context::DiagnosticEmitter& emitter) + -> void { + CARBON_DIAGNOSTIC(MissingDefinitionInImpl, Error, + "No definition found for declaration in impl file"); + for (SemIR::InstId decl_inst_id : context.definitions_required()) { + SemIR::Inst decl_inst = context.insts().Get(decl_inst_id); + CARBON_KIND_SWITCH(context.insts().Get(decl_inst_id)) { + case CARBON_KIND(SemIR::ClassDecl class_decl): { + if (!context.classes().Get(class_decl.class_id).is_defined()) { + emitter.Emit(decl_inst_id, MissingDefinitionInImpl); + } + break; + } + case CARBON_KIND(SemIR::FunctionDecl function_decl): { + if (context.functions().Get(function_decl.function_id).definition_id == + SemIR::InstId::Invalid) { + emitter.Emit(decl_inst_id, MissingDefinitionInImpl); + } + break; + } + case CARBON_KIND(SemIR::ImplDecl impl_decl): { + if (!context.impls().Get(impl_decl.impl_id).is_defined()) { + emitter.Emit(decl_inst_id, MissingDefinitionInImpl); + } + break; + } + case SemIR::InterfaceDecl::Kind: { + // TODO: handle `interface` as well, once we can test it without + // triggering https://github.com/carbon-language/carbon-lang/issues/4071 + CARBON_FATAL() + << "TODO: Support interfaces in DiagnoseMissingDefinitions"; + } + default: { + CARBON_FATAL() << "Unexpected inst in definitions_required: " + << decl_inst; + } + } + } +} + // Loops over all nodes in the tree. On some errors, this may return early, // for example if an unrecoverable state is encountered. // NOLINTNEXTLINE(readability-function-size) @@ -736,6 +780,8 @@ static auto CheckParseTree( context.FinalizeExports(); context.FinalizeGlobalInit(); + DiagnoseMissingDefinitions(context, emitter); + context.VerifyOnFinish(); sem_ir.set_has_errors(unit_info.err_tracker.seen_error()); diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 2523bf894cff..6f6637cb8c34 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -311,6 +311,12 @@ class Context { return check_ir_map_[sem_ir.check_ir_id().index]; } + // True if the current file is an impl file. + auto IsImplFile() -> bool { + return sem_ir_->import_irs().Get(SemIR::ImportIRId::ApiForImpl).sem_ir != + nullptr; + } + // Prints information for a stack dump. auto PrintForStackDump(llvm::raw_ostream& output) const -> void; @@ -406,6 +412,10 @@ class Context { } auto constants() -> SemIR::ConstantStore& { return sem_ir().constants(); } + auto definitions_required() -> llvm::SmallVector& { + return definitions_required_; + } + private: // A FoldingSet node for a type. class TypeNode : public llvm::FastFoldingSetNode { @@ -481,6 +491,10 @@ class Context { // // Inline 0 elements because it's expected to require heap allocation. llvm::SmallVector import_ir_constant_values_; + + // Declaration instructions of entities that should have definitions by the + // end of the current source file. + llvm::SmallVector definitions_required_; }; } // namespace Carbon::Check diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index d7f208bd794e..d28ca630ce33 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -263,6 +263,10 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id, } } + if (!is_definition && context.IsImplFile() && !is_extern) { + context.definitions_required().push_back(class_decl_id); + } + return {class_decl.class_id, class_decl_id}; } diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 6afb86df5eaa..e9e072c4f236 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -303,6 +303,10 @@ static auto BuildFunctionDecl(Context& context, } } + if (!is_definition && context.IsImplFile() && !is_extern) { + context.definitions_required().push_back(function_info.decl_id); + } + return {function_decl.function_id, function_info.decl_id}; } diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index fa1fb1728531..3fb541453871 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -174,7 +174,8 @@ static auto ExtendImpl(Context& context, Parse::NodeId extend_node, // Build an ImplDecl describing the signature of an impl. This handles the // common logic shared by impl forward declarations and impl definitions. -static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id) +static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id, + bool is_definition) -> std::pair { auto [constraint_node, constraint_id] = context.node_stack().PopExprWithNodeId(); @@ -221,18 +222,23 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id) params_node, constraint_type_id); } + if (!is_definition && context.IsImplFile()) { + context.definitions_required().push_back(impl_decl_id); + } + return {impl_decl.impl_id, impl_decl_id}; } auto HandleImplDecl(Context& context, Parse::ImplDeclId node_id) -> bool { - BuildImplDecl(context, node_id); + BuildImplDecl(context, node_id, /*is_definition=*/false); context.decl_name_stack().PopScope(); return true; } auto HandleImplDefinitionStart(Context& context, Parse::ImplDefinitionStartId node_id) -> bool { - auto [impl_id, impl_decl_id] = BuildImplDecl(context, node_id); + auto [impl_id, impl_decl_id] = + BuildImplDecl(context, node_id, /*is_definition=*/true); auto& impl_info = context.impls().Get(impl_id); if (impl_info.is_defined()) { diff --git a/toolchain/check/testdata/class/no_prelude/no_definition_in_impl_file.carbon b/toolchain/check/testdata/class/no_prelude/no_definition_in_impl_file.carbon new file mode 100644 index 000000000000..f9b7fb0e9440 --- /dev/null +++ b/toolchain/check/testdata/class/no_prelude/no_definition_in_impl_file.carbon @@ -0,0 +1,200 @@ +// 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/class/no_prelude/no_definition_in_impl_file.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/no_prelude/no_definition_in_impl_file.carbon + +// --- decl_in_api_definition_in_impl.carbon + +library "decl_in_api_definition_in_impl"; + +class A; + +// --- decl_in_api_definition_in_impl.impl.carbon + +impl library "decl_in_api_definition_in_impl"; + +class A; + +class A {} + +// --- use_decl_in_api.carbon + +library "use_decl_in_api"; + +// --- use_decl_in_api.impl.carbon + +impl library "use_decl_in_api"; + +import library "decl_in_api_definition_in_impl"; + +// --- decl_only_in_api.carbon + +library "decl_only_in_api"; + +class B; + +// --- decl_only_in_api.impl.carbon + +impl library "decl_only_in_api"; + +// --- decl_in_api_decl_in_impl.carbon + +library "decl_in_api_decl_in_impl"; + +class C; + +// --- fail_decl_in_api_decl_in_impl.impl.carbon + +impl library "decl_in_api_decl_in_impl"; + +// CHECK:STDERR: fail_decl_in_api_decl_in_impl.impl.carbon:[[@LINE+4]]:1: ERROR: No definition found for declaration in impl file +// CHECK:STDERR: class C; +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +class C; + +// --- decl_only_in_impl.carbon + +library "decl_only_in_impl"; + +// --- fail_decl_only_in_impl.impl.carbon + +impl library "decl_only_in_impl"; + +// CHECK:STDERR: fail_decl_only_in_impl.impl.carbon:[[@LINE+3]]:1: ERROR: No definition found for declaration in impl file +// CHECK:STDERR: class D; +// CHECK:STDERR: ^~~~~~~~ +class D; + +// CHECK:STDOUT: --- decl_in_api_definition_in_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A: type = class_type @A [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = class_decl @A [template = constants.%A] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @A; +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_in_api_definition_in_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A: type = class_type @A [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %A.decl.loc4 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: type = import_ref ir0, inst+1, loaded [template = constants.%A] +// CHECK:STDOUT: %A.decl.loc4: type = class_decl @A [template = constants.%A] {} +// CHECK:STDOUT: %A.decl.loc6: type = class_decl @A [template = constants.%A] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @A { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%A +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_decl_in_api.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_decl_in_api.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref = import_ref ir1, inst+1, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_only_in_api.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %B: type = class_type @B [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %B.decl: type = class_decl @B [template = constants.%B] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @B; +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_only_in_api.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .B = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref = import_ref ir0, inst+1, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_in_api_decl_in_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_decl_in_api_decl_in_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: type = import_ref ir0, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C; +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_only_in_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_decl_only_in_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %D: type = class_type @D [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .D = %D.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %D.decl: type = class_decl @D [template = constants.%D] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @D; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/declaration/no_prelude/implicit_import.carbon b/toolchain/check/testdata/function/declaration/no_prelude/implicit_import.carbon index b9ba782d8ce0..f4d8de9ed7d1 100644 --- a/toolchain/check/testdata/function/declaration/no_prelude/implicit_import.carbon +++ b/toolchain/check/testdata/function/declaration/no_prelude/implicit_import.carbon @@ -18,7 +18,7 @@ fn A(); impl library "basic"; -fn A(); +fn A() {} // --- extern_api.carbon @@ -30,7 +30,7 @@ extern fn A(); impl library "extern_api"; -// CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn A` in the same library must match use of `extern`. +// CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE+14]]:1: ERROR: Redeclarations of `fn A` in the same library must match use of `extern`. // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE-5]]:6: In import. @@ -40,6 +40,10 @@ impl library "extern_api"; // CHECK:STDERR: extern fn A(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: +// CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE+4]]:1: ERROR: No definition found for declaration in impl file +// CHECK:STDERR: fn A(); +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fn A(); // --- extern_impl.carbon @@ -96,7 +100,10 @@ extern fn A(); // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [template = constants.%A] {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @A(); +// CHECK:STDOUT: fn @A() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- extern_api.carbon // CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/declaration/no_prelude/no_definition_in_impl_file.carbon b/toolchain/check/testdata/function/declaration/no_prelude/no_definition_in_impl_file.carbon new file mode 100644 index 000000000000..89b8be182a27 --- /dev/null +++ b/toolchain/check/testdata/function/declaration/no_prelude/no_definition_in_impl_file.carbon @@ -0,0 +1,211 @@ +// 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/function/declaration/no_prelude/no_definition_in_impl_file.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/no_prelude/no_definition_in_impl_file.carbon + +// --- decl_in_api_definition_in_impl.carbon + +library "decl_in_api_definition_in_impl"; + +fn A(); + +// --- decl_in_api_definition_in_impl.impl.carbon + +impl library "decl_in_api_definition_in_impl"; + +fn A(); + +fn A() {} + +// --- use_decl_in_api.carbon + +library "use_decl_in_api"; + +// --- use_decl_in_api.impl.carbon + +impl library "use_decl_in_api"; + +import library "decl_in_api_definition_in_impl"; + +// --- decl_only_in_api.carbon + +library "decl_only_in_api"; + +fn B(); + +// --- decl_only_in_api.impl.carbon + +impl library "decl_only_in_api"; + +// --- decl_in_api_decl_in_impl.carbon + +library "decl_in_api_decl_in_impl"; + +fn C(); + +// --- fail_decl_in_api_decl_in_impl.impl.carbon + +impl library "decl_in_api_decl_in_impl"; + +// CHECK:STDERR: fail_decl_in_api_decl_in_impl.impl.carbon:[[@LINE+4]]:1: ERROR: No definition found for declaration in impl file +// CHECK:STDERR: fn C(); +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: +fn C(); + +// --- decl_only_in_impl.carbon + +library "decl_only_in_impl"; + +// --- fail_decl_only_in_impl.impl.carbon + +impl library "decl_only_in_impl"; + +// CHECK:STDERR: fail_decl_only_in_impl.impl.carbon:[[@LINE+3]]:1: ERROR: No definition found for declaration in impl file +// CHECK:STDERR: fn D(); +// CHECK:STDERR: ^~~~~~~ +fn D(); + +// CHECK:STDOUT: --- decl_in_api_definition_in_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A.type: type = fn_type @A [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %A: %A.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [template = constants.%A] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_in_api_definition_in_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A.type: type = fn_type @A [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %A: %A.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %A.decl.loc4 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: %A.type = import_ref ir0, inst+1, loaded [template = constants.%A] +// CHECK:STDOUT: %A.decl.loc4: %A.type = fn_decl @A [template = constants.%A] {} +// CHECK:STDOUT: %A.decl.loc6: %A.type = fn_decl @A [template = constants.%A] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_decl_in_api.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_decl_in_api.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref = import_ref ir1, inst+1, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_only_in_api.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %B.type: type = fn_type @B [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %B: %B.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [template = constants.%B] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @B(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_only_in_api.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .B = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref = import_ref ir0, inst+1, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_in_api_decl_in_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C.type: type = fn_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %C: %C.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [template = constants.%C] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @C(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_decl_in_api_decl_in_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C.type: type = fn_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %C: %C.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: %C.type = import_ref ir0, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [template = constants.%C] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @C(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_only_in_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_decl_only_in_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %D.type: type = fn_type @D [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %D: %D.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .D = %D.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %D.decl: %D.type = fn_decl @D [template = constants.%D] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @D(); +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/no_prelude/no_definition_in_impl_file.carbon b/toolchain/check/testdata/impl/no_prelude/no_definition_in_impl_file.carbon new file mode 100644 index 000000000000..746a182c8227 --- /dev/null +++ b/toolchain/check/testdata/impl/no_prelude/no_definition_in_impl_file.carbon @@ -0,0 +1,330 @@ +// 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/no_definition_in_impl_file.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/no_prelude/no_definition_in_impl_file.carbon + +// --- decl_in_api_definition_in_impl.carbon + +library "decl_in_api_definition_in_impl"; + +interface A {}; + +impl () as A; + +// --- decl_in_api_definition_in_impl.impl.carbon + +impl library "decl_in_api_definition_in_impl"; + +impl () as A; + +impl () as A {} + +// --- use_decl_in_api.carbon + +library "use_decl_in_api"; + +// --- use_decl_in_api.impl.carbon + +impl library "use_decl_in_api"; + +import library "decl_in_api_definition_in_impl"; + +// --- decl_only_in_api.carbon + +library "decl_only_in_api"; + +interface B {}; + +impl () as B; + +// --- decl_only_in_api.impl.carbon + +impl library "decl_only_in_api"; + +// --- decl_in_api_decl_in_impl.carbon + +library "decl_in_api_decl_in_impl"; + +interface C {}; + +impl () as C; + +// --- fail_decl_in_api_decl_in_impl.impl.carbon + +impl library "decl_in_api_decl_in_impl"; + +// CHECK:STDERR: fail_decl_in_api_decl_in_impl.impl.carbon:[[@LINE+4]]:1: ERROR: No definition found for declaration in impl file +// CHECK:STDERR: impl () as C; +// CHECK:STDERR: ^~~~~~~~~~~~~ +// CHECK:STDERR: +impl () as C; + +// --- decl_only_in_impl.carbon + +library "decl_only_in_impl"; + +// --- fail_decl_only_in_impl.impl.carbon + +impl library "decl_only_in_impl"; + +interface D {}; + +// CHECK:STDERR: fail_decl_only_in_impl.impl.carbon:[[@LINE+3]]:1: ERROR: No definition found for declaration in impl file +// CHECK:STDERR: impl () as D; +// CHECK:STDERR: ^~~~~~~~~~~~~ +impl () as D; + +// CHECK:STDOUT: --- decl_in_api_definition_in_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @A [template] +// CHECK:STDOUT: %Self: %.1 = bind_symbolic_name Self 0 [symbolic] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = interface_decl @A [template = constants.%.1] {} +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %.loc6_7.1: %.2 = tuple_literal () +// CHECK:STDOUT: %.loc6_7.2: type = converted %.loc6_7.1, constants.%.2 [template = constants.%.2] +// CHECK:STDOUT: %A.ref: type = name_ref A, %A.decl [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @A { +// CHECK:STDOUT: %Self: %.1 = 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: %.2 as %.1; +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_in_api_definition_in_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = interface_type @A [template] +// CHECK:STDOUT: %Self: %.2 = bind_symbolic_name Self 0 [symbolic] +// CHECK:STDOUT: %.3: = interface_witness () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %import_ref.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir0, inst+1, loaded [template = constants.%.2] +// CHECK:STDOUT: %import_ref.2 = import_ref ir0, inst+3, unloaded +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %.loc4_7.1: %.1 = tuple_literal () +// CHECK:STDOUT: %.loc4_7.2: type = converted %.loc4_7.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: %A.ref.loc4: type = name_ref A, %import_ref.1 [template = constants.%.2] +// CHECK:STDOUT: } +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %.loc6_7.1: %.1 = tuple_literal () +// CHECK:STDOUT: %.loc6_7.2: type = converted %.loc6_7.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: %A.ref.loc6: type = name_ref A, %import_ref.1 [template = constants.%.2] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @A { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = file.%import_ref.2 +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: %.1 as %.2 { +// CHECK:STDOUT: %.1: = interface_witness () [template = constants.%.3] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = %.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_decl_in_api.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_decl_in_api.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = interface_type @A [template] +// CHECK:STDOUT: %Self: %.2 = bind_symbolic_name Self 0 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %import_ref.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1 = import_ref ir1, inst+1, unloaded +// CHECK:STDOUT: %import_ref.2 = import_ref ir1, inst+3, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @A { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = file.%import_ref.2 +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: %.1 as %.2; +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_only_in_api.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @B [template] +// CHECK:STDOUT: %Self: %.1 = bind_symbolic_name Self 0 [symbolic] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %B.decl: type = interface_decl @B [template = constants.%.1] {} +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %.loc6_7.1: %.2 = tuple_literal () +// CHECK:STDOUT: %.loc6_7.2: type = converted %.loc6_7.1, constants.%.2 [template = constants.%.2] +// CHECK:STDOUT: %B.ref: type = name_ref B, %B.decl [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @B { +// CHECK:STDOUT: %Self: %.1 = 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: %.2 as %.1; +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_only_in_api.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = interface_type @B [template] +// CHECK:STDOUT: %Self: %.2 = bind_symbolic_name Self 0 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .B = %import_ref.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1 = import_ref ir0, inst+1, unloaded +// CHECK:STDOUT: %import_ref.2 = import_ref ir0, inst+3, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @B { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = file.%import_ref.2 +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: %.1 as %.2; +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_in_api_decl_in_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @C [template] +// CHECK:STDOUT: %Self: %.1 = bind_symbolic_name Self 0 [symbolic] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = interface_decl @C [template = constants.%.1] {} +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %.loc6_7.1: %.2 = tuple_literal () +// CHECK:STDOUT: %.loc6_7.2: type = converted %.loc6_7.1, constants.%.2 [template = constants.%.2] +// CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @C { +// CHECK:STDOUT: %Self: %.1 = 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: %.2 as %.1; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_decl_in_api_decl_in_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = interface_type @C [template] +// CHECK:STDOUT: %Self: %.2 = bind_symbolic_name Self 0 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir0, inst+1, loaded [template = constants.%.2] +// CHECK:STDOUT: %import_ref.2 = import_ref ir0, inst+3, unloaded +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %.loc8_7.1: %.1 = tuple_literal () +// CHECK:STDOUT: %.loc8_7.2: type = converted %.loc8_7.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%.2] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = file.%import_ref.2 +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: %.1 as %.2; +// CHECK:STDOUT: +// CHECK:STDOUT: --- decl_only_in_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_decl_only_in_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @D [template] +// CHECK:STDOUT: %Self: %.1 = bind_symbolic_name Self 0 [symbolic] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .D = %D.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %D.decl: type = interface_decl @D [template = constants.%.1] {} +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %.loc9_7.1: %.2 = tuple_literal () +// CHECK:STDOUT: %.loc9_7.2: type = converted %.loc9_7.1, constants.%.2 [template = constants.%.2] +// CHECK:STDOUT: %D.ref: type = name_ref D, %D.decl [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @D { +// CHECK:STDOUT: %Self: %.1 = 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: %.2 as %.1; +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index ade1b21b005e..7e021ba9584e 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -151,6 +151,7 @@ CARBON_DIAGNOSTIC_KIND(ExplicitImportApi) CARBON_DIAGNOSTIC_KIND(RepeatedImport) CARBON_DIAGNOSTIC_KIND(FirstImported) CARBON_DIAGNOSTIC_KIND(ExportFromImpl) +CARBON_DIAGNOSTIC_KIND(MissingDefinitionInImpl) // Merge-related redeclaration checking. CARBON_DIAGNOSTIC_KIND(RedeclPrevDecl)