Files
carbon-lang/toolchain/check/testdata/impl/orphan.carbon
T
Dana Jansens 057ef0d458 Orphan rule for scopes (#7140)
Update the orphan rule to require a name to be defined within, or by,
the same scope as the impl declaration. Since libraries can not be
nested, this also enforces the old rule, but rejects more impl
declarations. In particular, it rejects an impl declaration in a generic
context which would have no way to provide a value for the generic
bindings it inherited from its enclosing scope, making the impl
unusable.

Discussed in open discussion
[2026-05-04](https://docs.google.com/document/d/1mjllGO3ZCL4qGt9uJHUtcxKoHAGEY7Y999ie4EtBWB8/edit?tab=t.3ifnhz83n73d#heading=h.p45zfugbmdih).
2026-06-02 22:33:15 +00:00

427 lines
11 KiB
Plaintext

// 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
//
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/orphan.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/orphan.carbon
// --- imports.carbon
library "[[@TEST_NAME]]";
class ImportedC {}
class ImportedD(T:! type) {}
interface ImportedY {}
interface ImportedZ(T:! type) {}
constraint ImportedN(T:! type) {}
class ImportedAnyParam[T:! type](X:! T) {}
// --- fail_todo_class_definition_missing.carbon
library "[[@TEST_NAME]]";
import library "imports";
// This is the first owning decl of C, so it can be an anchor name.
class C;
// TODO: This should be accepted.
// CHECK:STDERR: fail_todo_class_definition_missing.carbon:[[@LINE+4]]:1: error: orphan `impl` found; something in the self-type or constraint must be defined in the same file [ImplIsOrphan]
// CHECK:STDERR: impl C as ImportedY {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
impl C as ImportedY {}
// --- fail_todo_class_definition_missing_for_self_specific.carbon
library "[[@TEST_NAME]]";
import library "imports";
// This is the first owning decl of C, so it can be an anchor name.
class C;
// TODO: This should be accepted.
// CHECK:STDERR: fail_todo_class_definition_missing_for_self_specific.carbon:[[@LINE+4]]:1: error: orphan `impl` found; something in the self-type or constraint must be defined in the same file [ImplIsOrphan]
// CHECK:STDERR: impl ImportedD(C) as ImportedY {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
impl ImportedD(C) as ImportedY {}
// --- fail_todo_class_definition_missing_for_interface_specific.carbon
library "[[@TEST_NAME]]";
import library "imports";
// This is the first owning decl of C, so it can be an anchor name.
class C;
// TODO: This should be accepted.
// CHECK:STDERR: fail_todo_class_definition_missing_for_interface_specific.carbon:[[@LINE+4]]:1: error: orphan `impl` found; something in the self-type or constraint must be defined in the same file [ImplIsOrphan]
// CHECK:STDERR: impl ImportedC as ImportedZ(C) {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
impl ImportedC as ImportedZ(C) {}
// --- fail_imported_class.carbon
library "[[@TEST_NAME]]";
import library "imports";
// CHECK:STDERR: fail_imported_class.carbon:[[@LINE+4]]:1: error: orphan `impl` found; something in the self-type or constraint must be defined in the same file [ImplIsOrphan]
// CHECK:STDERR: impl ImportedC as ImportedY {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
impl ImportedC as ImportedY {}
// --- fail_imported_class_for_self_specific.carbon
library "[[@TEST_NAME]]";
import library "imports";
// CHECK:STDERR: fail_imported_class_for_self_specific.carbon:[[@LINE+4]]:1: error: orphan `impl` found; something in the self-type or constraint must be defined in the same file [ImplIsOrphan]
// CHECK:STDERR: impl ImportedD(ImportedC) as ImportedY {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
impl ImportedD(ImportedC) as ImportedY {}
// --- fail_imported_class_for_interface_specific.carbon
library "[[@TEST_NAME]]";
import library "imports";
// CHECK:STDERR: fail_imported_class_for_interface_specific.carbon:[[@LINE+4]]:1: error: orphan `impl` found; something in the self-type or constraint must be defined in the same file [ImplIsOrphan]
// CHECK:STDERR: impl ImportedC as ImportedZ(ImportedC) {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
impl ImportedC as ImportedZ(ImportedC) {}
// --- class_definition_after.carbon
library "[[@TEST_NAME]]";
import library "imports";
class C;
impl C as ImportedY {}
class C {}
// --- class_definition_after_for_self_specific.carbon
library "[[@TEST_NAME]]";
import library "imports";
class C;
impl ImportedD(C) as ImportedY {}
class C {}
// --- class_definition_after_for_interface_specific.carbon
library "[[@TEST_NAME]]";
import library "imports";
class C;
impl ImportedC as ImportedZ(C) {}
class C {}
// --- class_definition_before.carbon
library "[[@TEST_NAME]]";
import library "imports";
class C {}
impl C as ImportedY {}
// --- class_definition_before_for_self_specific.carbon
library "[[@TEST_NAME]]";
import library "imports";
class C {}
impl ImportedD(C) as ImportedY {}
// --- class_definition_before_for_interface_specific.carbon
library "[[@TEST_NAME]]";
import library "imports";
class C {}
impl ImportedC as ImportedZ(C) {}
// --- fail_generic_class_from_import.carbon
library "[[@TEST_NAME]]";
import library "imports";
// CHECK:STDERR: fail_generic_class_from_import.carbon:[[@LINE+4]]:1: error: orphan `impl` found; something in the self-type or constraint must be defined in the same file [ImplIsOrphan]
// CHECK:STDERR: impl ImportedAnyParam(ImportedD) as ImportedY {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
impl ImportedAnyParam(ImportedD) as ImportedY {}
// --- generic_class.carbon
library "[[@TEST_NAME]]";
import library "imports";
class D(T:! type) {}
impl ImportedAnyParam(D) as ImportedY {}
// --- fail_generic_interface_from_import.carbon
library "[[@TEST_NAME]]";
import library "imports";
// CHECK:STDERR: fail_generic_interface_from_import.carbon:[[@LINE+4]]:1: error: orphan `impl` found; something in the self-type or constraint must be defined in the same file [ImplIsOrphan]
// CHECK:STDERR: impl ImportedAnyParam(ImportedZ) as ImportedY {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
impl ImportedAnyParam(ImportedZ) as ImportedY {}
// --- generic_interface.carbon
library "[[@TEST_NAME]]";
import library "imports";
interface Z(T:! type) {}
impl ImportedAnyParam(Z) as ImportedY {}
// --- fail_generic_named_constraint_from_import.carbon
library "[[@TEST_NAME]]";
import library "imports";
// CHECK:STDERR: fail_generic_named_constraint_from_import.carbon:[[@LINE+4]]:1: error: orphan `impl` found; something in the self-type or constraint must be defined in the same file [ImplIsOrphan]
// CHECK:STDERR: impl ImportedAnyParam(ImportedN) as ImportedY {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
impl ImportedAnyParam(ImportedN) as ImportedY {}
// --- generic_named_constraint.carbon
library "[[@TEST_NAME]]";
import library "imports";
constraint N(T:! type) {}
impl ImportedAnyParam(N) as ImportedY {}
// --- todo_fail_orphan_in_class_scope.carbon
library "[[@TEST_NAME]]";
interface Z {}
// Neither {} nor Z are defined within or by the scope of `C`.
class C {
impl {} as Z {}
}
// --- valid_in_fn_scope.carbon
library "[[@TEST_NAME]]";
interface Z {}
fn F() {
class C {}
impl C as Z {}
}
// --- todo_fail_orphan_in_fn_scope.carbon
library "[[@TEST_NAME]]";
interface Z {}
// Neither {} nor Z are defined within or by the scope of `F`.
fn F() {
impl {} as Z {}
}
// --- valid_in_generic_fn_scope.carbon
library "[[@TEST_NAME]]";
interface Z {}
fn F(unused T:! type) {
class C {}
impl C as Z {}
}
// --- fail_todo_orphan_in_generic_fn_scope.carbon
library "[[@TEST_NAME]]";
interface Z {}
// Neither {} nor Z are defined within or by the scope of `F`.
fn F(unused T:! type) {
// TODO: The error should be about being an orphan, not an unused binding.
// CHECK:STDERR: fail_todo_orphan_in_generic_fn_scope.carbon:[[@LINE+4]]:3: error: `impl` with unused generic binding [ImplUnusedBinding]
// CHECK:STDERR: impl {} as Z {}
// CHECK:STDERR: ^~~~~~~~~~~~~~
// CHECK:STDERR:
impl {} as Z {}
}
// --- valid_in_block_scope.carbon
library "[[@TEST_NAME]]";
interface Z {}
fn F() {
if (true) {
class C {}
impl C as Z {}
}
}
// --- todo_fail_orphan_in_block_scope.carbon
library "[[@TEST_NAME]]";
interface Z {}
fn F() {
class C {}
// Neither C nor Z are defined within or by this if block scope.
if (true) {
impl C as Z {}
}
}
// --- name_defined_by_scope.carbon
library "[[@TEST_NAME]]";
import library "imports";
class C {
impl as ImportedY {}
};
// --- name_defined_in_scope.carbon
library "[[@TEST_NAME]]";
import library "imports";
fn F() {
class C {}
impl C as ImportedY {}
};
// --- name_defined_after_in_scope.carbon
library "[[@TEST_NAME]]";
import library "imports";
fn F() {
class C;
impl C as ImportedY {}
class C {}
};
// --- name_defined_in_nested_namespace_scope.carbon
library "[[@TEST_NAME]]";
import library "imports";
namespace N;
class N.C {}
impl N.C as ImportedY {}
// --- name_defined_after_in_nested_namespace_scope.carbon
library "[[@TEST_NAME]]";
import library "imports";
namespace N;
class N.C;
impl N.C as ImportedY {}
class N.C {}
// --- name_defined_in_nested_class_scope.carbon
library "[[@TEST_NAME]]";
import library "imports";
class C {
class D {}
}
impl C.D as ImportedY {}
// --- name_defined_after_in_nested_class_scope.carbon
library "[[@TEST_NAME]]";
import library "imports";
class C {
class D;
}
impl C.D as ImportedY {}
class C.D {}
// --- fail_todo_extern_class.carbon
library "[[@TEST_NAME]]";
import library "use_extern_class";
// CHECK:STDERR: fail_todo_extern_class.carbon:[[@LINE+4]]:1: error: redeclaration of `class C` is redundant [RedeclRedundant]
// CHECK:STDERR: extern class C;
// CHECK:STDERR: ^~~~~~~~~~~~~~~
// CHECK:STDERR: fail_todo_extern_class.carbon:[[@LINE-5]]:1: in import [InImport]
extern class C;
// --- fail_use_extern_class.carbon
library "[[@TEST_NAME]]";
import library "imports";
// A non-owning decl of C.
// CHECK:STDERR: fail_use_extern_class.carbon:[[@LINE+8]]:1: note: previously declared here [RedeclPrevDecl]
// CHECK:STDERR: extern library "extern_class" class C;
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_use_extern_class.carbon:[[@LINE+4]]:1: error: semantics TODO: `extern library` [SemanticsTodo]
// CHECK:STDERR: extern library "extern_class" class C;
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
extern library "extern_class" class C;
// CHECK:STDERR: fail_use_extern_class.carbon:[[@LINE+4]]:1: error: orphan `impl` found; something in the self-type or constraint must be defined in the same file [ImplIsOrphan]
// CHECK:STDERR: impl C as ImportedY {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
impl C as ImportedY {}
// --- imported_first_owning_decl.carbon
library "[[@TEST_NAME]]";
class ImportedClassDecl;
interface ImportedInterface {}
// --- todo_fail_imported_first_owning_decl.impl.carbon
impl library "[[@TEST_NAME]]";
// This is an owning decl, but not the first owning decl, which is in the api
// file. So it can not be an anchor name.
class ImportedClassDecl {}
// TODO: This should fail, there is no anchor name.
impl ImportedClassDecl as ImportedInterface {}