diff --git a/toolchain/check/call.cpp b/toolchain/check/call.cpp index b9f2e347da77..4621d8841a3c 100644 --- a/toolchain/check/call.cpp +++ b/toolchain/check/call.cpp @@ -116,24 +116,42 @@ static auto PerformCallToGenericClass(Context& context, SemIR::LocId loc_id, .specific_id = *callee_specific_id}); } -// Performs a call where the callee is the name of a generic interface, such as -// `AddWith(i32)`. -static auto PerformCallToGenericInterface( - Context& context, SemIR::LocId loc_id, SemIR::InterfaceId interface_id, +static auto EntityFromInterfaceOrNamedConstraint( + Context& context, SemIR::InterfaceId interface_id) + -> const SemIR::EntityWithParamsBase& { + return context.interfaces().Get(interface_id); +} + +static auto EntityFromInterfaceOrNamedConstraint( + Context& context, SemIR::NamedConstraintId named_constraint_id) + -> const SemIR::EntityWithParamsBase& { + return context.named_constraints().Get(named_constraint_id); +} + +// Performs a call where the callee is the name of a generic interface or named +// constraint, such as `AddWith(i32)`. +template + requires SameAsOneOf +static auto PerformCallToGenericInterfaceOrNamedConstaint( + Context& context, SemIR::LocId loc_id, IdT id, SemIR::SpecificId enclosing_specific_id, llvm::ArrayRef arg_ids) -> SemIR::InstId { - const auto& interface = context.interfaces().Get(interface_id); + const auto& entity = EntityFromInterfaceOrNamedConstraint(context, id); auto callee_specific_id = - ResolveCalleeInCall(context, loc_id, interface, - EntityKind::GenericInterface, enclosing_specific_id, + ResolveCalleeInCall(context, loc_id, entity, EntityKind::GenericInterface, + enclosing_specific_id, /*self_type_id=*/SemIR::InstId::None, /*self_id=*/SemIR::InstId::None, arg_ids); if (!callee_specific_id) { return SemIR::ErrorInst::InstId; } - return GetOrAddInst( - context, loc_id, - FacetTypeFromInterface(context, interface_id, *callee_specific_id)); + std::optional facet_type; + if constexpr (std::same_as) { + facet_type = FacetTypeFromInterface(context, id, *callee_specific_id); + } else { + facet_type = FacetTypeFromNamedConstraint(context, id, *callee_specific_id); + } + return GetOrAddInst(context, loc_id, *facet_type); } // Builds an appropriate specific function for the callee, also handling @@ -307,10 +325,15 @@ static auto PerformCallToNonFunction(Context& context, SemIR::LocId loc_id, arg_ids); } case CARBON_KIND(SemIR::GenericInterfaceType generic_interface): { - return PerformCallToGenericInterface( + return PerformCallToGenericInterfaceOrNamedConstaint( context, loc_id, generic_interface.interface_id, generic_interface.enclosing_specific_id, arg_ids); } + case CARBON_KIND(SemIR::GenericNamedConstraintType generic_constraint): { + return PerformCallToGenericInterfaceOrNamedConstaint( + context, loc_id, generic_constraint.named_constraint_id, + generic_constraint.enclosing_specific_id, arg_ids); + } default: { CARBON_DIAGNOSTIC(CallToNonCallable, Error, "value of type {0} is not callable", TypeOfInstId); diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 9e7640cce83c..6d6b07ce6b7b 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -265,6 +265,9 @@ class Context { auto classes() -> SemIR::ClassStore& { return sem_ir().classes(); } auto vtables() -> SemIR::VtableStore& { return sem_ir().vtables(); } auto interfaces() -> SemIR::InterfaceStore& { return sem_ir().interfaces(); } + auto named_constraints() -> SemIR::NamedConstraintStore& { + return sem_ir().named_constraints(); + } auto associated_constants() -> SemIR::AssociatedConstantStore& { return sem_ir().associated_constants(); } diff --git a/toolchain/check/eval_inst.cpp b/toolchain/check/eval_inst.cpp index 64aacad2775f..48b2d86719e3 100644 --- a/toolchain/check/eval_inst.cpp +++ b/toolchain/check/eval_inst.cpp @@ -451,6 +451,25 @@ auto EvalConstantInst(Context& context, SemIR::InterfaceDecl inst) context.generics().GetSelfSpecific(interface_info.generic_id))); } +auto EvalConstantInst(Context& context, SemIR::NamedConstraintDecl inst) + -> ConstantEvalResult { + const auto& named_constraint_info = + context.named_constraints().Get(inst.named_constraint_id); + + // If the named constraint has generic parameters, we don't produce a named + // constraint type, but a callable whose return value is a named constraint + // type. + if (named_constraint_info.has_parameters()) { + return ConstantEvalResult::NewSamePhase(SemIR::StructValue{ + .type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty}); + } + + // A non-parameterized named constraint declaration evaluates to a facet type. + return ConstantEvalResult::NewAnyPhase(FacetTypeFromNamedConstraint( + context, inst.named_constraint_id, + context.generics().GetSelfSpecific(named_constraint_info.generic_id))); +} + auto EvalConstantInst(Context& context, SemIR::NameRef inst) -> ConstantEvalResult { // A name reference evaluates to the value the name resolves to. diff --git a/toolchain/check/facet_type.cpp b/toolchain/check/facet_type.cpp index d961593601ed..b633f318f8ce 100644 --- a/toolchain/check/facet_type.cpp +++ b/toolchain/check/facet_type.cpp @@ -8,6 +8,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" +#include "toolchain/base/kind_switch.h" #include "toolchain/check/convert.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/generic.h" @@ -24,8 +25,23 @@ namespace Carbon::Check { auto FacetTypeFromInterface(Context& context, SemIR::InterfaceId interface_id, SemIR::SpecificId specific_id) -> SemIR::FacetType { - auto info = - SemIR::FacetTypeInfo{.extend_constraints = {{interface_id, specific_id}}}; + auto info = SemIR::FacetTypeInfo{}; + + info.extend_constraints.push_back({interface_id, specific_id}); + // TODO: Add `require impls` to the set of constraints. + + info.Canonicalize(); + SemIR::FacetTypeId facet_type_id = context.facet_types().Add(info); + return {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id}; +} + +auto FacetTypeFromNamedConstraint( + Context& context, SemIR::NamedConstraintId /*named_constraint_id*/, + SemIR::SpecificId /*specific_id*/) -> SemIR::FacetType { + auto info = SemIR::FacetTypeInfo{}; + + // TODO: Add `require impls` to the set of constraints. + info.Canonicalize(); SemIR::FacetTypeId facet_type_id = context.facet_types().Add(info); return {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id}; diff --git a/toolchain/check/facet_type.h b/toolchain/check/facet_type.h index 5a0828540937..71af9bc68d77 100644 --- a/toolchain/check/facet_type.h +++ b/toolchain/check/facet_type.h @@ -8,16 +8,27 @@ #include #include "toolchain/check/context.h" +#include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { -// Create a FacetType typed instruction object consisting of a single -// interface. The `specific_id` specifies arguments in the case the interface is -// generic. +// Create a FacetType typed instruction object consisting of a interface. The +// `specific_id` specifies arguments in the case the interface is generic. +// +// The resulting FacetType may contain multiple interfaces if the named +// interface contains `require` declarations. auto FacetTypeFromInterface(Context& context, SemIR::InterfaceId interface_id, SemIR::SpecificId specific_id) -> SemIR::FacetType; +// Create a FacetType typed instruction object consisting of a named constraint. +// The `specific_id` specifies arguments in the case the named constraint is +// generic. +auto FacetTypeFromNamedConstraint(Context& context, + SemIR::NamedConstraintId named_constraint_id, + SemIR::SpecificId specific_id) + -> SemIR::FacetType; + // Given an ImplWitnessAccessSubstituted, returns the InstId of the // ImplWitnessAccess. Otherwise, returns the input `inst_id` unchanged. // diff --git a/toolchain/check/generic.cpp b/toolchain/check/generic.cpp index bce78293fee5..1a6c7a924d67 100644 --- a/toolchain/check/generic.cpp +++ b/toolchain/check/generic.cpp @@ -730,7 +730,7 @@ auto ResolveSpecificDefinition(Context& context, SemIR::LocId loc_id, } auto DiagnoseIfGenericMissingExplicitParameters( - Context& context, SemIR::EntityWithParamsBase& entity_base) -> void { + Context& context, const SemIR::EntityWithParamsBase& entity_base) -> void { if (!entity_base.implicit_param_patterns_id.has_value() || entity_base.param_patterns_id.has_value()) { return; diff --git a/toolchain/check/generic.h b/toolchain/check/generic.h index cb86a3def31d..3d477eae7452 100644 --- a/toolchain/check/generic.h +++ b/toolchain/check/generic.h @@ -123,7 +123,7 @@ auto ResolveSpecificDefinition(Context& context, SemIR::LocId loc_id, // Diagnoses if an entity has implicit parameters, indicating it's generic, but // is missing explicit parameters. auto DiagnoseIfGenericMissingExplicitParameters( - Context& context, SemIR::EntityWithParamsBase& entity_base) -> void; + Context& context, const SemIR::EntityWithParamsBase& entity_base) -> void; } // namespace Carbon::Check diff --git a/toolchain/check/handle_interface.cpp b/toolchain/check/handle_interface.cpp index 9c3dcd04ec91..7015d24805f0 100644 --- a/toolchain/check/handle_interface.cpp +++ b/toolchain/check/handle_interface.cpp @@ -10,11 +10,15 @@ #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" +#include "toolchain/check/interface.h" #include "toolchain/check/merge.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/name_component.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" +#include "toolchain/sem_ir/entity_with_params_base.h" +#include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/interface.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { @@ -26,11 +30,12 @@ auto HandleParseNode(Context& context, Parse::InterfaceIntroducerId node_id) // Create an instruction block to hold the instructions created as part of the // interface signature, such as generic parameters. context.inst_block_stack().Push(); - // Push the bracketing node. - context.node_stack().Push(node_id); // Optional modifiers and the name follow. context.decl_introducer_state_stack().Push(); context.decl_name_stack().PushScopeAndStartName(); + + // Push the bracketing node. + context.node_stack().Push(node_id); return true; } @@ -56,87 +61,44 @@ static auto BuildInterfaceDecl(Context& context, // Add the interface declaration. auto interface_decl = SemIR::InterfaceDecl{ SemIR::TypeType::TypeId, SemIR::InterfaceId::None, decl_block_id}; - auto interface_decl_id = AddPlaceholderInst(context, node_id, interface_decl); + auto decl_inst_id = AddPlaceholderInst(context, node_id, interface_decl); SemIR::Interface interface_info = {name_context.MakeEntityWithParamsBase( - name, interface_decl_id, /*is_extern=*/false, - SemIR::LibraryNameId::None)}; + name, decl_inst_id, /*is_extern=*/false, SemIR::LibraryNameId::None)}; DiagnoseIfGenericMissingExplicitParameters(context, interface_info); // Check whether this is a redeclaration. SemIR::ScopeLookupResult lookup_result = context.decl_name_stack().LookupOrAddName( - name_context, interface_decl_id, - introducer.modifier_set.GetAccessKind()); - if (lookup_result.is_poisoned()) { - // This is a declaration of a poisoned name. - DiagnosePoisonedName(context, name_context.name_id_for_new_inst(), - lookup_result.poisoning_loc_id(), name_context.loc_id); - } else if (lookup_result.is_found()) { - SemIR::InstId existing_id = lookup_result.target_inst_id(); - if (auto existing_interface_decl = - context.insts().Get(existing_id).TryAs()) { - auto existing_interface = - context.interfaces().Get(existing_interface_decl->interface_id); - if (CheckRedeclParamsMatch( - context, - DeclParams(SemIR::LocId(interface_decl_id), - name.first_param_node_id, name.last_param_node_id, - name.implicit_param_patterns_id, - name.param_patterns_id), - DeclParams(existing_interface))) { - // TODO: This should be refactored a little, particularly for - // prev_import_ir_id. See similar logic for classes and functions, which - // might also be refactored to merge. - DiagnoseIfInvalidRedecl( - context, Lex::TokenKind::Interface, existing_interface.name_id, - RedeclInfo(interface_info, node_id, is_definition), - RedeclInfo(existing_interface, - SemIR::LocId(existing_interface.latest_decl_id()), - existing_interface.has_definition_started()), - /*prev_import_ir_id=*/SemIR::ImportIRId::None); + name_context, decl_inst_id, introducer.modifier_set.GetAccessKind()); + if (auto existing_decl = TryGetExistingDecl(context, name, lookup_result, + interface_info, is_definition)) { + auto existing_interface_decl = existing_decl->As(); + interface_decl.interface_id = existing_interface_decl.interface_id; + interface_decl.type_id = existing_interface_decl.type_id; + // TODO: If the new declaration is a definition, keep its parameter + // and implicit parameter lists rather than the ones from the + // previous declaration. - // Can't merge interface definitions due to the generic requirements. - if (!is_definition || !existing_interface.has_definition_started()) { - // This is a redeclaration of an existing interface. - interface_decl.interface_id = existing_interface_decl->interface_id; - interface_decl.type_id = existing_interface_decl->type_id; - // TODO: If the new declaration is a definition, keep its parameter - // and implicit parameter lists rather than the ones from the - // previous declaration. - } - } - } else { - // This is a redeclaration of something other than a interface. - DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id, - SemIR::LocId(existing_id)); - } - } - - // Create a new interface if this isn't a valid redeclaration. - if (!interface_decl.interface_id.has_value()) { - // TODO: If this is an invalid redeclaration of a non-interface entity or - // there was an error in the qualifier, we will have lost track of the - // interface name here. We should keep track of it even if the name is - // invalid. - interface_info.generic_id = BuildGenericDecl(context, interface_decl_id); + auto prev_decl_generic_id = + context.interfaces().Get(interface_decl.interface_id).generic_id; + FinishGenericRedecl(context, prev_decl_generic_id); + } else { + // Create a new interface if this isn't a valid redeclaration. + interface_info.generic_id = BuildGenericDecl(context, decl_inst_id); interface_decl.interface_id = context.interfaces().Add(interface_info); if (interface_info.has_parameters()) { interface_decl.type_id = GetGenericInterfaceType(context, interface_decl.interface_id, context.scope_stack().PeekSpecificId()); } - } else { - auto prev_decl_generic_id = - context.interfaces().Get(interface_decl.interface_id).generic_id; - FinishGenericRedecl(context, prev_decl_generic_id); } // Write the interface ID into the InterfaceDecl. - ReplaceInstBeforeConstantUse(context, interface_decl_id, interface_decl); + ReplaceInstBeforeConstantUse(context, decl_inst_id, interface_decl); - return {interface_decl.interface_id, interface_decl_id}; + return {interface_decl.interface_id, decl_inst_id}; } auto HandleParseNode(Context& context, Parse::InterfaceDeclId node_id) -> bool { @@ -147,16 +109,16 @@ auto HandleParseNode(Context& context, Parse::InterfaceDeclId node_id) -> bool { auto HandleParseNode(Context& context, Parse::InterfaceDefinitionStartId node_id) -> bool { - auto [interface_id, interface_decl_id] = + auto [interface_id, decl_inst_id] = BuildInterfaceDecl(context, node_id, /*is_definition=*/true); auto& interface_info = context.interfaces().Get(interface_id); // Track that this declaration is the definition. CARBON_CHECK(!interface_info.has_definition_started(), "Can't merge with defined interfaces."); - interface_info.definition_id = interface_decl_id; + interface_info.definition_id = decl_inst_id; interface_info.scope_id = context.name_scopes().Add( - interface_decl_id, SemIR::NameId::None, interface_info.parent_scope_id); + decl_inst_id, SemIR::NameId::None, interface_info.parent_scope_id); context.name_scopes() .Get(interface_info.scope_id) .set_is_interface_definition(); @@ -167,35 +129,21 @@ auto HandleParseNode(Context& context, StartGenericDefinition(context, interface_info.generic_id); context.inst_block_stack().Push(); - context.node_stack().Push(node_id, interface_id); // We use the arg stack to build the witness table type. context.args_type_info_stack().Push(); - // Declare and introduce `Self`. + // Declare and introduce `Self`. We model `Self` as a symbolic binding whose + // type is the interface, excluding any other interfaces mentioned by + // `require` declarations. SemIR::TypeId self_type_id = GetInterfaceType(context, interface_id, self_specific_id); - - // We model `Self` as a symbolic binding whose type is the interface. - // Because there is no equivalent non-symbolic value, we use `None` as - // the `value_id` on the `BindSymbolicName`. - auto entity_name_id = context.entity_names().AddSymbolicBindingName( - SemIR::NameId::SelfType, interface_info.scope_id, - context.scope_stack().AddCompileTimeBinding(), - /*is_template=*/false); - interface_info.self_param_id = - AddInst(context, SemIR::LocIdAndInst::NoLoc( - {.type_id = self_type_id, - .entity_name_id = entity_name_id, - .value_id = SemIR::InstId::None})); - context.scope_stack().PushCompileTimeBinding(interface_info.self_param_id); - context.name_scopes().AddRequiredName(interface_info.scope_id, - SemIR::NameId::SelfType, - interface_info.self_param_id); + interface_info.self_param_id = AddSelfGenericParameter( + context, self_type_id, interface_info.scope_id, /*is_template=*/false); // Enter the interface scope. - context.scope_stack().PushForEntity( - interface_decl_id, interface_info.scope_id, self_specific_id); + context.scope_stack().PushForEntity(decl_inst_id, interface_info.scope_id, + self_specific_id); // TODO: Handle the case where there's control flow in the interface body. For // example: @@ -207,6 +155,8 @@ auto HandleParseNode(Context& context, // We may need to track a list of instruction blocks here, as we do for a // function. interface_info.body_block_id = context.inst_block_stack().PeekOrAdd(); + + context.node_stack().Push(node_id, interface_id); return true; } diff --git a/toolchain/check/handle_named_constraint.cpp b/toolchain/check/handle_named_constraint.cpp index 70201cbbfcea..8db68990aeda 100644 --- a/toolchain/check/handle_named_constraint.cpp +++ b/toolchain/check/handle_named_constraint.cpp @@ -3,29 +3,169 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" +#include "toolchain/check/generic.h" #include "toolchain/check/handle.h" +#include "toolchain/check/inst.h" +#include "toolchain/check/interface.h" +#include "toolchain/check/modifiers.h" +#include "toolchain/check/type.h" +#include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/named_constraint.h" +#include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { -auto HandleParseNode(Context& context, Parse::NamedConstraintDeclId node_id) - -> bool { - return context.TODO(node_id, "HandleNamedConstraintDecl"); +auto HandleParseNode(Context& context, + Parse::NamedConstraintIntroducerId node_id) -> bool { + // This named constraint is potentially generic. + StartGenericDecl(context); + // Create an instruction block to hold the instructions created as part of the + // named constraint signature, such as generic parameters. + context.inst_block_stack().Push(); + // Optional modifiers and the name follow. + context.decl_introducer_state_stack().Push(); + context.decl_name_stack().PushScopeAndStartName(); + + // Push the bracketing node. + context.node_stack().Push(node_id); + return true; } -auto HandleParseNode(Context& context, - Parse::NamedConstraintDefinitionId node_id) -> bool { - // Note that the decl_name_stack will be popped by `ProcessNodeIds`. - return context.TODO(node_id, "HandleNamedConstraintDefinition"); +static auto BuildNamedConstraintDecl(Context& context, + Parse::AnyNamedConstraintDeclId node_id, + bool is_definition) + -> std::tuple { + auto name = PopNameComponent(context); + auto name_context = context.decl_name_stack().FinishName(name); + context.node_stack() + .PopAndDiscardSoloNodeId(); + + // TODO: PopSoloNodeId(`template`) if it's present, and track that in the + // NamedConstraint. Or maybe it should be a modifier, like `abstract class`? + + // Process modifiers. + auto [_, parent_scope_inst] = + context.name_scopes().GetInstIfValid(name_context.parent_scope_id); + auto introducer = + context.decl_introducer_state_stack().Pop(); + CheckAccessModifiersOnDecl(context, introducer, parent_scope_inst); + LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Access); + + auto decl_block_id = context.inst_block_stack().Pop(); + + // Add the constraint declaration. + auto constraint_decl = SemIR::NamedConstraintDecl{ + SemIR::TypeType::TypeId, SemIR::NamedConstraintId::None, decl_block_id}; + auto decl_inst_id = AddPlaceholderInst(context, node_id, constraint_decl); + + SemIR::NamedConstraint constraint_info = { + name_context.MakeEntityWithParamsBase(name, decl_inst_id, + /*is_extern=*/false, + SemIR::LibraryNameId::None)}; + + DiagnoseIfGenericMissingExplicitParameters(context, constraint_info); + + // Check whether this is a redeclaration. + SemIR::ScopeLookupResult lookup_result = + context.decl_name_stack().LookupOrAddName( + name_context, decl_inst_id, introducer.modifier_set.GetAccessKind()); + if (auto existing_decl = TryGetExistingDecl(context, name, lookup_result, + constraint_info, is_definition)) { + auto existing_constraint_decl = + existing_decl->As(); + constraint_decl.named_constraint_id = + existing_constraint_decl.named_constraint_id; + constraint_decl.type_id = existing_constraint_decl.type_id; + // TODO: If the new declaration is a definition, keep its parameter + // and implicit parameter lists rather than the ones from the + // previous declaration. + + auto prev_decl_generic_id = context.named_constraints() + .Get(constraint_decl.named_constraint_id) + .generic_id; + FinishGenericRedecl(context, prev_decl_generic_id); + } else { + // Create a new named constraint if this isn't a valid redeclaration. + constraint_info.generic_id = BuildGenericDecl(context, decl_inst_id); + constraint_decl.named_constraint_id = + context.named_constraints().Add(constraint_info); + if (constraint_info.has_parameters()) { + constraint_decl.type_id = GetGenericNamedConstraintType( + context, constraint_decl.named_constraint_id, + context.scope_stack().PeekSpecificId()); + } + } + + // Write the completed NamedConstraintDecl instruction. + ReplaceInstBeforeConstantUse(context, decl_inst_id, constraint_decl); + + return {constraint_decl.named_constraint_id, decl_inst_id}; +} + +auto HandleParseNode(Context& context, Parse::NamedConstraintDeclId node_id) + -> bool { + BuildNamedConstraintDecl(context, node_id, /*is_definition=*/false); + context.decl_name_stack().PopScope(); + return true; } auto HandleParseNode(Context& context, Parse::NamedConstraintDefinitionStartId node_id) -> bool { - return context.TODO(node_id, "HandleNamedConstraintDefinitionStart"); + auto [named_constraint_id, decl_inst_id] = + BuildNamedConstraintDecl(context, node_id, /*is_definition=*/true); + auto& constraint_info = context.named_constraints().Get(named_constraint_id); + + // TODO: Support for `template constraint`. + bool is_template = false; + + // Track that this declaration is the definition. + CARBON_CHECK(!constraint_info.has_definition_started(), + "Can't merge with defined named constraints."); + constraint_info.definition_id = decl_inst_id; + constraint_info.scope_id = context.name_scopes().Add( + decl_inst_id, SemIR::NameId::None, constraint_info.parent_scope_id); + + auto self_specific_id = + context.generics().GetSelfSpecific(constraint_info.generic_id); + StartGenericDefinition(context, constraint_info.generic_id); + + context.inst_block_stack().Push(); + + // Declare and introduce `Self`. We model `Self` as a symbolic binding whose + // type is the named constraint, excluding any other interfaces mentioned by + // `require` declarations. This makes it an empty facet type. + SemIR::TypeId self_type_id = + GetNamedConstraintType(context, named_constraint_id, self_specific_id); + constraint_info.self_param_id = AddSelfGenericParameter( + context, self_type_id, constraint_info.scope_id, is_template); + + // Enter the constraint scope. + context.scope_stack().PushForEntity(decl_inst_id, constraint_info.scope_id, + self_specific_id); + + constraint_info.body_block_id = context.inst_block_stack().PeekOrAdd(); + + context.node_stack().Push(node_id, named_constraint_id); + return true; } auto HandleParseNode(Context& context, - Parse::NamedConstraintIntroducerId node_id) -> bool { - return context.TODO(node_id, "HandleNamedConstraintIntroducer"); + Parse::NamedConstraintDefinitionId /*node_id*/) -> bool { + auto named_constraint_id = + context.node_stack() + .Pop(); + context.inst_block_stack().Pop(); + + const auto& constraint_info = + context.named_constraints().Get(named_constraint_id); + + // TODO: Do something with `require` and `alias` statements in the body of the + // constraint. + + FinishGenericDefinition(context, constraint_info.generic_id); + + // The decl_name_stack and scopes are popped by `ProcessNodeIds`. + return true; } } // namespace Carbon::Check diff --git a/toolchain/check/interface.cpp b/toolchain/check/interface.cpp index 2b73a056fb51..75a987bbc3c8 100644 --- a/toolchain/check/interface.cpp +++ b/toolchain/check/interface.cpp @@ -7,11 +7,15 @@ #include #include +#include "common/concepts.h" #include "toolchain/check/context.h" #include "toolchain/check/eval.h" #include "toolchain/check/generic.h" #include "toolchain/check/inst.h" +#include "toolchain/check/merge.h" +#include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" +#include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" @@ -219,4 +223,131 @@ auto GetTypeForSpecificAssociatedEntity(Context& context, SemIR::LocId loc_id, CARBON_FATAL("Unexpected kind for associated constant {0}", decl); } +auto AddSelfGenericParameter(Context& context, SemIR::TypeId type_id, + SemIR::NameScopeId scope_id, bool is_template) + -> SemIR::InstId { + auto entity_name_id = context.entity_names().AddSymbolicBindingName( + SemIR::NameId::SelfType, scope_id, + context.scope_stack().AddCompileTimeBinding(), is_template); + // Because there is no equivalent non-symbolic value, we use `None` as + // the `value_id` on the `BindSymbolicName`. + auto self_param_inst_id = + AddInst(context, SemIR::LocIdAndInst::NoLoc( + {.type_id = type_id, + .entity_name_id = entity_name_id, + .value_id = SemIR::InstId::None})); + context.scope_stack().PushCompileTimeBinding(self_param_inst_id); + context.name_scopes().AddRequiredName(scope_id, SemIR::NameId::SelfType, + self_param_inst_id); + return self_param_inst_id; +} + +template + requires std::same_as +static auto TryGetEntity(Context& context, SemIR::Inst inst) + -> const SemIR::EntityWithParamsBase* { + if (auto decl = inst.TryAs()) { + return &context.interfaces().Get(decl->interface_id); + } else { + return nullptr; + } +} + +template + requires std::same_as +static auto TryGetEntity(Context& context, SemIR::Inst inst) + -> const SemIR::EntityWithParamsBase* { + if (auto decl = inst.TryAs()) { + return &context.named_constraints().Get(decl->named_constraint_id); + } else { + return nullptr; + } +} + +template + requires std::same_as +static constexpr auto DeclTokenKind() -> Lex::TokenKind { + return Lex::TokenKind::Interface; +} + +template + requires std::same_as +static constexpr auto DeclTokenKind() -> Lex::TokenKind { + return Lex::TokenKind::Constraint; +} + +template + requires SameAsOneOf +auto TryGetExistingDecl(Context& context, const NameComponent& name, + SemIR::ScopeLookupResult lookup_result, + const EntityT& entity, bool is_definition) + -> std::optional { + if (lookup_result.is_poisoned()) { + // This is a declaration of a poisoned name. + DiagnosePoisonedName(context, name.name_id, + lookup_result.poisoning_loc_id(), name.name_loc_id); + return std::nullopt; + } + + if (!lookup_result.is_found()) { + return std::nullopt; + } + + SemIR::InstId existing_id = lookup_result.target_inst_id(); + SemIR::Inst existing_decl_inst = context.insts().Get(existing_id); + const auto* existing_decl_entity = + TryGetEntity(context, existing_decl_inst); + if (!existing_decl_entity) { + // This is a redeclaration with a different entity kind. + DiagnoseDuplicateName(context, name.name_id, name.name_loc_id, + SemIR::LocId(existing_id)); + return std::nullopt; + } + + if (!CheckRedeclParamsMatch( + context, + DeclParams(SemIR::LocId(entity.latest_decl_id()), + name.first_param_node_id, name.last_param_node_id, + name.implicit_param_patterns_id, name.param_patterns_id), + DeclParams(*existing_decl_entity))) { + // Mismatch is diagnosed already if found. + return std::nullopt; + } + + // TODO: This should be refactored a little, particularly for + // prev_import_ir_id. See similar logic for classes and functions, which + // might also be refactored to merge. + DiagnoseIfInvalidRedecl( + context, DeclTokenKind(), existing_decl_entity->name_id, + RedeclInfo(entity, SemIR::LocId(entity.latest_decl_id()), is_definition), + RedeclInfo(*existing_decl_entity, + SemIR::LocId(existing_decl_entity->latest_decl_id()), + existing_decl_entity->has_definition_started()), + /*prev_import_ir_id=*/SemIR::ImportIRId::None); + + if (is_definition && existing_decl_entity->has_definition_started()) { + // DiagnoseIfInvalidRedecl would diagnose an error in this case, since we'd + // have two definitions. Given the declaration parts of the definitions + // match, we would be able to use the prior declaration for error recovery, + // except that having two definitions causes larger problems for generics. + // All interfaces (and named constraints) are generic with an implicit Self + // compile time binding. + return std::nullopt; + } + + // This is a matching redeclaration of an existing entity of the same type. + return existing_decl_inst; +} + +template auto TryGetExistingDecl(Context& context, const NameComponent& name, + SemIR::ScopeLookupResult lookup_result, + const SemIR::Interface& entity, + bool is_definition) + -> std::optional; +template auto TryGetExistingDecl(Context& context, const NameComponent& name, + SemIR::ScopeLookupResult lookup_result, + const SemIR::NamedConstraint& entity, + bool is_definition) + -> std::optional; + } // namespace Carbon::Check diff --git a/toolchain/check/interface.h b/toolchain/check/interface.h index 0b762cab003a..e3633564b2ea 100644 --- a/toolchain/check/interface.h +++ b/toolchain/check/interface.h @@ -5,8 +5,16 @@ #ifndef CARBON_TOOLCHAIN_CHECK_INTERFACE_H_ #define CARBON_TOOLCHAIN_CHECK_INTERFACE_H_ +#include + #include "toolchain/check/context.h" +#include "toolchain/check/decl_name_stack.h" +#include "toolchain/check/name_component.h" +#include "toolchain/parse/node_ids.h" +#include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/name_scope.h" +#include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { @@ -34,6 +42,25 @@ auto GetTypeForSpecificAssociatedEntity(Context& context, SemIR::LocId loc_id, SemIR::InstId self_witness_id) -> SemIR::TypeId; +// Creates a symbolic binding for `Self` of type `type_id` in the scope of +// `scope_id`, and add the name `Self` for the compile time binding. +// +// Returns the symbolic binding instruction. +auto AddSelfGenericParameter(Context& context, SemIR::TypeId type_id, + SemIR::NameScopeId scope_id, bool is_template) + -> SemIR::InstId; + +// Given a search result `lookup_result` for `name`, returns the previous valid +// declaration of `name` if there is one. The `entity` is a new decl of the same +// `name`, and the existing decl need to be of the same entity type. Otherwise, +// produces diagnostics if needed and returns nullopt. +template + requires SameAsOneOf +auto TryGetExistingDecl(Context& context, const NameComponent& name, + SemIR::ScopeLookupResult lookup_result, + const EntityT& entity, bool is_definition) + -> std::optional; + } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_INTERFACE_H_ diff --git a/toolchain/check/merge.h b/toolchain/check/merge.h index 9fe940bb7fc6..143c4460fec1 100644 --- a/toolchain/check/merge.h +++ b/toolchain/check/merge.h @@ -18,8 +18,8 @@ auto DiagnoseExternRequiresDeclInApiFile(Context& context, SemIR::LocId loc_id) // Information on new and previous declarations for DiagnoseIfInvalidRedecl. struct RedeclInfo { - explicit RedeclInfo(SemIR::EntityWithParamsBase params, SemIR::LocId loc_id, - bool is_definition) + explicit RedeclInfo(const SemIR::EntityWithParamsBase& params, + SemIR::LocId loc_id, bool is_definition) : loc_id(loc_id), is_definition(is_definition), is_extern(params.is_extern), diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 9480c2e56a62..168949e78f05 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -436,12 +436,16 @@ class NodeStack { return Id::KindFor(); case Parse::NodeKind::ImplDefinitionStart: return Id::KindFor(); + case Parse::NodeKind::NamedConstraintDefinitionStart: + return Id::KindFor(); case Parse::NodeKind::SelfTypeName: case Parse::NodeKind::SelfValueName: return Id::KindFor(); case Parse::NodeKind::DefaultLibrary: case Parse::NodeKind::LibraryName: return Id::KindFor(); + case Parse::NodeKind::AssociatedConstantInitializer: + case Parse::NodeKind::AssociatedConstantIntroducer: case Parse::NodeKind::BuiltinName: case Parse::NodeKind::ChoiceIntroducer: case Parse::NodeKind::ClassIntroducer: @@ -457,8 +461,7 @@ class NodeStack { case Parse::NodeKind::InterfaceIntroducer: case Parse::NodeKind::LetInitializer: case Parse::NodeKind::LetIntroducer: - case Parse::NodeKind::AssociatedConstantIntroducer: - case Parse::NodeKind::AssociatedConstantInitializer: + case Parse::NodeKind::NamedConstraintIntroducer: case Parse::NodeKind::ReturnStatementStart: case Parse::NodeKind::StructLiteralStart: case Parse::NodeKind::StructTypeLiteralField: @@ -515,8 +518,6 @@ class NodeStack { case Parse::NodeKind::MatchDefaultStart: case Parse::NodeKind::MatchIntroducer: case Parse::NodeKind::MatchStatementStart: - case Parse::NodeKind::NamedConstraintDefinitionStart: - case Parse::NodeKind::NamedConstraintIntroducer: case Parse::NodeKind::NamespaceStart: case Parse::NodeKind::PackageIntroducer: case Parse::NodeKind::ParenExprStart: diff --git a/toolchain/check/testdata/named_constraint/basic.carbon b/toolchain/check/testdata/named_constraint/basic.carbon new file mode 100644 index 000000000000..561eb5eaa1c5 --- /dev/null +++ b/toolchain/check/testdata/named_constraint/basic.carbon @@ -0,0 +1,97 @@ +// 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/named_constraint/basic.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/named_constraint/basic.carbon + +// --- definition.carbon +library "[[@TEST_NAME]]"; +//@include-in-dumps + +constraint Declared {} + +// --- forward_declaration.carbon +library "[[@TEST_NAME]]"; +//@include-in-dumps + +constraint ForwardDeclared; + +constraint ForwardDeclared {} + +// --- fail_name_conflict.carbon +library "[[@TEST_NAME]]"; + +class ForwardDeclared; + +// CHECK:STDERR: fail_name_conflict.carbon:[[@LINE+7]]:12: error: duplicate name `ForwardDeclared` being declared in the same scope [NameDeclDuplicate] +// CHECK:STDERR: constraint ForwardDeclared {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_name_conflict.carbon:[[@LINE-5]]:1: note: name is previously declared here [NameDeclPrevious] +// CHECK:STDERR: class ForwardDeclared; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +constraint ForwardDeclared {} + +// --- fail_defined_twice.carbon +library "[[@TEST_NAME]]"; + +constraint Declared {} + +// CHECK:STDERR: fail_defined_twice.carbon:[[@LINE+7]]:1: error: redefinition of `constraint Declared` [RedeclRedef] +// CHECK:STDERR: constraint Declared {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_defined_twice.carbon:[[@LINE-5]]:1: note: previously defined here [RedeclPrevDef] +// CHECK:STDERR: constraint Declared {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +constraint Declared {} + +// CHECK:STDOUT: --- definition.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %Self: %type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Declared = %Declared.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Declared.decl: type = constraint_decl @Declared [concrete = constants.%type] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: constraint @Declared { +// CHECK:STDOUT: %Self: %type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- forward_declaration.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %Self: %type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .ForwardDeclared = %ForwardDeclared.decl.loc4 +// CHECK:STDOUT: } +// CHECK:STDOUT: %ForwardDeclared.decl.loc4: type = constraint_decl @ForwardDeclared [concrete = constants.%type] {} {} +// CHECK:STDOUT: %ForwardDeclared.decl.loc6: type = constraint_decl @ForwardDeclared [concrete = constants.%type] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: constraint @ForwardDeclared { +// CHECK:STDOUT: %Self: %type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/named_constraint/convert.carbon b/toolchain/check/testdata/named_constraint/convert.carbon new file mode 100644 index 000000000000..d81a97db2d26 --- /dev/null +++ b/toolchain/check/testdata/named_constraint/convert.carbon @@ -0,0 +1,183 @@ +// 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/convert.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/named_constraint/convert.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/named_constraint/convert.carbon + +// --- type_and_with_named_constraint_self.carbon +library "[[@TEST_NAME]]"; + +constraint E {} + +fn F(T:! E) {} + +//@dump-sem-ir-begin +fn G(T:! E & E) { + F(T); +} +//@dump-sem-ir-end + +// --- compatible_constraints.carbon +library "[[@TEST_NAME]]"; + +constraint E1 {} +constraint E2 {} + +fn F(T:! E1) {} + +//@dump-sem-ir-begin +fn G(T:! E2) { + F(T); +} +//@dump-sem-ir-end + +// --- fail_todo_unspecified.carbon +library "[[@TEST_NAME]]"; + +interface Z {} + +constraint E { + // CHECK:STDERR: fail_todo_unspecified.carbon:[[@LINE+8]]:3: error: unrecognized declaration introducer [UnrecognizedDecl] + // CHECK:STDERR: require impls Z; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_todo_unspecified.carbon:[[@LINE+4]]:3: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] + // CHECK:STDERR: require impls Z; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + require impls Z; +} + +fn F(T:! E) {} + +class C {} +impl C as Z {} + +fn G(T:! Z) { + // Anything that impls Z also impls E, since that's all it requires. Nothing + // needs to specify that T impls E. + F(T); + // Same for concrete types. + F(C); +} + +// CHECK:STDOUT: --- type_and_with_named_constraint_self.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %T: %type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type.e25: type = pattern_type %type [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %BitAndWith.type.8a6: type = facet_type <@BitAndWith, @BitAndWith(type)> [concrete] +// CHECK:STDOUT: %BitAndWith.Op.type.9a3: type = fn_type @BitAndWith.Op, @BitAndWith(type) [concrete] +// CHECK:STDOUT: %BitAndWith.impl_witness: = impl_witness imports.%BitAndWith.impl_witness_table [concrete] +// CHECK:STDOUT: %BitAndWith.facet: %BitAndWith.type.8a6 = facet_value type, (%BitAndWith.impl_witness) [concrete] +// CHECK:STDOUT: %.fa7: type = fn_type_with_self_type %BitAndWith.Op.type.9a3, %BitAndWith.facet [concrete] +// CHECK:STDOUT: %type.as.BitAndWith.impl.Op.type: type = fn_type @type.as.BitAndWith.impl.Op [concrete] +// CHECK:STDOUT: %type.as.BitAndWith.impl.Op: %type.as.BitAndWith.impl.Op.type = struct_value () [concrete] +// CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound: = bound_method %type, %type.as.BitAndWith.impl.Op [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%T) [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.636: %type.as.BitAndWith.impl.Op.type = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [concrete = constants.%type.as.BitAndWith.impl.Op] +// CHECK:STDOUT: %BitAndWith.impl_witness_table = impl_witness_table (%Core.import_ref.636), @type.as.BitAndWith.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %T.patt: %pattern_type.e25 = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc8_12.1: type = splice_block %.loc8_12.3 [concrete = constants.%type] { +// CHECK:STDOUT: +// CHECK:STDOUT: %E.ref.loc8_10: type = name_ref E, file.%E.decl [concrete = constants.%type] +// CHECK:STDOUT: %E.ref.loc8_14: type = name_ref E, file.%E.decl [concrete = constants.%type] +// CHECK:STDOUT: %impl.elem0: %.fa7 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] +// CHECK:STDOUT: %bound_method: = bound_method %E.ref.loc8_10, %impl.elem0 [concrete = constants.%type.as.BitAndWith.impl.Op.bound] +// CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call: init type = call %bound_method(%E.ref.loc8_10, %E.ref.loc8_14) [concrete = constants.%type] +// CHECK:STDOUT: %.loc8_12.2: type = value_of_initializer %type.as.BitAndWith.impl.Op.call [concrete = constants.%type] +// CHECK:STDOUT: %.loc8_12.3: type = converted %type.as.BitAndWith.impl.Op.call, %.loc8_12.2 [concrete = constants.%type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %T.loc8_6.2: %type = bind_symbolic_name T, 0 [symbolic = %T.loc8_6.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @G(%T.loc8_6.2: %type) { +// CHECK:STDOUT: %T.loc8_6.1: %type = bind_symbolic_name T, 0 [symbolic = %T.loc8_6.1 (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %F.specific_fn.loc9_3.2: = specific_function constants.%F, @F(%T.loc8_6.1) [symbolic = %F.specific_fn.loc9_3.2 (constants.%F.specific_fn)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %T.ref: %type = name_ref T, %T.loc8_6.2 [symbolic = %T.loc8_6.1 (constants.%T)] +// CHECK:STDOUT: %F.specific_fn.loc9_3.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc9_3.2 (constants.%F.specific_fn)] +// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn.loc9_3.1() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @G(constants.%T) { +// CHECK:STDOUT: %T.loc8_6.1 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- compatible_constraints.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %T: %type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type: type = pattern_type %type [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%T) [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc9: type = splice_block %E2.ref [concrete = constants.%type] { +// CHECK:STDOUT: +// CHECK:STDOUT: %E2.ref: type = name_ref E2, file.%E2.decl [concrete = constants.%type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %T.loc9_6.2: %type = bind_symbolic_name T, 0 [symbolic = %T.loc9_6.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @G(%T.loc9_6.2: %type) { +// CHECK:STDOUT: %T.loc9_6.1: %type = bind_symbolic_name T, 0 [symbolic = %T.loc9_6.1 (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %F.specific_fn.loc10_3.2: = specific_function constants.%F, @F(%T.loc9_6.1) [symbolic = %F.specific_fn.loc10_3.2 (constants.%F.specific_fn)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %T.ref: %type = name_ref T, %T.loc9_6.2 [symbolic = %T.loc9_6.1 (constants.%T)] +// CHECK:STDOUT: %F.specific_fn.loc10_3.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc10_3.2 (constants.%F.specific_fn)] +// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn.loc10_3.1() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @G(constants.%T) { +// CHECK:STDOUT: %T.loc9_6.1 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/named_constraint/empty.carbon b/toolchain/check/testdata/named_constraint/empty.carbon new file mode 100644 index 000000000000..e24203b8d81e --- /dev/null +++ b/toolchain/check/testdata/named_constraint/empty.carbon @@ -0,0 +1,191 @@ +// 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/named_constraint/empty.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/named_constraint/empty.carbon + +constraint Empty {} + +// No constraints, so it matches any type, but you can't do anything with it. +// Like `type`. +// +//@dump-sem-ir-begin +fn F(T:! Empty) {} +//@dump-sem-ir-end + +interface Z {} + +//@dump-sem-ir-begin +fn G(T:! Z, U:! type, V:! Empty) { + F({}); + F(type); + F(T); + F(U); + F(V); +} +//@dump-sem-ir-end + +// CHECK:STDOUT: --- empty.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %T.85e: %type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type.e25: type = pattern_type %type [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] +// CHECK:STDOUT: %T.1d7: %Z.type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type.4a0: type = pattern_type %Z.type [concrete] +// CHECK:STDOUT: %U: type = bind_symbolic_name U, 1 [symbolic] +// CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] +// CHECK:STDOUT: %V: %type = bind_symbolic_name V, 2 [symbolic] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %facet_value.6fa: %type = facet_value %empty_struct_type, () [concrete] +// CHECK:STDOUT: %F.specific_fn.007: = specific_function %F, @F(%facet_value.6fa) [concrete] +// CHECK:STDOUT: %facet_value.832: %type = facet_value type, () [concrete] +// CHECK:STDOUT: %F.specific_fn.7db: = specific_function %F, @F(%facet_value.832) [concrete] +// CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.1d7 [symbolic] +// CHECK:STDOUT: %facet_value.f04: %type = facet_value %T.binding.as_type, () [symbolic] +// CHECK:STDOUT: %F.specific_fn.59e: = specific_function %F, @F(%facet_value.f04) [symbolic] +// CHECK:STDOUT: %facet_value.137: %type = facet_value %U, () [symbolic] +// CHECK:STDOUT: %F.specific_fn.296: = specific_function %F, @F(%facet_value.137) [symbolic] +// CHECK:STDOUT: %F.specific_fn.88e: = specific_function %F, @F(%V) [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %T.patt: %pattern_type.e25 = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc19: type = splice_block %Empty.ref [concrete = constants.%type] { +// CHECK:STDOUT: +// CHECK:STDOUT: %Empty.ref: type = name_ref Empty, file.%Empty.decl [concrete = constants.%type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %T.loc19_6.2: %type = bind_symbolic_name T, 0 [symbolic = %T.loc19_6.1 (constants.%T.85e)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %T.patt: %pattern_type.4a0 = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] +// CHECK:STDOUT: %V.patt: %pattern_type.e25 = symbolic_binding_pattern V, 2 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc25_10: type = splice_block %Z.ref [concrete = constants.%Z.type] { +// CHECK:STDOUT: +// CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %T.loc25_6.2: %Z.type = bind_symbolic_name T, 0 [symbolic = %T.loc25_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: +// CHECK:STDOUT: %U.loc25_13.2: type = bind_symbolic_name U, 1 [symbolic = %U.loc25_13.1 (constants.%U)] +// CHECK:STDOUT: %.loc25_27: type = splice_block %Empty.ref [concrete = constants.%type] { +// CHECK:STDOUT: +// CHECK:STDOUT: %Empty.ref: type = name_ref Empty, file.%Empty.decl [concrete = constants.%type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %V.loc25_23.2: %type = bind_symbolic_name V, 2 [symbolic = %V.loc25_23.1 (constants.%V)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @F(%T.loc19_6.2: %type) { +// CHECK:STDOUT: %T.loc19_6.1: %type = bind_symbolic_name T, 0 [symbolic = %T.loc19_6.1 (constants.%T.85e)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: fn() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @G(%T.loc25_6.2: %Z.type, %U.loc25_13.2: type, %V.loc25_23.2: %type) { +// CHECK:STDOUT: %T.loc25_6.1: %Z.type = bind_symbolic_name T, 0 [symbolic = %T.loc25_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: %U.loc25_13.1: type = bind_symbolic_name U, 1 [symbolic = %U.loc25_13.1 (constants.%U)] +// CHECK:STDOUT: %V.loc25_23.1: %type = bind_symbolic_name V, 2 [symbolic = %V.loc25_23.1 (constants.%V)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc25_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %facet_value.loc28_6.2: %type = facet_value %T.binding.as_type, () [symbolic = %facet_value.loc28_6.2 (constants.%facet_value.f04)] +// CHECK:STDOUT: %F.specific_fn.loc28_3.2: = specific_function constants.%F, @F(%facet_value.loc28_6.2) [symbolic = %F.specific_fn.loc28_3.2 (constants.%F.specific_fn.59e)] +// CHECK:STDOUT: %facet_value.loc29_6.2: %type = facet_value %U.loc25_13.1, () [symbolic = %facet_value.loc29_6.2 (constants.%facet_value.137)] +// CHECK:STDOUT: %F.specific_fn.loc29_3.2: = specific_function constants.%F, @F(%facet_value.loc29_6.2) [symbolic = %F.specific_fn.loc29_3.2 (constants.%F.specific_fn.296)] +// CHECK:STDOUT: %F.specific_fn.loc30_3.2: = specific_function constants.%F, @F(%V.loc25_23.1) [symbolic = %F.specific_fn.loc30_3.2 (constants.%F.specific_fn.88e)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref.loc26: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %.loc26_6: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %facet_value.loc26: %type = facet_value constants.%empty_struct_type, () [concrete = constants.%facet_value.6fa] +// CHECK:STDOUT: %.loc26_7: %type = converted %.loc26_6, %facet_value.loc26 [concrete = constants.%facet_value.6fa] +// CHECK:STDOUT: %F.specific_fn.loc26: = specific_function %F.ref.loc26, @F(constants.%facet_value.6fa) [concrete = constants.%F.specific_fn.007] +// CHECK:STDOUT: %F.call.loc26: init %empty_tuple.type = call %F.specific_fn.loc26() +// CHECK:STDOUT: %F.ref.loc27: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %facet_value.loc27: %type = facet_value type, () [concrete = constants.%facet_value.832] +// CHECK:STDOUT: %.loc27: %type = converted type, %facet_value.loc27 [concrete = constants.%facet_value.832] +// CHECK:STDOUT: %F.specific_fn.loc27: = specific_function %F.ref.loc27, @F(constants.%facet_value.832) [concrete = constants.%F.specific_fn.7db] +// CHECK:STDOUT: %F.call.loc27: init %empty_tuple.type = call %F.specific_fn.loc27() +// CHECK:STDOUT: %F.ref.loc28: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %T.ref: %Z.type = name_ref T, %T.loc25_6.2 [symbolic = %T.loc25_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %facet_value.loc28_6.1: %type = facet_value %T.as_type, () [symbolic = %facet_value.loc28_6.2 (constants.%facet_value.f04)] +// CHECK:STDOUT: %.loc28: %type = converted %T.ref, %facet_value.loc28_6.1 [symbolic = %facet_value.loc28_6.2 (constants.%facet_value.f04)] +// CHECK:STDOUT: %F.specific_fn.loc28_3.1: = specific_function %F.ref.loc28, @F(constants.%facet_value.f04) [symbolic = %F.specific_fn.loc28_3.2 (constants.%F.specific_fn.59e)] +// CHECK:STDOUT: %F.call.loc28: init %empty_tuple.type = call %F.specific_fn.loc28_3.1() +// CHECK:STDOUT: %F.ref.loc29: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %U.ref: type = name_ref U, %U.loc25_13.2 [symbolic = %U.loc25_13.1 (constants.%U)] +// CHECK:STDOUT: %facet_value.loc29_6.1: %type = facet_value %U.ref, () [symbolic = %facet_value.loc29_6.2 (constants.%facet_value.137)] +// CHECK:STDOUT: %.loc29: %type = converted %U.ref, %facet_value.loc29_6.1 [symbolic = %facet_value.loc29_6.2 (constants.%facet_value.137)] +// CHECK:STDOUT: %F.specific_fn.loc29_3.1: = specific_function %F.ref.loc29, @F(constants.%facet_value.137) [symbolic = %F.specific_fn.loc29_3.2 (constants.%F.specific_fn.296)] +// CHECK:STDOUT: %F.call.loc29: init %empty_tuple.type = call %F.specific_fn.loc29_3.1() +// CHECK:STDOUT: %F.ref.loc30: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %V.ref: %type = name_ref V, %V.loc25_23.2 [symbolic = %V.loc25_23.1 (constants.%V)] +// CHECK:STDOUT: %F.specific_fn.loc30_3.1: = specific_function %F.ref.loc30, @F(constants.%V) [symbolic = %F.specific_fn.loc30_3.2 (constants.%F.specific_fn.88e)] +// CHECK:STDOUT: %F.call.loc30: init %empty_tuple.type = call %F.specific_fn.loc30_3.1() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T.85e) { +// CHECK:STDOUT: %T.loc19_6.1 => constants.%T.85e +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @G(constants.%T.1d7, constants.%U, constants.%V) { +// CHECK:STDOUT: %T.loc25_6.1 => constants.%T.1d7 +// CHECK:STDOUT: %U.loc25_13.1 => constants.%U +// CHECK:STDOUT: %V.loc25_23.1 => constants.%V +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%facet_value.6fa) { +// CHECK:STDOUT: %T.loc19_6.1 => constants.%facet_value.6fa +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%facet_value.832) { +// CHECK:STDOUT: %T.loc19_6.1 => constants.%facet_value.832 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%facet_value.f04) { +// CHECK:STDOUT: %T.loc19_6.1 => constants.%facet_value.f04 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%facet_value.137) { +// CHECK:STDOUT: %T.loc19_6.1 => constants.%facet_value.137 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%V) { +// CHECK:STDOUT: %T.loc19_6.1 => constants.%V +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/named_constraint/empty_generic.carbon b/toolchain/check/testdata/named_constraint/empty_generic.carbon new file mode 100644 index 000000000000..e56b77366892 --- /dev/null +++ b/toolchain/check/testdata/named_constraint/empty_generic.carbon @@ -0,0 +1,257 @@ +// 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/named_constraint/empty_generic.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/named_constraint/empty_generic.carbon + +interface Z {} + +//@dump-sem-ir-begin +constraint Empty(T:! type) {} + +fn F(U:! type, T:! Empty(U)) {} + +fn G(T:! Z, U:! type, V:! Empty(T)) { + F(T, {}); + F(T, type); + F(T, T); + F(T, U); + F(T, V); +} + +//@dump-sem-ir-end + +// CHECK:STDOUT: --- empty_generic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %T.8b3: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] +// CHECK:STDOUT: %Empty.type: type = generic_named_constaint_type @Empty [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %empty_struct: %Empty.type = struct_value () [concrete] +// CHECK:STDOUT: %U.8b3: type = bind_symbolic_name U, 0 [symbolic] +// CHECK:STDOUT: %T.eca: %type = bind_symbolic_name T, 1 [symbolic] +// CHECK:STDOUT: %pattern_type.e25: type = pattern_type %type [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %T.1d7: %Z.type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type.4a0: type = pattern_type %Z.type [concrete] +// CHECK:STDOUT: %U.336: type = bind_symbolic_name U, 1 [symbolic] +// CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.1d7 [symbolic] +// CHECK:STDOUT: %V: %type = bind_symbolic_name V, 2 [symbolic] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %facet_value.6fa: %type = facet_value %empty_struct_type, () [concrete] +// CHECK:STDOUT: %F.specific_fn.8f2: = specific_function %F, @F(%T.binding.as_type, %facet_value.6fa) [symbolic] +// CHECK:STDOUT: %facet_value.832: %type = facet_value type, () [concrete] +// CHECK:STDOUT: %F.specific_fn.d97: = specific_function %F, @F(%T.binding.as_type, %facet_value.832) [symbolic] +// CHECK:STDOUT: %facet_value.f04: %type = facet_value %T.binding.as_type, () [symbolic] +// CHECK:STDOUT: %F.specific_fn.bd1: = specific_function %F, @F(%T.binding.as_type, %facet_value.f04) [symbolic] +// CHECK:STDOUT: %facet_value.137: %type = facet_value %U.336, () [symbolic] +// CHECK:STDOUT: %F.specific_fn.76a: = specific_function %F, @F(%T.binding.as_type, %facet_value.137) [symbolic] +// CHECK:STDOUT: %F.specific_fn.685: = specific_function %F, @F(%T.binding.as_type, %V) [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %Empty.decl: %Empty.type = constraint_decl @Empty [concrete = constants.%empty_struct] { +// CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: %T.loc16_18.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc16_18.1 (constants.%T.8b3)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 0 [concrete] +// CHECK:STDOUT: %T.patt: %pattern_type.e25 = symbolic_binding_pattern T, 1 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: %U.loc18_6.2: type = bind_symbolic_name U, 0 [symbolic = %U.loc18_6.1 (constants.%U.8b3)] +// CHECK:STDOUT: %.loc18: type = splice_block %type [concrete = constants.%type] { +// CHECK:STDOUT: +// CHECK:STDOUT: %Empty.ref: %Empty.type = name_ref Empty, file.%Empty.decl [concrete = constants.%empty_struct] +// CHECK:STDOUT: %U.ref: type = name_ref U, %U.loc18_6.2 [symbolic = %U.loc18_6.1 (constants.%U.8b3)] +// CHECK:STDOUT: %type: type = facet_type [concrete = constants.%type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %T.loc18_16.2: %type = bind_symbolic_name T, 1 [symbolic = %T.loc18_16.1 (constants.%T.eca)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %T.patt: %pattern_type.4a0 = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] +// CHECK:STDOUT: %V.patt: %pattern_type.e25 = symbolic_binding_pattern V, 2 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc20_10: type = splice_block %Z.ref [concrete = constants.%Z.type] { +// CHECK:STDOUT: +// CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %T.loc20_6.2: %Z.type = bind_symbolic_name T, 0 [symbolic = %T.loc20_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: +// CHECK:STDOUT: %U.loc20_13.2: type = bind_symbolic_name U, 1 [symbolic = %U.loc20_13.1 (constants.%U.336)] +// CHECK:STDOUT: %.loc20_34.1: type = splice_block %type [concrete = constants.%type] { +// CHECK:STDOUT: +// CHECK:STDOUT: %Empty.ref: %Empty.type = name_ref Empty, file.%Empty.decl [concrete = constants.%empty_struct] +// CHECK:STDOUT: %T.ref.loc20: %Z.type = name_ref T, %T.loc20_6.2 [symbolic = %T.loc20_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: %T.as_type.loc20: type = facet_access_type %T.ref.loc20 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %.loc20_34.2: type = converted %T.ref.loc20, %T.as_type.loc20 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %type: type = facet_type [concrete = constants.%type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %V.loc20_23.2: %type = bind_symbolic_name V, 2 [symbolic = %V.loc20_23.1 (constants.%V)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic constraint @Empty(%T.loc16_18.2: type) { +// CHECK:STDOUT: %T.loc16_18.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc16_18.1 (constants.%T.8b3)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: +// CHECK:STDOUT: constraint { +// CHECK:STDOUT: +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @F(%U.loc18_6.2: type, %T.loc18_16.2: %type) { +// CHECK:STDOUT: %U.loc18_6.1: type = bind_symbolic_name U, 0 [symbolic = %U.loc18_6.1 (constants.%U.8b3)] +// CHECK:STDOUT: %T.loc18_16.1: %type = bind_symbolic_name T, 1 [symbolic = %T.loc18_16.1 (constants.%T.eca)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: fn() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @G(%T.loc20_6.2: %Z.type, %U.loc20_13.2: type, %V.loc20_23.2: %type) { +// CHECK:STDOUT: %T.loc20_6.1: %Z.type = bind_symbolic_name T, 0 [symbolic = %T.loc20_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: %U.loc20_13.1: type = bind_symbolic_name U, 1 [symbolic = %U.loc20_13.1 (constants.%U.336)] +// CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc20_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %V.loc20_23.1: %type = bind_symbolic_name V, 2 [symbolic = %V.loc20_23.1 (constants.%V)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %F.specific_fn.loc21_3.2: = specific_function constants.%F, @F(%T.binding.as_type, constants.%facet_value.6fa) [symbolic = %F.specific_fn.loc21_3.2 (constants.%F.specific_fn.8f2)] +// CHECK:STDOUT: %F.specific_fn.loc22_3.2: = specific_function constants.%F, @F(%T.binding.as_type, constants.%facet_value.832) [symbolic = %F.specific_fn.loc22_3.2 (constants.%F.specific_fn.d97)] +// CHECK:STDOUT: %facet_value.loc23_9.2: %type = facet_value %T.binding.as_type, () [symbolic = %facet_value.loc23_9.2 (constants.%facet_value.f04)] +// CHECK:STDOUT: %F.specific_fn.loc23_3.2: = specific_function constants.%F, @F(%T.binding.as_type, %facet_value.loc23_9.2) [symbolic = %F.specific_fn.loc23_3.2 (constants.%F.specific_fn.bd1)] +// CHECK:STDOUT: %facet_value.loc24_9.2: %type = facet_value %U.loc20_13.1, () [symbolic = %facet_value.loc24_9.2 (constants.%facet_value.137)] +// CHECK:STDOUT: %F.specific_fn.loc24_3.2: = specific_function constants.%F, @F(%T.binding.as_type, %facet_value.loc24_9.2) [symbolic = %F.specific_fn.loc24_3.2 (constants.%F.specific_fn.76a)] +// CHECK:STDOUT: %F.specific_fn.loc25_3.2: = specific_function constants.%F, @F(%T.binding.as_type, %V.loc20_23.1) [symbolic = %F.specific_fn.loc25_3.2 (constants.%F.specific_fn.685)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref.loc21: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %T.ref.loc21: %Z.type = name_ref T, %T.loc20_6.2 [symbolic = %T.loc20_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: %.loc21_9: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %T.as_type.loc21: type = facet_access_type %T.ref.loc21 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %.loc21_10.1: type = converted %T.ref.loc21, %T.as_type.loc21 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %facet_value.loc21: %type = facet_value constants.%empty_struct_type, () [concrete = constants.%facet_value.6fa] +// CHECK:STDOUT: %.loc21_10.2: %type = converted %.loc21_9, %facet_value.loc21 [concrete = constants.%facet_value.6fa] +// CHECK:STDOUT: %F.specific_fn.loc21_3.1: = specific_function %F.ref.loc21, @F(constants.%T.binding.as_type, constants.%facet_value.6fa) [symbolic = %F.specific_fn.loc21_3.2 (constants.%F.specific_fn.8f2)] +// CHECK:STDOUT: %F.call.loc21: init %empty_tuple.type = call %F.specific_fn.loc21_3.1() +// CHECK:STDOUT: %F.ref.loc22: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %T.ref.loc22: %Z.type = name_ref T, %T.loc20_6.2 [symbolic = %T.loc20_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: %T.as_type.loc22: type = facet_access_type %T.ref.loc22 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %.loc22_12.1: type = converted %T.ref.loc22, %T.as_type.loc22 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %facet_value.loc22: %type = facet_value type, () [concrete = constants.%facet_value.832] +// CHECK:STDOUT: %.loc22_12.2: %type = converted type, %facet_value.loc22 [concrete = constants.%facet_value.832] +// CHECK:STDOUT: %F.specific_fn.loc22_3.1: = specific_function %F.ref.loc22, @F(constants.%T.binding.as_type, constants.%facet_value.832) [symbolic = %F.specific_fn.loc22_3.2 (constants.%F.specific_fn.d97)] +// CHECK:STDOUT: %F.call.loc22: init %empty_tuple.type = call %F.specific_fn.loc22_3.1() +// CHECK:STDOUT: %F.ref.loc23: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %T.ref.loc23_5: %Z.type = name_ref T, %T.loc20_6.2 [symbolic = %T.loc20_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: %T.ref.loc23_8: %Z.type = name_ref T, %T.loc20_6.2 [symbolic = %T.loc20_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: %T.as_type.loc23_9.1: type = facet_access_type %T.ref.loc23_5 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %.loc23_9.1: type = converted %T.ref.loc23_5, %T.as_type.loc23_9.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %T.as_type.loc23_9.2: type = facet_access_type %T.ref.loc23_8 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %facet_value.loc23_9.1: %type = facet_value %T.as_type.loc23_9.2, () [symbolic = %facet_value.loc23_9.2 (constants.%facet_value.f04)] +// CHECK:STDOUT: %.loc23_9.2: %type = converted %T.ref.loc23_8, %facet_value.loc23_9.1 [symbolic = %facet_value.loc23_9.2 (constants.%facet_value.f04)] +// CHECK:STDOUT: %F.specific_fn.loc23_3.1: = specific_function %F.ref.loc23, @F(constants.%T.binding.as_type, constants.%facet_value.f04) [symbolic = %F.specific_fn.loc23_3.2 (constants.%F.specific_fn.bd1)] +// CHECK:STDOUT: %F.call.loc23: init %empty_tuple.type = call %F.specific_fn.loc23_3.1() +// CHECK:STDOUT: %F.ref.loc24: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %T.ref.loc24: %Z.type = name_ref T, %T.loc20_6.2 [symbolic = %T.loc20_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: %U.ref: type = name_ref U, %U.loc20_13.2 [symbolic = %U.loc20_13.1 (constants.%U.336)] +// CHECK:STDOUT: %T.as_type.loc24: type = facet_access_type %T.ref.loc24 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %.loc24_9.1: type = converted %T.ref.loc24, %T.as_type.loc24 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %facet_value.loc24_9.1: %type = facet_value %U.ref, () [symbolic = %facet_value.loc24_9.2 (constants.%facet_value.137)] +// CHECK:STDOUT: %.loc24_9.2: %type = converted %U.ref, %facet_value.loc24_9.1 [symbolic = %facet_value.loc24_9.2 (constants.%facet_value.137)] +// CHECK:STDOUT: %F.specific_fn.loc24_3.1: = specific_function %F.ref.loc24, @F(constants.%T.binding.as_type, constants.%facet_value.137) [symbolic = %F.specific_fn.loc24_3.2 (constants.%F.specific_fn.76a)] +// CHECK:STDOUT: %F.call.loc24: init %empty_tuple.type = call %F.specific_fn.loc24_3.1() +// CHECK:STDOUT: %F.ref.loc25: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %T.ref.loc25: %Z.type = name_ref T, %T.loc20_6.2 [symbolic = %T.loc20_6.1 (constants.%T.1d7)] +// CHECK:STDOUT: %V.ref: %type = name_ref V, %V.loc20_23.2 [symbolic = %V.loc20_23.1 (constants.%V)] +// CHECK:STDOUT: %T.as_type.loc25: type = facet_access_type %T.ref.loc25 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %.loc25: type = converted %T.ref.loc25, %T.as_type.loc25 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] +// CHECK:STDOUT: %F.specific_fn.loc25_3.1: = specific_function %F.ref.loc25, @F(constants.%T.binding.as_type, constants.%V) [symbolic = %F.specific_fn.loc25_3.2 (constants.%F.specific_fn.685)] +// CHECK:STDOUT: %F.call.loc25: init %empty_tuple.type = call %F.specific_fn.loc25_3.1() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Empty(constants.%T.8b3) { +// CHECK:STDOUT: %T.loc16_18.1 => constants.%T.8b3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Empty(constants.%U.8b3) { +// CHECK:STDOUT: %T.loc16_18.1 => constants.%U.8b3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%U.8b3, constants.%T.eca) { +// CHECK:STDOUT: %U.loc18_6.1 => constants.%U.8b3 +// CHECK:STDOUT: %T.loc18_16.1 => constants.%T.eca +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Empty(constants.%T.binding.as_type) { +// CHECK:STDOUT: %T.loc16_18.1 => constants.%T.binding.as_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @G(constants.%T.1d7, constants.%U.336, constants.%V) { +// CHECK:STDOUT: %T.loc20_6.1 => constants.%T.1d7 +// CHECK:STDOUT: %U.loc20_13.1 => constants.%U.336 +// CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type +// CHECK:STDOUT: %V.loc20_23.1 => constants.%V +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T.binding.as_type, constants.%facet_value.6fa) { +// CHECK:STDOUT: %U.loc18_6.1 => constants.%T.binding.as_type +// CHECK:STDOUT: %T.loc18_16.1 => constants.%facet_value.6fa +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T.binding.as_type, constants.%facet_value.832) { +// CHECK:STDOUT: %U.loc18_6.1 => constants.%T.binding.as_type +// CHECK:STDOUT: %T.loc18_16.1 => constants.%facet_value.832 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T.binding.as_type, constants.%facet_value.f04) { +// CHECK:STDOUT: %U.loc18_6.1 => constants.%T.binding.as_type +// CHECK:STDOUT: %T.loc18_16.1 => constants.%facet_value.f04 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T.binding.as_type, constants.%facet_value.137) { +// CHECK:STDOUT: %U.loc18_6.1 => constants.%T.binding.as_type +// CHECK:STDOUT: %T.loc18_16.1 => constants.%facet_value.137 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T.binding.as_type, constants.%V) { +// CHECK:STDOUT: %U.loc18_6.1 => constants.%T.binding.as_type +// CHECK:STDOUT: %T.loc18_16.1 => constants.%V +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/named_constraint/generic.carbon b/toolchain/check/testdata/named_constraint/generic.carbon new file mode 100644 index 000000000000..6c6be8177077 --- /dev/null +++ b/toolchain/check/testdata/named_constraint/generic.carbon @@ -0,0 +1,184 @@ +// 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/named_constraint/generic.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/named_constraint/generic.carbon + +// --- generic.carbon +library "[[@TEST_NAME]]"; +//@include-in-dumps + +constraint GenericType(T:! type) {} + +interface Z {} +constraint GenericZ(U:! Z) {} + +constraint ForwardDeclaredGeneric(T:! type); +constraint ForwardDeclaredGeneric(T:! type) {} + +// --- fail_mismatched_generic_param_types.carbon +library "[[@TEST_NAME]]"; + +constraint ForwardDeclaredGeneric(T:! ()); +// CHECK:STDERR: fail_mismatched_generic_param_types.carbon:[[@LINE+7]]:35: error: type `` of parameter 1 in redeclaration differs from previous parameter type `` [RedeclParamDiffersType] +// CHECK:STDERR: constraint ForwardDeclaredGeneric(T:! type) {} +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_mismatched_generic_param_types.carbon:[[@LINE-4]]:35: note: previous declaration's corresponding parameter here [RedeclParamPrevious] +// CHECK:STDERR: constraint ForwardDeclaredGeneric(T:! ()); +// CHECK:STDERR: ^ +// CHECK:STDERR: +constraint ForwardDeclaredGeneric(T:! type) {} + +// --- fail_mismatched_generic_param_arity.carbon +library "[[@TEST_NAME]]"; + +constraint ForwardDeclaredGeneric(T:! type, U:! type); +// CHECK:STDERR: fail_mismatched_generic_param_arity.carbon:[[@LINE+7]]:1: error: redeclaration differs because of parameter count of 1 [RedeclParamCountDiffers] +// CHECK:STDERR: constraint ForwardDeclaredGeneric(T:! type) {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_mismatched_generic_param_arity.carbon:[[@LINE-4]]:1: note: previously declared with parameter count of 2 [RedeclParamCountPrevious] +// CHECK:STDERR: constraint ForwardDeclaredGeneric(T:! type, U:! type); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +constraint ForwardDeclaredGeneric(T:! type) {} + +// --- fail_inside_definition.carbon +library "[[@TEST_NAME]]"; + +constraint Generic(T:! type) {} +// CHECK:STDERR: fail_inside_definition.carbon:[[@LINE+7]]:1: error: redefinition of `constraint Generic` [RedeclRedef] +// CHECK:STDERR: constraint Generic(T:! type) {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_inside_definition.carbon:[[@LINE-4]]:1: note: previously defined here [RedeclPrevDef] +// CHECK:STDERR: constraint Generic(T:! type) {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +constraint Generic(T:! type) {} + +// CHECK:STDOUT: --- generic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %.Self: %type = bind_symbolic_name .Self [symbolic_self] +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] +// CHECK:STDOUT: %GenericType.type: type = generic_named_constaint_type @GenericType [concrete] +// CHECK:STDOUT: %empty_struct.bd5: %GenericType.type = struct_value () [concrete] +// CHECK:STDOUT: %Self.eca39f.1: %type = bind_symbolic_name Self, 1 [symbolic] +// CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] +// CHECK:STDOUT: %Self.1d7: %Z.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %U: %Z.type = bind_symbolic_name U, 0 [symbolic] +// CHECK:STDOUT: %pattern_type.4a0: type = pattern_type %Z.type [concrete] +// CHECK:STDOUT: %GenericZ.type: type = generic_named_constaint_type @GenericZ [concrete] +// CHECK:STDOUT: %empty_struct.05e: %GenericZ.type = struct_value () [concrete] +// CHECK:STDOUT: %Self.eca39f.2: %type = bind_symbolic_name Self, 1 [symbolic] +// CHECK:STDOUT: %ForwardDeclaredGeneric.type: type = generic_named_constaint_type @ForwardDeclaredGeneric [concrete] +// CHECK:STDOUT: %empty_struct.1a7: %ForwardDeclaredGeneric.type = struct_value () [concrete] +// CHECK:STDOUT: %Self.eca39f.3: %type = bind_symbolic_name Self, 1 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .GenericType = %GenericType.decl +// CHECK:STDOUT: .Z = %Z.decl +// CHECK:STDOUT: .GenericZ = %GenericZ.decl +// CHECK:STDOUT: .ForwardDeclaredGeneric = %ForwardDeclaredGeneric.decl.loc9 +// CHECK:STDOUT: } +// CHECK:STDOUT: %GenericType.decl: %GenericType.type = constraint_decl @GenericType [concrete = constants.%empty_struct.bd5] { +// CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.Self: %type = bind_symbolic_name .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %T.loc4_24.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_24.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {} +// CHECK:STDOUT: %GenericZ.decl: %GenericZ.type = constraint_decl @GenericZ [concrete = constants.%empty_struct.05e] { +// CHECK:STDOUT: %U.patt: %pattern_type.4a0 = symbolic_binding_pattern U, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc7: type = splice_block %Z.ref [concrete = constants.%Z.type] { +// CHECK:STDOUT: %.Self: %type = bind_symbolic_name .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %U.loc7_21.2: %Z.type = bind_symbolic_name U, 0 [symbolic = %U.loc7_21.1 (constants.%U)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %ForwardDeclaredGeneric.decl.loc9: %ForwardDeclaredGeneric.type = constraint_decl @ForwardDeclaredGeneric [concrete = constants.%empty_struct.1a7] { +// CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.Self.2: %type = bind_symbolic_name .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %T.loc9_35.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_35.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %ForwardDeclaredGeneric.decl.loc10: %ForwardDeclaredGeneric.type = constraint_decl @ForwardDeclaredGeneric [concrete = constants.%empty_struct.1a7] { +// CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.Self.1: %type = bind_symbolic_name .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %T.loc10: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_35.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Z { +// CHECK:STDOUT: %Self: %Z.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.1d7] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic constraint @GenericType(%T.loc4_24.2: type) { +// CHECK:STDOUT: %T.loc4_24.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_24.1 (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %Self.2: %type = bind_symbolic_name Self, 1 [symbolic = %Self.2 (constants.%Self.eca39f.1)] +// CHECK:STDOUT: +// CHECK:STDOUT: constraint { +// CHECK:STDOUT: %Self.1: %type = bind_symbolic_name Self, 1 [symbolic = %Self.2 (constants.%Self.eca39f.1)] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic constraint @GenericZ(%U.loc7_21.2: %Z.type) { +// CHECK:STDOUT: %U.loc7_21.1: %Z.type = bind_symbolic_name U, 0 [symbolic = %U.loc7_21.1 (constants.%U)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %Self.2: %type = bind_symbolic_name Self, 1 [symbolic = %Self.2 (constants.%Self.eca39f.2)] +// CHECK:STDOUT: +// CHECK:STDOUT: constraint { +// CHECK:STDOUT: %Self.1: %type = bind_symbolic_name Self, 1 [symbolic = %Self.2 (constants.%Self.eca39f.2)] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic constraint @ForwardDeclaredGeneric(%T.loc9_35.2: type) { +// CHECK:STDOUT: %T.loc9_35.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_35.1 (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %Self.2: %type = bind_symbolic_name Self, 1 [symbolic = %Self.2 (constants.%Self.eca39f.3)] +// CHECK:STDOUT: +// CHECK:STDOUT: constraint { +// CHECK:STDOUT: %Self.1: %type = bind_symbolic_name Self, 1 [symbolic = %Self.2 (constants.%Self.eca39f.3)] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @GenericType(constants.%T) { +// CHECK:STDOUT: %T.loc4_24.1 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @GenericZ(constants.%U) { +// CHECK:STDOUT: %U.loc7_21.1 => constants.%U +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @ForwardDeclaredGeneric(constants.%T) { +// CHECK:STDOUT: %T.loc9_35.1 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/named_constraint/invalid_members.carbon b/toolchain/check/testdata/named_constraint/invalid_members.carbon new file mode 100644 index 000000000000..b7addc259add --- /dev/null +++ b/toolchain/check/testdata/named_constraint/invalid_members.carbon @@ -0,0 +1,74 @@ +// 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/named_constraint/invalid_members.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/named_constraint/invalid_members.carbon + +// --- todo_fail_invalid_fn.carbon +library "[[@TEST_NAME]]"; + +constraint E { + // TODO: Any fn in a non-template constraint is an error. + fn DeclaredFunction(); + fn DeclaredMethod[self: Self](b: Self); +} + +constraint F { + // TODO: Any fn in a non-template constraint is an error. + fn DefinedFunction() {} + fn DefinedMethod[self: Self](b: Self) {} +} + +// --- todo_fail_invalid_var.carbon +library "[[@TEST_NAME]]"; + +constraint E { + // TODO: Any var in a constraint is an error. + var c: (); +} + +// --- fail_todo_invalid_var_template.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_todo_invalid_var_template.carbon:[[@LINE+8]]:1: error: unrecognized declaration introducer [UnrecognizedDecl] +// CHECK:STDERR: template constraint E { +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_todo_invalid_var_template.carbon:[[@LINE+4]]:1: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] +// CHECK:STDERR: template constraint E { +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +template constraint E { + // TODO: Any var in a constraint is an error. + var c: (); +} + +// --- todo_fail_invalid_let.carbon +library "[[@TEST_NAME]]"; + +constraint E { + // TODO: Any let in a constraint is an error. + var c: (); +} + +// --- fail_todo_invalid_let_template.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_todo_invalid_let_template.carbon:[[@LINE+8]]:1: error: unrecognized declaration introducer [UnrecognizedDecl] +// CHECK:STDERR: template constraint E { +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_todo_invalid_let_template.carbon:[[@LINE+4]]:1: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] +// CHECK:STDERR: template constraint E { +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +template constraint E { + // TODO: Any let in a constraint is an error. + var c: (); +} diff --git a/toolchain/check/type.cpp b/toolchain/check/type.cpp index e18087617c36..bda36b7f77b8 100644 --- a/toolchain/check/type.cpp +++ b/toolchain/check/type.cpp @@ -204,6 +204,14 @@ auto GetGenericInterfaceType(Context& context, SemIR::InterfaceId interface_id, context, interface_id, enclosing_specific_id); } +auto GetGenericNamedConstraintType(Context& context, + SemIR::NamedConstraintId named_constraint_id, + SemIR::SpecificId enclosing_specific_id) + -> SemIR::TypeId { + return GetCompleteTypeImpl( + context, named_constraint_id, enclosing_specific_id); +} + auto GetInterfaceType(Context& context, SemIR::InterfaceId interface_id, SemIR::SpecificId specific_id) -> SemIR::TypeId { return GetTypeImpl( @@ -211,6 +219,15 @@ auto GetInterfaceType(Context& context, SemIR::InterfaceId interface_id, FacetTypeFromInterface(context, interface_id, specific_id).facet_type_id); } +auto GetNamedConstraintType(Context& context, + SemIR::NamedConstraintId named_constraint_id, + SemIR::SpecificId specific_id) -> SemIR::TypeId { + return GetTypeImpl( + context, + FacetTypeFromNamedConstraint(context, named_constraint_id, specific_id) + .facet_type_id); +} + auto GetFacetType(Context& context, const SemIR::FacetTypeInfo& info) -> SemIR::TypeId { return GetTypeImpl(context, diff --git a/toolchain/check/type.h b/toolchain/check/type.h index e3546b5efb66..7645dee6ffcd 100644 --- a/toolchain/check/type.h +++ b/toolchain/check/type.h @@ -79,10 +79,23 @@ auto GetGenericInterfaceType(Context& context, SemIR::InterfaceId interface_id, SemIR::SpecificId enclosing_specific_id) -> SemIR::TypeId; +// Gets a generic named constraint type, which is the type of a name of a +// generic named constraint, such as the type of `AddWith` given `constraint +// AddWith(T:! type)`. The returned type will be complete. +auto GetGenericNamedConstraintType(Context& context, + SemIR::NamedConstraintId named_constraint_id, + SemIR::SpecificId enclosing_specific_id) + -> SemIR::TypeId; + // Gets the facet type corresponding to a particular interface. auto GetInterfaceType(Context& context, SemIR::InterfaceId interface_id, SemIR::SpecificId specific_id) -> SemIR::TypeId; +// Gets the facet type corresponding to a particular named constraint. +auto GetNamedConstraintType(Context& context, + SemIR::NamedConstraintId named_constraint_id, + SemIR::SpecificId specific_id) -> SemIR::TypeId; + // Gets the facet type for the given `info`. auto GetFacetType(Context& context, const SemIR::FacetTypeInfo& info) -> SemIR::TypeId; diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 98de1241fde3..d2d5410df1e4 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -125,7 +125,8 @@ class TypeCompleter { SemIR::AssociatedEntityType, SemIR::CppOverloadSetType, SemIR::FunctionType, SemIR::FunctionTypeWithSelfType, SemIR::GenericClassType, SemIR::GenericInterfaceType, - SemIR::InstType, SemIR::UnboundElementType, SemIR::WhereExpr>()) + SemIR::GenericNamedConstraintType, SemIR::InstType, + SemIR::UnboundElementType, SemIR::WhereExpr>()) auto BuildInfoForInst(SemIR::TypeId /*type_id*/, InstT /*inst*/) const -> SemIR::CompleteTypeInfo { // These types have no runtime operations, so we use an empty value diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 68818f5ab80b..eee26b68846b 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -915,8 +915,8 @@ template SemIR::AssociatedEntityType, SemIR::CppOverloadSetType, SemIR::FacetType, SemIR::FunctionType, SemIR::FunctionTypeWithSelfType, SemIR::GenericClassType, - SemIR::GenericInterfaceType, SemIR::InstType, - SemIR::UnboundElementType, SemIR::WhereExpr>()) + SemIR::GenericInterfaceType, SemIR::GenericNamedConstraintType, + SemIR::InstType, SemIR::UnboundElementType, SemIR::WhereExpr>()) static auto BuildTypeForInst(FileContext& context, InstT /*inst*/) -> llvm::Type* { // Return an empty struct as a placeholder. diff --git a/toolchain/parse/node_ids.h b/toolchain/parse/node_ids.h index 50702575dea9..1cf2255e48e8 100644 --- a/toolchain/parse/node_ids.h +++ b/toolchain/parse/node_ids.h @@ -162,6 +162,8 @@ using AnyFunctionDeclId = NodeIdOneOf; using AnyInterfaceDeclId = NodeIdOneOf; +using AnyNamedConstraintDeclId = + NodeIdOneOf; using AnyNamespaceId = NodeIdOneOf; using AnyPackagingDeclId = diff --git a/toolchain/parse/testdata/generics/named_constraint/basic.carbon b/toolchain/parse/testdata/generics/named_constraint/basic.carbon index c2c4a76ab3de..eedb08bfbe81 100644 --- a/toolchain/parse/testdata/generics/named_constraint/basic.carbon +++ b/toolchain/parse/testdata/generics/named_constraint/basic.carbon @@ -8,24 +8,41 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/named_constraint/basic.carbon -constraint Foo { - fn Baz(); +// --- empty.carbon + +constraint Empty {} + +// --- with_require.carbon + +constraint WithRequire { + // TODO: Add a require statement. } +// --- forward_decl.carbon + constraint ForwardDeclared; -// CHECK:STDOUT: - filename: basic.carbon +// CHECK:STDOUT: - filename: empty.carbon // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'NamedConstraintIntroducer', text: 'constraint'}, -// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'Empty'}, // CHECK:STDOUT: {kind: 'NamedConstraintDefinitionStart', text: '{', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, -// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'Baz'}, -// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('}, -// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'NamedConstraintDefinition', text: '}', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'NamedConstraintDefinition', text: '}', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: with_require.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'NamedConstraintIntroducer', text: 'constraint'}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'WithRequire'}, +// CHECK:STDOUT: {kind: 'NamedConstraintDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NamedConstraintDefinition', text: '}', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: forward_decl.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'NamedConstraintIntroducer', text: 'constraint'}, // CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'ForwardDeclared'}, // CHECK:STDOUT: {kind: 'NamedConstraintDecl', text: ';', subtree_size: 3}, diff --git a/toolchain/parse/testdata/generics/named_constraint/defined_method.carbon b/toolchain/parse/testdata/generics/named_constraint/invalid_method.carbon similarity index 54% rename from toolchain/parse/testdata/generics/named_constraint/defined_method.carbon rename to toolchain/parse/testdata/generics/named_constraint/invalid_method.carbon index 8f55eb5bbef2..8dd102fe1000 100644 --- a/toolchain/parse/testdata/generics/named_constraint/defined_method.carbon +++ b/toolchain/parse/testdata/generics/named_constraint/invalid_method.carbon @@ -4,22 +4,47 @@ // // AUTOUPDATE // TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/generics/named_constraint/defined_method.carbon +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/generics/named_constraint/invalid_method.carbon // TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/named_constraint/defined_method.carbon +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/named_constraint/invalid_method.carbon -constraint Foo { - fn Add[self: Self](b: Self) -> Self {} +constraint E { + // This is an error in the check phase. + fn DeclaredMethod[self: Self](b: Self) -> Self; } -// CHECK:STDOUT: - filename: defined_method.carbon +constraint Foo { + // This is an error in the check phase. + fn DefinedMethod[self: Self](b: Self) -> Self {} +} + +// CHECK:STDOUT: - filename: invalid_method.carbon // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'NamedConstraintIntroducer', text: 'constraint'}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'E'}, +// CHECK:STDOUT: {kind: 'NamedConstraintDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'DeclaredMethod'}, +// CHECK:STDOUT: {kind: 'ImplicitParamListStart', text: '['}, +// CHECK:STDOUT: {kind: 'SelfValueName', text: 'self'}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, +// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ImplicitParamList', text: ']', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'b'}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, +// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'NamedConstraintDefinition', text: '}', subtree_size: 19}, +// CHECK:STDOUT: {kind: 'NamedConstraintIntroducer', text: 'constraint'}, // CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'Foo'}, // CHECK:STDOUT: {kind: 'NamedConstraintDefinitionStart', text: '{', subtree_size: 3}, // CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, -// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'Add'}, +// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'DefinedMethod'}, // CHECK:STDOUT: {kind: 'ImplicitParamListStart', text: '['}, // CHECK:STDOUT: {kind: 'SelfValueName', text: 'self'}, // CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'}, diff --git a/toolchain/parse/testdata/generics/named_constraint/template_constraint.carbon b/toolchain/parse/testdata/generics/named_constraint/template_constraint.carbon new file mode 100644 index 000000000000..edee27a093d1 --- /dev/null +++ b/toolchain/parse/testdata/generics/named_constraint/template_constraint.carbon @@ -0,0 +1,37 @@ +// 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/parse/testdata/generics/named_constraint/template_constraint.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/named_constraint/template_constraint.carbon + +// --- fail_todo_template_constraint.carbon + +// CHECK:STDERR: fail_todo_template_constraint.carbon:[[@LINE+4]]:1: error: unrecognized declaration introducer [UnrecognizedDecl] +// CHECK:STDERR: template constraint Foo { +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +template constraint Foo { + // TODO: Add a require statement. + fn DeclaredMethod(); + fn DefinedMethod() {} +} + +// CHECK:STDERR: fail_todo_template_constraint.carbon:[[@LINE+4]]:1: error: unrecognized declaration introducer [UnrecognizedDecl] +// CHECK:STDERR: template constraint ForwardDeclared; +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +template constraint ForwardDeclared; + +// CHECK:STDOUT: - filename: fail_todo_template_constraint.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'InvalidParseStart', text: 'template', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParseSubtree', text: '}', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'InvalidParseStart', text: 'template', has_error: yes}, +// CHECK:STDOUT: {kind: 'InvalidParseSubtree', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 2634610ee544..3faec327c58b 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -102,6 +102,7 @@ cc_library( "interface.h", "name.h", "name_scope.h", + "named_constraint.h", "pattern.h", "struct_type_field.h", "type.h", diff --git a/toolchain/sem_ir/expr_info.cpp b/toolchain/sem_ir/expr_info.cpp index 4be902a4728e..08ba96446731 100644 --- a/toolchain/sem_ir/expr_info.cpp +++ b/toolchain/sem_ir/expr_info.cpp @@ -126,6 +126,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case FunctionTypeWithSelfType::Kind: case GenericClassType::Kind: case GenericInterfaceType::Kind: + case GenericNamedConstraintType::Kind: case LookupImplWitness::Kind: case ImplWitness::Kind: case ImplWitnessAccess::Kind: @@ -141,6 +142,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case IntValue::Kind: case InterfaceDecl::Kind: case MaybeUnformedType::Kind: + case NamedConstraintDecl::Kind: case NamespaceType::Kind: case PartialType::Kind: case PatternType::Kind: diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index fd15ef275ee2..137fe3c2c011 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -35,6 +35,7 @@ #include "toolchain/sem_ir/interface.h" #include "toolchain/sem_ir/name.h" #include "toolchain/sem_ir/name_scope.h" +#include "toolchain/sem_ir/named_constraint.h" #include "toolchain/sem_ir/singleton_insts.h" #include "toolchain/sem_ir/specific_interface.h" #include "toolchain/sem_ir/struct_type_field.h" @@ -171,6 +172,12 @@ class File : public Printable { auto classes() const -> const ClassStore& { return classes_; } auto interfaces() -> InterfaceStore& { return interfaces_; } auto interfaces() const -> const InterfaceStore& { return interfaces_; } + auto named_constraints() -> NamedConstraintStore& { + return named_constraints_; + } + auto named_constraints() const -> const NamedConstraintStore& { + return named_constraints_; + } auto associated_constants() -> AssociatedConstantStore& { return associated_constants_; } @@ -321,6 +328,9 @@ class File : public Printable { // Storage for interfaces. InterfaceStore interfaces_; + // Storage for named constraints. + NamedConstraintStore named_constraints_; + // Storage for associated constants. AssociatedConstantStore associated_constants_; diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 399f305f71a1..4c8cf15d7b37 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -80,6 +80,11 @@ auto Formatter::Format() -> void { FormatInterface(id, interface); } + for (const auto& [id, constraint] : + sem_ir_->named_constraints().enumerate()) { + FormatNamedConstraint(id, constraint); + } + for (const auto& [id, assoc_const] : sem_ir_->associated_constants().enumerate()) { FormatAssociatedConstant(id, assoc_const); @@ -417,6 +422,39 @@ auto Formatter::FormatInterface(InterfaceId id, const Interface& interface_info) FormatEntityEnd(interface_info.generic_id); } +auto Formatter::FormatNamedConstraint(NamedConstraintId id, + const NamedConstraint& constraint_info) + -> void { + if (!ShouldFormatEntity(constraint_info)) { + return; + } + + FormatEntityStart("constraint", constraint_info, id); + + llvm::SaveAndRestore constraint_scope(scope_, inst_namer_.GetScopeFor(id)); + + if (constraint_info.scope_id.has_value()) { + out_ << ' '; + OpenBrace(); + FormatCodeBlock(constraint_info.body_block_id); + + // Always include the !members label because we always list the witness in + // this section. + IndentLabel(); + out_ << "!members:\n"; + FormatNameScope(constraint_info.scope_id); + + // TODO: Print `require` statements. + + CloseBrace(); + } else { + Semicolon(); + } + out_ << '\n'; + + FormatEntityEnd(constraint_info.generic_id); +} + auto Formatter::FormatAssociatedConstant(AssociatedConstantId id, const AssociatedConstant& assoc_const) -> void { @@ -1084,6 +1122,15 @@ auto Formatter::FormatInstRhs(Inst inst) -> void { return; } + case CARBON_KIND(NamedConstraintDecl decl): { + FormatDeclRhs(decl.named_constraint_id, + sem_ir_->named_constraints() + .Get(decl.named_constraint_id) + .pattern_block_id, + decl.decl_block_id); + return; + } + case CARBON_KIND(Namespace ns): { if (ns.import_id.has_value()) { FormatArgs(ns.import_id, ns.name_scope_id); diff --git a/toolchain/sem_ir/formatter.h b/toolchain/sem_ir/formatter.h index 76221c878a60..dfa7e2800b6e 100644 --- a/toolchain/sem_ir/formatter.h +++ b/toolchain/sem_ir/formatter.h @@ -158,6 +158,10 @@ class Formatter { // Formats a full interface. auto FormatInterface(InterfaceId id, const Interface& interface_info) -> void; + // Formats a full named constraint. + auto FormatNamedConstraint(NamedConstraintId id, + const NamedConstraint& constraint_info) -> void; + // Formats an associated constant entity. auto FormatAssociatedConstant(AssociatedConstantId id, const AssociatedConstant& assoc_const) -> void; diff --git a/toolchain/sem_ir/id_kind.h b/toolchain/sem_ir/id_kind.h index f24b1477fbe3..cea3cb97286f 100644 --- a/toolchain/sem_ir/id_kind.h +++ b/toolchain/sem_ir/id_kind.h @@ -60,6 +60,7 @@ using IdKind = TypeEnum< MetaInstId, NameId, NameScopeId, + NamedConstraintId, SpecificId, SpecificInterfaceId, StructTypeFieldsId, diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 7ada776d77f8..99bbebc32481 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -327,6 +327,13 @@ struct InterfaceId : public IdBase { using IdBase::IdBase; }; +// The ID of a `NamedConstraint`. +struct NamedConstraintId : public IdBase { + static constexpr llvm::StringLiteral Label = "constraint"; + + using IdBase::IdBase; +}; + // The ID of an `AssociatedConstant`. struct AssociatedConstantId : public IdBase { static constexpr llvm::StringLiteral Label = "assoc_const"; diff --git a/toolchain/sem_ir/inst_fingerprinter.cpp b/toolchain/sem_ir/inst_fingerprinter.cpp index 4858ae00de98..a43a029b37f7 100644 --- a/toolchain/sem_ir/inst_fingerprinter.cpp +++ b/toolchain/sem_ir/inst_fingerprinter.cpp @@ -238,6 +238,10 @@ struct Worklist { AddEntity(sem_ir->interfaces().Get(interface_id)); } + auto Add(NamedConstraintId named_constraint_id) -> void { + AddEntity(sem_ir->named_constraints().Get(named_constraint_id)); + } + auto Add(AssociatedConstantId assoc_const_id) -> void { AddEntity( sem_ir->associated_constants().Get(assoc_const_id)); diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 6c041cbeda77..639a9e285788 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -76,6 +76,7 @@ CARBON_SEM_IR_INST_KIND(FunctionType) CARBON_SEM_IR_INST_KIND(FunctionTypeWithSelfType) CARBON_SEM_IR_INST_KIND(GenericClassType) CARBON_SEM_IR_INST_KIND(GenericInterfaceType) +CARBON_SEM_IR_INST_KIND(GenericNamedConstraintType) CARBON_SEM_IR_INST_KIND(ImplDecl) CARBON_SEM_IR_INST_KIND(ImplWitness) CARBON_SEM_IR_INST_KIND(ImplWitnessAccess) @@ -98,6 +99,7 @@ CARBON_SEM_IR_INST_KIND(InterfaceDecl) CARBON_SEM_IR_INST_KIND(LookupImplWitness) CARBON_SEM_IR_INST_KIND(MaybeUnformedType) CARBON_SEM_IR_INST_KIND(NameBindingDecl) +CARBON_SEM_IR_INST_KIND(NamedConstraintDecl) CARBON_SEM_IR_INST_KIND(NameRef) CARBON_SEM_IR_INST_KIND(Namespace) CARBON_SEM_IR_INST_KIND(NamespaceType) diff --git a/toolchain/sem_ir/inst_namer.cpp b/toolchain/sem_ir/inst_namer.cpp index eaabe4cf40d7..ffa697c2a0ac 100644 --- a/toolchain/sem_ir/inst_namer.cpp +++ b/toolchain/sem_ir/inst_namer.cpp @@ -155,6 +155,10 @@ auto InstNamer::GetScopeIdOffset(ScopeIdTypeEnum id_enum) const -> int { [[fallthrough]]; case ScopeIdTypeEnum::For: + offset += sem_ir_->named_constraints().size(); + [[fallthrough]]; + case ScopeIdTypeEnum::For: + offset += sem_ir_->specific_interfaces().size(); [[fallthrough]]; case ScopeIdTypeEnum::For: @@ -546,6 +550,9 @@ auto InstNamer::GetNameForParentNameScope(NameScopeId name_scope_id) case CARBON_KIND(InterfaceDecl interface): { return MaybePushEntity(interface.interface_id); } + case CARBON_KIND(NamedConstraintDecl named_constraint): { + return MaybePushEntity(named_constraint.named_constraint_id); + } case SemIR::Namespace::Kind: { // Only prefix type scopes. return ""; @@ -645,6 +652,23 @@ auto InstNamer::PushEntity(InterfaceId interface_id, ScopeId scope_id, PushBlockId(scope_id, interface.pattern_block_id); } +auto InstNamer::PushEntity(NamedConstraintId named_constraint_id, + ScopeId scope_id, Scope& scope) -> void { + const auto& constraint = + sem_ir_->named_constraints().Get(named_constraint_id); + LocId constraint_loc(constraint.latest_decl_id()); + scope.name = globals_.AllocateName( + *this, constraint_loc, + sem_ir_->names().GetIRBaseName(constraint.name_id).str()); + AddBlockLabel(scope_id, constraint.body_block_id, "constraint", + constraint_loc); + + // Push blocks in reverse order. + PushGeneric(scope_id, constraint.generic_id); + PushBlockId(scope_id, constraint.body_block_id); + PushBlockId(scope_id, constraint.pattern_block_id); +} + auto InstNamer::PushEntity(VtableId vtable_id, ScopeId /*scope_id*/, Scope& scope) -> void { const auto& vtable = sem_ir_->vtables().Get(vtable_id); @@ -924,6 +948,10 @@ auto InstNamer::NamingContext::NameInst() -> void { AddEntityNameAndMaybePush(inst.interface_id, ".type"); return; } + case CARBON_KIND(GenericNamedConstraintType inst): { + AddEntityNameAndMaybePush(inst.named_constraint_id, ".type"); + return; + } case CARBON_KIND(ImplDecl inst): { // `impl` declarations aren't named because they aren't added to any // namespace, and so aren't referenced directly. @@ -1046,6 +1074,13 @@ auto InstNamer::NamingContext::NameInst() -> void { PushBlockId(scope_id_, inst.pattern_block_id); return; } + case CARBON_KIND(NamedConstraintDecl inst): { + AddEntityNameAndMaybePush(inst.named_constraint_id, ".decl"); + auto interface_scope_id = + inst_namer_->GetScopeFor(inst.named_constraint_id); + PushBlockId(interface_scope_id, inst.decl_block_id); + return; + } case CARBON_KIND(NameRef inst): { AddInstNameId(inst.name_id, ".ref"); return; diff --git a/toolchain/sem_ir/inst_namer.h b/toolchain/sem_ir/inst_namer.h index 089641dfb12e..27b21cf14011 100644 --- a/toolchain/sem_ir/inst_namer.h +++ b/toolchain/sem_ir/inst_namer.h @@ -39,7 +39,8 @@ class InstNamer { // Entities whose scopes get entries from `ScopeId`. using ScopeIdTypeEnum = TypeEnum; + ImplId, InterfaceId, NamedConstraintId, SpecificInterfaceId, + VtableId>; // Construct the instruction namer, and assign names to all instructions in // the provided file. @@ -218,6 +219,8 @@ class InstNamer { auto PushEntity(ImplId impl_id, ScopeId scope_id, Scope& scope) -> void; auto PushEntity(InterfaceId interface_id, ScopeId scope_id, Scope& scope) -> void; + auto PushEntity(NamedConstraintId named_constraint_id, ScopeId scope_id, + Scope& scope) -> void; auto PushEntity(VtableId vtable_id, ScopeId scope_id, Scope& scope) -> void; // Always returns the name of the entity. May push it if it has not yet been diff --git a/toolchain/sem_ir/named_constraint.h b/toolchain/sem_ir/named_constraint.h new file mode 100644 index 000000000000..65fd0d5748bb --- /dev/null +++ b/toolchain/sem_ir/named_constraint.h @@ -0,0 +1,54 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_SEM_IR_NAMED_CONSTRAINT_H_ +#define CARBON_TOOLCHAIN_SEM_IR_NAMED_CONSTRAINT_H_ + +#include "toolchain/base/value_store.h" +#include "toolchain/sem_ir/entity_with_params_base.h" +#include "toolchain/sem_ir/ids.h" + +namespace Carbon::SemIR { + +// Named constraint-specific fields. +struct NamedConstraintFields { + // The following members are set at the `{` of the constraint definition. + + // The constraint scope. + NameScopeId scope_id = NameScopeId::None; + // The first block of the constraint body. + InstBlockId body_block_id = InstBlockId::None; + // The implicit `Self` parameter. This is a BindSymbolicName instruction. + InstId self_param_id = InstId::None; + + // The following members are set at the `}` of the constraint definition. + + bool complete = false; +}; + +// A named constraint. See EntityWithParamsBase regarding the inheritance here. +struct NamedConstraint : public EntityWithParamsBase, + public NamedConstraintFields, + public Printable { + auto Print(llvm::raw_ostream& out) const -> void { + out << "{"; + PrintBaseFields(out); + out << "}"; + } + + // This is false until we reach the `}` of the constraint definition. + auto is_complete() const -> bool { return complete; } + + // Determines whether we're currently defining the constraint. This is true + // between the braces of the constraint. + auto is_being_defined() const -> bool { + return has_definition_started() && !is_complete(); + } +}; + +using NamedConstraintStore = ValueStore; + +} // namespace Carbon::SemIR + +#endif // CARBON_TOOLCHAIN_SEM_IR_NAMED_CONSTRAINT_H_ diff --git a/toolchain/sem_ir/stringify.cpp b/toolchain/sem_ir/stringify.cpp index 5fbf0dd258c4..d3e2d6f19fee 100644 --- a/toolchain/sem_ir/stringify.cpp +++ b/toolchain/sem_ir/stringify.cpp @@ -437,6 +437,16 @@ class Stringifier { ">"); } + auto StringifyInst(InstId /*inst_id*/, GenericNamedConstraintType inst) + -> void { + const auto& constraint = + sem_ir_->named_constraints().Get(inst.named_constraint_id); + *out_ << "Push(StepStack::QualifiedNameItem{constraint.parent_scope_id, + constraint.name_id}, + ">"); + } + // Determine the specific interface that an impl witness instruction provides // an implementation of. // TODO: Should we track this in the type? diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 4ebd08c2cb2e..10ef2f4e5348 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -856,6 +856,21 @@ struct GenericInterfaceType { SpecificId enclosing_specific_id; }; +// The type of the name of a generic named constraint. The corresponding value +// is an empty `StructValue`. +struct GenericNamedConstraintType { + // This is only ever created as a constant, so doesn't have a location. + static constexpr auto Kind = + InstKind::GenericNamedConstraintType.Define( + {.ir_name = "generic_named_constaint_type", + .is_type = InstIsType::Always, + .constant_kind = InstConstantKind::WheneverPossible}); + + TypeId type_id; + NamedConstraintId named_constraint_id; + SpecificId enclosing_specific_id; +}; + // An `impl` declaration. struct ImplDecl { static constexpr auto Kind = InstKind::ImplDecl.Define( @@ -1115,6 +1130,7 @@ struct InterfaceDecl { InstKind::InterfaceDecl.Define( {.ir_name = "interface_decl", .is_lowered = false}); + // Always `type`. TypeId type_id; InterfaceId interface_id; // The declaration block, containing the interface name's qualifiers and the @@ -1220,6 +1236,20 @@ struct NameBindingDecl { InstBlockId pattern_block_id; }; +// A named constraint declaration. +struct NamedConstraintDecl { + static constexpr auto Kind = + InstKind::NamedConstraintDecl.Define( + {.ir_name = "constraint_decl", .is_lowered = false}); + + // Always `type`. + TypeId type_id; + NamedConstraintId named_constraint_id; + // The declaration block, containing the constraint name's qualifiers and the + // constraint's generic parameters. + DeclInstBlockId decl_block_id; +}; + // A name reference, with the value of the name. This only handles name // resolution; the value may be used for reading or writing. struct NameRef {