Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,14 @@ static auto ResolveSpecificDeclForInst(EvalContext& eval_context,
for (const auto& interface : info.self_impls_constraints) {
ResolveSpecificDeclForSpecificId(eval_context, interface.specific_id);
}
for (const auto& constraint : info.extend_named_constraints) {
ResolveSpecificDeclForSpecificId(eval_context,
constraint.specific_id);
}
for (const auto& constraint : info.self_impls_named_constraints) {
ResolveSpecificDeclForSpecificId(eval_context,
constraint.specific_id);
}
break;
}
case CARBON_KIND(SemIR::SpecificId specific_id): {
Expand Down
26 changes: 12 additions & 14 deletions toolchain/check/facet_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,20 +55,15 @@ static auto WitnessQueryMatchesInterface(
static auto IncompleteFacetTypeDiagnosticBuilder(
Context& context, SemIR::LocId loc_id, SemIR::TypeInstId facet_type_inst_id,
bool is_definition) -> DiagnosticBuilder {
if (is_definition) {
CARBON_DIAGNOSTIC(ImplAsIncompleteFacetTypeDefinition, Error,
"definition of impl as incomplete facet type {0}",
InstIdAsType);
return context.emitter().Build(loc_id, ImplAsIncompleteFacetTypeDefinition,
facet_type_inst_id);
} else {
CARBON_DIAGNOSTIC(
ImplAsIncompleteFacetTypeRewrites, Error,
"declaration of impl as incomplete facet type {0} with rewrites",
InstIdAsType);
return context.emitter().Build(loc_id, ImplAsIncompleteFacetTypeRewrites,
facet_type_inst_id);
}
// TODO: Remove this parameter. Facet types don't need to be complete for in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// TODO: Remove this parameter. Facet types don't need to be complete for in
// TODO: Remove this parameter. Facet types don't need to be complete for impl

// declarations, unless there's a rewrite into `.Self`. But that completeness
// is checked/required by the member access of the rewrite.
CARBON_CHECK(is_definition);
CARBON_DIAGNOSTIC(ImplAsIncompleteFacetTypeDefinition, Error,
"definition of impl as incomplete facet type {0}",
InstIdAsType);
return context.emitter().Build(loc_id, ImplAsIncompleteFacetTypeDefinition,
facet_type_inst_id);
}

auto GetImplWitnessAccessWithoutSubstitution(Context& context,
Expand Down Expand Up @@ -105,6 +100,9 @@ auto InitialFacetTypeImplWitness(
.specific_id = self_specific_id});
}

// The presence of any rewrite constraints requires that we know how many
// entries to allocate in the witness table, which requires the entire facet
// type to be complete, even if this was a declaration.
if (!RequireCompleteType(
context, facet_type_id, SemIR::LocId(facet_type_inst_id), [&] {
return IncompleteFacetTypeDiagnosticBuilder(
Expand Down
38 changes: 31 additions & 7 deletions toolchain/check/handle_require.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ static auto TypeStructureReferencesSelf(

struct ValidateRequireResult {
SemIR::FacetType facet_type;
SemIR::TypeId facet_type_type_id;
const SemIR::IdentifiedFacetType* identified;
};

Expand All @@ -164,12 +165,14 @@ static auto ValidateRequire(Context& context, SemIR::LocId loc_id,
SemIR::InstId constraint_inst_id,
SemIR::InstId scope_inst_id)
-> std::optional<ValidateRequireResult> {
auto constraint_constant_value_inst_id =
context.constant_values().GetConstantInstId(constraint_inst_id);
auto constraint_facet_type = context.insts().TryGetAs<SemIR::FacetType>(
constraint_constant_value_inst_id);
auto constraint_constant_value_id =
context.constant_values().Get(constraint_inst_id);
auto constraint_type_id =
SemIR::TypeId::ForTypeConstant(constraint_constant_value_id);
auto constraint_facet_type =
context.types().TryGetAs<SemIR::FacetType>(constraint_type_id);
if (!constraint_facet_type) {
if (constraint_constant_value_inst_id != SemIR::ErrorInst::InstId) {
if (constraint_constant_value_id != SemIR::ErrorInst::ConstantId) {
CARBON_DIAGNOSTIC(
RequireImplsMissingFacetType, Error,
"`require` declaration constrained by a non-facet type; "
Expand Down Expand Up @@ -218,6 +221,7 @@ static auto ValidateRequire(Context& context, SemIR::LocId loc_id,
}

return ValidateRequireResult{.facet_type = *constraint_facet_type,
.facet_type_type_id = constraint_type_id,
.identified = &identified};
}

Expand All @@ -244,7 +248,7 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
return true;
}

auto [constraint_facet_type, identified] = *validated;
auto [constraint_facet_type, constraint_type_id, identified] = *validated;
if (identified->required_interfaces().empty()) {
// A `require T impls type` adds no actual constraints, so nothing to do.
DiscardGenericDecl(context);
Expand All @@ -271,8 +275,28 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
require_impls_decl.require_impls_id = require_impls_id;
ReplaceInstBeforeConstantUse(context, decl_id, require_impls_decl);

context.require_impls_stack().AppendToTop(require_impls_id);
// We look for a complete type after BuildGenericDecl, so that the resulting
// RequireCompleteType instruction is part of the enclosing interface or named
// constraint generic definition. Then requiring enclosing entity to be
// complete will resolve that definition (via ResolveSpecificDefinition()) and
// also construct a specific for the `constraint_inst_id`, finding any
// monomorphization errors that result.
if (extend) {
if (!RequireCompleteType(
context, constraint_type_id, SemIR::LocId(constraint_inst_id), [&] {
CARBON_DIAGNOSTIC(RequireImplsIncompleteFacetType, Error,
"`extend require` of incomplete facet type {0}",
InstIdAsType);
return context.emitter().Build(constraint_inst_id,
RequireImplsIncompleteFacetType,
constraint_inst_id);
})) {
// DiscardGenericDecl(context);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// DiscardGenericDecl(context);

return true;
}
}

context.require_impls_stack().AppendToTop(require_impls_id);
return true;
}

Expand Down
2 changes: 1 addition & 1 deletion toolchain/check/interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ static auto GetGenericArgsWithSelfType(Context& context,
arg_ids.reserve(std::max(reserve_args_size, interface_args.size() + 1));

// Start with the enclosing arguments from the interface.
arg_ids.assign(interface_args.begin(), interface_args.end());
llvm::append_range(arg_ids, interface_args);

// Add the `Self` argument.
arg_ids.push_back(GetSelfFacet(context, interface_specific_id, generic_id,
Expand Down
93 changes: 0 additions & 93 deletions toolchain/check/testdata/facet/fail_incomplete.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -10,99 +10,6 @@
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/fail_incomplete.carbon

// --- fail_incomplete_interface.carbon
library "[[@TEST_NAME]]";

interface X;
class C {}

// Requires X identified.
impl C as X;

// Requires X complete.
// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `X` [ImplAsIncompleteFacetTypeDefinition]
// CHECK:STDERR: impl C as X {}
// CHECK:STDERR: ^~~~~~~~~~~~~
// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE-10]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere]
// CHECK:STDERR: interface X;
// CHECK:STDERR: ^~~~~~~~~~~~
// CHECK:STDERR:
impl C as X {}

// --- fail_incomplete_interface_without_forward_decl.carbon
library "[[@TEST_NAME]]";

interface X;
class C {}

// Requires X complete.
// CHECK:STDERR: fail_incomplete_interface_without_forward_decl.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `X` [ImplAsIncompleteFacetTypeDefinition]
// CHECK:STDERR: impl C as X {}
// CHECK:STDERR: ^~~~~~~~~~~~~
// CHECK:STDERR: fail_incomplete_interface_without_forward_decl.carbon:[[@LINE-7]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere]
// CHECK:STDERR: interface X;
// CHECK:STDERR: ^~~~~~~~~~~~
// CHECK:STDERR:
impl C as X {}

// --- fail_unidentified_constraint.carbon
library "[[@TEST_NAME]]";

constraint X;
class C {}

// Requires X identified.
// CHECK:STDERR: fail_unidentified_constraint.carbon:[[@LINE+7]]:1: error: facet type `X` cannot be identified in `impl as` [ImplOfUnidentifiedFacetType]
// CHECK:STDERR: impl C as X;
// CHECK:STDERR: ^~~~~~~~~~~~
// CHECK:STDERR: fail_unidentified_constraint.carbon:[[@LINE-7]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
// CHECK:STDERR: constraint X;
// CHECK:STDERR: ^~~~~~~~~~~~~
// CHECK:STDERR:
impl C as X;

// --- nested_require_incomplete_interface.carbon
library "[[@TEST_NAME]]";

interface Z;
constraint Y {
require impls Z;
}
interface X {
require impls Y;
}

class C {}

// Requires X identified.
impl C as X;

// Requires X complete.
impl C as X {}

// --- fail_incomplete_through_constraint.carbon
library "[[@TEST_NAME]]";

interface Z;
constraint Y {
extend require impls Z;
}

class C {}

// Requires Y identified.
impl C as Y;

// Requires Y complete.
// CHECK:STDERR: fail_incomplete_through_constraint.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `Y` [ImplAsIncompleteFacetTypeDefinition]
// CHECK:STDERR: impl C as Y {}
// CHECK:STDERR: ^~~~~~~~~~~~~
// CHECK:STDERR: fail_incomplete_through_constraint.carbon:[[@LINE-14]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere]
// CHECK:STDERR: interface Z;
// CHECK:STDERR: ^~~~~~~~~~~~
// CHECK:STDERR:
impl C as Y {}

// --- fail_impl_lookup_incomplete.carbon
library "[[@TEST_NAME]]";

Expand Down
139 changes: 139 additions & 0 deletions toolchain/check/testdata/generic/extend_type_completion.carbon
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// 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/int.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/extend_type_completion.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/extend_type_completion.carbon

// --- class_impl_doesnt_need_complete_interface.carbon
library "[[@TEST_NAME]]";

interface K(T:! type) {}

class C(N:! i32) {
impl as K(array(i32, N)) {}
}

// C does not extend K so the type of K is not completed. No error.
var v: C(-1);

// --- fail_class_extend_impl_does_need_complete_interface.carbon
library "[[@TEST_NAME]]";

interface K(T:! type) {}

class C(N:! i32) {
// CHECK:STDERR: fail_class_extend_impl_does_need_complete_interface.carbon:[[@LINE+3]]:18: error: array bound of -1 is negative [ArrayBoundNegative]
// CHECK:STDERR: extend impl as K(array(i32, N)) {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
extend impl as K(array(i32, N)) {}
}

// C extends K so the type of K is completed, but is invalid.
// CHECK:STDERR: fail_class_extend_impl_does_need_complete_interface.carbon:[[@LINE+4]]:8: note: in `C(-1)` used here [ResolvingSpecificHere]
// CHECK:STDERR: var v: C(-1);
// CHECK:STDERR: ^~~~~
// CHECK:STDERR:
var v: C(-1);

// --- interface_require_impls_doesnt_need_complete_interface.carbon
library "[[@TEST_NAME]]";

interface K(T:! type) {}
interface J(N:! i32) {
require impls K(array(i32, N));
}

// J does not extend K so the type of K is not completed. No error.
var v: J(-1);

// --- fail_interface_extend_require_impls_does_need_complete_interface.carbon
library "[[@TEST_NAME]]";

interface K(T:! type) {}
interface J(N:! i32) {
// CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: error: array bound of -1 is negative [ArrayBoundNegative]
// CHECK:STDERR: extend require impls K(array(i32, N));
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
extend require impls K(array(i32, N));
}
interface I(N:! i32) {
// CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: note: in `J(-1)` used here [ResolvingSpecificHere]
// CHECK:STDERR: extend require impls J(N);
// CHECK:STDERR: ^~~~
extend require impls J(N);
}

// I extends J extends K so the type of K is completed, but is invalid.
//
// TODO: The error location should be the type, like in the class case above. We
// need a location for the type in context.bind_name_map() to use as the
// location to Convert().
//
// CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+4]]:1: note: in `I(-1)` used here [ResolvingSpecificHere]
// CHECK:STDERR: var v: I(-1);
// CHECK:STDERR: ^~~~~~~~~~~~
// CHECK:STDERR:
var v: I(-1);

// --- constraint_require_impls_doesnt_need_complete_interface.carbon
library "[[@TEST_NAME]]";

interface K(T:! type) {}
constraint J(N:! i32) {
require impls K(array(i32, N));
}

// J does not extend K so the type of K is not completed. No error.
var v: J(-1);

// --- fail_constraint_extend_require_impls_does_need_complete_interface.carbon
library "[[@TEST_NAME]]";

interface K(T:! type) {}
constraint J(N:! i32) {
// CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: error: array bound of -1 is negative [ArrayBoundNegative]
// CHECK:STDERR: extend require impls K(array(i32, N));
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
extend require impls K(array(i32, N));
}
constraint I(N:! i32) {
// CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: note: in `{}` used here [ResolvingSpecificHere]
// CHECK:STDERR: extend require impls J(N);
// CHECK:STDERR: ^~~~
extend require impls J(N);
}

// I extends J extends K so the type of K is completed, but is invalid.
// CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+4]]:1: note: in `{}` used here [ResolvingSpecificHere]
// CHECK:STDERR: var v: I(-1);
// CHECK:STDERR: ^~~~~~~~~~~~
// CHECK:STDERR:
var v: I(-1);

// --- interface_require_impls_doesnt_need_complete_self.carbon
library "[[@TEST_NAME]]";

interface K {}
interface J(N:! i32) {
require array(Self, N) impls K;
}

// J does not extend K so the type of Self impling K is not completed. No error.
var v: J(-1);

// --- constraint_require_impls_doesnt_need_complete_self.carbon
library "[[@TEST_NAME]]";

interface K {}
constraint J(N:! i32) {
require array(Self, N) impls K;
}

// J does not extend K so the type of Self impling K is not completed. No error.
var v: J(-1);
Loading
Loading