From 52bb8b44959fe713392a3be8b2af2ecd9da050e3 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 26 Aug 2025 11:07:35 +0100 Subject: [PATCH 1/2] [CS] Record opened types early in `getTypeOfMemberReference` Requirement opening relies on being able to query the opened type to allow requirement fix coalescing to work. Record the opened type before we open requirements to ensure we don't duplicate requirement fixes on the base type with requirement fixes for the member. --- lib/Sema/TypeOfReference.cpp | 30 +++++++++++++++++++---------- test/decl/nested/type_in_type.swift | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/Sema/TypeOfReference.cpp b/lib/Sema/TypeOfReference.cpp index 30fdee97131b5..e44568bd9f878 100644 --- a/lib/Sema/TypeOfReference.cpp +++ b/lib/Sema/TypeOfReference.cpp @@ -1646,17 +1646,30 @@ DeclReferenceType ConstraintSystem::getTypeOfMemberReference( DeclContext *innerDC = value->getInnermostDeclContext(); DeclContext *outerDC = value->getDeclContext(); + auto genericSig = innerDC->getGenericSignatureOfContext(); + // Open the type of the generic function or member of a generic type. Type openedType; + ArrayRef replacements; SmallVector localReplacements; - auto &replacements = replacementsPtr ? *replacementsPtr : localReplacements; + { + auto &_replacements = replacementsPtr ? *replacementsPtr : localReplacements; - // If we have a generic signature, open the parameters. We delay opening - // requirements to allow contextual types to affect the situation. - auto genericSig = innerDC->getGenericSignatureOfContext(); - if (genericSig) { - openGenericParameters(outerDC, genericSig, replacements, locator, - preparedOverload); + // If we have a generic signature, open the parameters. We delay opening + // requirements to allow contextual types to affect the situation. + if (genericSig) { + openGenericParameters(outerDC, genericSig, _replacements, locator, + preparedOverload); + } + + // If we opened up any type variables, record the replacements. We do this + // up-front to allow requirement fix coalescing logic to work correctly with + // requirements imposed on base type (since that relies on being able to + // find the recorded opened type). We then make the array immutable for the + // following logic to ensure they don't attempt to add any additional opened + // types. + recordOpenedTypes(locator, _replacements, preparedOverload); + replacements = _replacements; } Type thrownErrorType; @@ -1867,9 +1880,6 @@ DeclReferenceType ConstraintSystem::getTypeOfMemberReference( isDynamicLookup, replacements); } - // If we opened up any type variables, record the replacements. - recordOpenedTypes(locator, replacements, preparedOverload); - return { origOpenedType, openedType, origType, type, thrownErrorType }; } diff --git a/test/decl/nested/type_in_type.swift b/test/decl/nested/type_in_type.swift index 530cd903f5ad2..37cd5bff807d5 100644 --- a/test/decl/nested/type_in_type.swift +++ b/test/decl/nested/type_in_type.swift @@ -427,3 +427,17 @@ extension OuterGeneric.MidNonGeneric { } } + +protocol P {} + +struct A {} +extension A where T: P { // expected-note {{where 'T' = 'T'}} + struct B { + init(_ u: U) {} + } +} +extension A { + func bar() { + B(0) // expected-error {{referencing generic struct 'B' on 'A' requires that 'T' conform to 'P'}} + } +} From 2e0be5afd70e0a334e8fd40d163475dc7f8b4cc7 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 26 Aug 2025 11:07:35 +0100 Subject: [PATCH 2/2] [CS] Open generic requirements for types with unbound + placeholder types - Introduce a generic requirements opening function for type resolution, which is used by the constraint system in cases where we need to impose requirements on opened type variables. - Refactor `replaceInferableTypesWithTypeVars` to use this function when opening generic types that contain either placeholder or unbound generic types. Together these changes ensure that we don't drop generic requirements when an unbound generic or placeholder type is used as a generic argument. --- include/swift/Sema/ConstraintSystem.h | 17 +++ lib/Sema/CSGen.cpp | 9 +- lib/Sema/ConstraintSystem.cpp | 33 +++++ lib/Sema/TypeCheckType.cpp | 149 +++++++++++---------- lib/Sema/TypeCheckType.h | 35 +++-- lib/Sema/TypeOfReference.cpp | 137 ++++++++++++++----- test/Constraints/generics.swift | 1 + test/Constraints/requirement_opening.swift | 54 ++++++++ test/decl/nested/type_in_type.swift | 9 +- 9 files changed, 322 insertions(+), 122 deletions(-) create mode 100644 test/Constraints/requirement_opening.swift diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 4992cfad31f81..6aab1119e1d09 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -5796,6 +5796,23 @@ class OpenUnboundGenericType { } }; +/// A function object suitable for use as an \c OpenRequirementFn that "opens" +/// the requirements for a given type's generic signature given a set of +/// argument substitutions. +class OpenGenericTypeRequirements { + ConstraintSystem &cs; + const ConstraintLocatorBuilder &locator; + PreparedOverloadBuilder *preparedOverload; + +public: + explicit OpenGenericTypeRequirements( + ConstraintSystem &cs, const ConstraintLocatorBuilder &locator, + PreparedOverloadBuilder *preparedOverload) + : cs(cs), locator(locator), preparedOverload(preparedOverload) {} + + void operator()(GenericTypeDecl *decl, TypeSubstitutionFn subst) const; +}; + class HandlePlaceholderType { ConstraintSystem &cs; ConstraintLocator *locator; diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index f6d348dea9c02..cff569091d147 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1697,6 +1697,9 @@ namespace { // Introduce type variables for unbound generics. const auto genericOpener = OpenUnboundGenericType(CS, locator); const auto placeholderHandler = HandlePlaceholderType(CS, locator); + const auto requirementOpener = + OpenGenericTypeRequirements(CS, locator, + /*preparedOverload*/ nullptr); // Add a PackElementOf constraint for 'each T' type reprs. PackExpansionExpr *elementEnv = nullptr; @@ -1708,7 +1711,7 @@ namespace { const auto result = TypeResolution::resolveContextualType( repr, CS.DC, options, genericOpener, placeholderHandler, - packElementOpener); + packElementOpener, requirementOpener); if (result->hasError()) { CS.recordFix( IgnoreInvalidASTNode::create(CS, CS.getConstraintLocator(locator))); @@ -1973,7 +1976,9 @@ namespace { // Introduce type variables for unbound generics. OpenUnboundGenericType(CS, argLocator), HandlePlaceholderType(CS, argLocator), - OpenPackElementType(CS, argLocator, elementEnv)); + OpenPackElementType(CS, argLocator, elementEnv), + OpenGenericTypeRequirements(CS, locator, + /*preparedOverload*/ nullptr)); if (result->hasError()) { auto &ctxt = CS.getASTContext(); result = PlaceholderType::get(ctxt, specializationArg); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 97e8debcacb53..c6408b2ddb508 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3414,6 +3414,39 @@ bool ConstraintSystem::diagnoseAmbiguity(ArrayRef solutions) { return false; } +void OpenGenericTypeRequirements::operator()(GenericTypeDecl *decl, + TypeSubstitutionFn subst) const { + auto *outerDC = decl->getDeclContext(); + auto sig = decl->getGenericSignature(); + + // In principle we shouldn't need to open the generic parameters here, we + // could just open the requirements using the substituted arguments directly, + // but that doesn't allow us to correctly handle requirement fix coalescing + // in `isFixedRequirement`. So instead we open the generic parameters and + // then bind the resulting type variables to the substituted args. + SmallVector replacements; + cs.openGenericParameters(outerDC, sig, replacements, locator, + preparedOverload); + + // FIXME: Get rid of fixmeAllowDuplicates. This is the same issue as in + // `openUnboundGenericType`; both `applyUnboundGenericArguments` & + // `replaceInferableTypesWithTypeVars` can open multiple different generic + // types with the same locator. For the former we ought to plumb through the + // TypeRepr and use that to distinguish the locator. For the latter we ought + // to try migrating clients off it, pushing the opening up to type resolution. + cs.recordOpenedTypes(locator, replacements, preparedOverload, + /*fixmeAllowDuplicates*/ true); + + for (auto [gp, typeVar] : replacements) + cs.addConstraint(ConstraintKind::Bind, typeVar, subst(gp), locator); + + auto openType = [&](Type ty) -> Type { + return cs.openType(ty, replacements, locator, preparedOverload); + }; + cs.openGenericRequirements(outerDC, sig, /*skipProtocolSelf*/ false, locator, + openType, preparedOverload); +} + ConstraintLocator * constraints::simplifyLocator(ConstraintSystem &cs, ConstraintLocator *locator, SourceRange &range) { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index b3d7ff5430dad..5cc47ab8d8332 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -77,36 +77,40 @@ TypeResolution::forStructural(DeclContext *dc, TypeResolutionOptions options, HandlePlaceholderTypeReprFn placeholderHandler, OpenPackElementFn packElementOpener) { return TypeResolution(dc, {}, TypeResolutionStage::Structural, options, - unboundTyOpener, placeholderHandler, packElementOpener); + unboundTyOpener, placeholderHandler, packElementOpener, + /*requirementOpener*/ nullptr); } TypeResolution TypeResolution::forInterface(DeclContext *dc, TypeResolutionOptions options, OpenUnboundGenericTypeFn unboundTyOpener, HandlePlaceholderTypeReprFn placeholderHandler, - OpenPackElementFn packElementOpener) { + OpenPackElementFn packElementOpener, + OpenRequirementFn requirementOpener) { return forInterface(dc, dc->getGenericSignatureOfContext(), options, - unboundTyOpener, placeholderHandler, packElementOpener); + unboundTyOpener, placeholderHandler, packElementOpener, + requirementOpener); } -TypeResolution -TypeResolution::forInterface(DeclContext *dc, GenericSignature genericSig, - TypeResolutionOptions options, - OpenUnboundGenericTypeFn unboundTyOpener, - HandlePlaceholderTypeReprFn placeholderHandler, - OpenPackElementFn packElementOpener) { +TypeResolution TypeResolution::forInterface( + DeclContext *dc, GenericSignature genericSig, TypeResolutionOptions options, + OpenUnboundGenericTypeFn unboundTyOpener, + HandlePlaceholderTypeReprFn placeholderHandler, + OpenPackElementFn packElementOpener, OpenRequirementFn requirementOpener) { return TypeResolution(dc, genericSig, TypeResolutionStage::Interface, options, - unboundTyOpener, placeholderHandler, packElementOpener); + unboundTyOpener, placeholderHandler, packElementOpener, + requirementOpener); } TypeResolution TypeResolution::withOptions(TypeResolutionOptions opts) const { return TypeResolution(dc, genericSig, stage, opts, unboundTyOpener, - placeholderHandler, packElementOpener); + placeholderHandler, packElementOpener, + requirementOpener); } TypeResolution TypeResolution::withoutPackElementOpener() const { return TypeResolution(dc, genericSig, stage, options, unboundTyOpener, - placeholderHandler, {}); + placeholderHandler, {}, requirementOpener); } ASTContext &TypeResolution::getASTContext() const { @@ -1185,6 +1189,7 @@ Type TypeResolution::applyUnboundGenericArguments( // or unbound generics, let's skip the check here, and let the solver // do it when missing types are deduced. bool skipRequirementsCheck = false; + bool hasTypeVariables = false; if (options.contains(TypeResolutionFlags::SILType)) { if (auto nominal = dyn_cast(decl)) { if (nominal->isOptionalDecl()) { @@ -1215,7 +1220,7 @@ Type TypeResolution::applyUnboundGenericArguments( subs = parentTy->getContextSubstitutions(decl->getDeclContext()); } - skipRequirementsCheck |= parentTy->hasTypeVariable(); + hasTypeVariables |= parentTy->hasTypeVariable(); // Fill in substitutions for outer generic parameters if we have a local // type in generic context. This isn't actually supported all the way, @@ -1243,56 +1248,59 @@ Type TypeResolution::applyUnboundGenericArguments( // Enter the substitution. subs[paramTy] = substTy; - skipRequirementsCheck |= - substTy->hasTypeVariable() || substTy->hasUnboundGenericType(); + hasTypeVariables |= substTy->hasTypeVariable(); + skipRequirementsCheck |= substTy->hasUnboundGenericType(); } + const auto substitutions = [&](SubstitutableType *type) -> Type { + auto result = QueryTypeSubstitutionMap{subs}(type); + if (result->hasTypeParameter()) { + if (const auto contextSig = getGenericSignature()) { + auto *genericEnv = contextSig.getGenericEnvironment(); + // FIXME: This should just use mapTypeIntoContext(), but we can't yet + // because we sometimes have type parameters here that are invalid for + // our generic signature. This can happen if the type parameter was + // found via unqualified lookup, but the current context's + // generic signature failed to build because of circularity or + // completion failure. + return result.subst(QueryInterfaceTypeSubstitutions{genericEnv}, + LookUpConformanceInModule(), + SubstFlags::PreservePackExpansionLevel); + } + } + return result; + }; + // Check the generic arguments against the requirements of the declaration's // generic signature. if (!skipRequirementsCheck && getStage() == TypeResolutionStage::Interface) { - // Check the generic arguments against the requirements of the declaration's - // generic signature. + if (hasTypeVariables) { + ASSERT(requirementOpener && "Must have requirement opener for type vars"); + requirementOpener(decl, substitutions); + } else { + SourceLoc noteLoc = decl->getLoc(); + if (noteLoc.isInvalid()) + noteLoc = loc; - SourceLoc noteLoc = decl->getLoc(); - if (noteLoc.isInvalid()) - noteLoc = loc; + auto genericSig = decl->getGenericSignature(); - auto genericSig = decl->getGenericSignature(); - const auto substitutions = [&](SubstitutableType *type) -> Type { - auto result = QueryTypeSubstitutionMap{subs}(type); - if (result->hasTypeParameter()) { - if (const auto contextSig = getGenericSignature()) { - auto *genericEnv = contextSig.getGenericEnvironment(); - // FIXME: This should just use mapTypeIntoContext(), but we can't yet - // because we sometimes have type parameters here that are invalid for - // our generic signature. This can happen if the type parameter was - // found via unqualified lookup, but the current context's - // generic signature failed to build because of circularity or - // completion failure. - return result.subst(QueryInterfaceTypeSubstitutions{genericEnv}, - LookUpConformanceInModule(), - SubstFlags::PreservePackExpansionLevel); + const auto result = TypeChecker::checkGenericArgumentsForDiagnostics( + genericSig, substitutions); + switch (result.getKind()) { + case CheckRequirementsResult::RequirementFailure: + if (loc.isValid()) { + TypeChecker::diagnoseRequirementFailure( + result.getRequirementFailureInfo(), loc, noteLoc, + UnboundGenericType::get(decl, parentTy, ctx), + genericSig.getGenericParams(), substitutions); } - } - return result; - }; - const auto result = TypeChecker::checkGenericArgumentsForDiagnostics( - genericSig, substitutions); - switch (result.getKind()) { - case CheckRequirementsResult::RequirementFailure: - if (loc.isValid()) { - TypeChecker::diagnoseRequirementFailure( - result.getRequirementFailureInfo(), loc, noteLoc, - UnboundGenericType::get(decl, parentTy, ctx), - genericSig.getGenericParams(), substitutions); + LLVM_FALLTHROUGH; + case CheckRequirementsResult::SubstitutionFailure: + return ErrorType::get(ctx); + case CheckRequirementsResult::Success: + break; } - - LLVM_FALLTHROUGH; - case CheckRequirementsResult::SubstitutionFailure: - return ErrorType::get(ctx); - case CheckRequirementsResult::Success: - break; } } @@ -2564,22 +2572,22 @@ Type TypeResolution::resolveContextualType( TypeRepr *TyR, DeclContext *dc, TypeResolutionOptions opts, OpenUnboundGenericTypeFn unboundTyOpener, HandlePlaceholderTypeReprFn placeholderHandler, - OpenPackElementFn packElementOpener, + OpenPackElementFn packElementOpener, OpenRequirementFn requirementOpener, SILTypeResolutionContext *silContext) { - return resolveContextualType(TyR, dc, dc->getGenericSignatureOfContext(), - opts, unboundTyOpener, placeholderHandler, - packElementOpener, silContext); + return resolveContextualType( + TyR, dc, dc->getGenericSignatureOfContext(), opts, unboundTyOpener, + placeholderHandler, packElementOpener, requirementOpener, silContext); } Type TypeResolution::resolveContextualType( TypeRepr *TyR, DeclContext *dc, GenericSignature genericSig, TypeResolutionOptions opts, OpenUnboundGenericTypeFn unboundTyOpener, HandlePlaceholderTypeReprFn placeholderHandler, - OpenPackElementFn packElementOpener, + OpenPackElementFn packElementOpener, OpenRequirementFn requirementOpener, SILTypeResolutionContext *silContext) { const auto resolution = TypeResolution::forInterface( dc, genericSig, opts, unboundTyOpener, placeholderHandler, - packElementOpener); + packElementOpener, requirementOpener); const auto ty = resolution.resolveType(TyR, silContext); return GenericEnvironment::mapTypeIntoContext( @@ -4384,11 +4392,10 @@ NeverNullType TypeResolver::resolveSILBoxType(SILBoxTypeRepr *repr, auto *genericParams = repr->getGenericParams(); if (genericParams) { - fieldResolution = - TypeResolution::forInterface(getDeclContext(), genericSig, options, - resolution.getUnboundTypeOpener(), - resolution.getPlaceholderHandler(), - resolution.getPackElementOpener()); + fieldResolution = TypeResolution::forInterface( + getDeclContext(), genericSig, options, + resolution.getUnboundTypeOpener(), resolution.getPlaceholderHandler(), + resolution.getPackElementOpener(), resolution.getRequirementOpener()); } SILInnerGenericContextRAII scope(silContext, genericParams); @@ -4593,9 +4600,8 @@ NeverNullType TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, if (componentTypeSig) { functionResolution = TypeResolution::forInterface( getDeclContext(), componentTypeSig, options, - resolution.getUnboundTypeOpener(), - resolution.getPlaceholderHandler(), - resolution.getPackElementOpener()); + resolution.getUnboundTypeOpener(), resolution.getPlaceholderHandler(), + resolution.getPackElementOpener(), resolution.getRequirementOpener()); } SILInnerGenericContextRAII innerGenericContext(silContext, @@ -4651,11 +4657,10 @@ NeverNullType TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, SubstitutionMap patternSubs; if (!repr->getPatternSubstitutions().empty()) { if (genericSig) { - auto resolveSILParameters = - TypeResolution::forInterface(getDeclContext(), genericSig, options, - resolution.getUnboundTypeOpener(), - resolution.getPlaceholderHandler(), - resolution.getPackElementOpener()); + auto resolveSILParameters = TypeResolution::forInterface( + getDeclContext(), genericSig, options, + resolution.getUnboundTypeOpener(), resolution.getPlaceholderHandler(), + resolution.getPackElementOpener(), resolution.getRequirementOpener()); patternSubs = resolveSubstitutions(repr->getPatternGenericSignature(), repr->getPatternSubstitutions(), TypeResolver{resolveSILParameters, diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index ecf18d2f05e00..dfc7ecec1426d 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -574,6 +574,9 @@ using HandlePlaceholderTypeReprFn = using OpenPackElementFn = llvm::function_ref; +using OpenRequirementFn = + llvm::function_ref; + /// Handles the resolution of types within a given declaration context, /// which might involve resolving generic parameters to a particular /// stage. @@ -584,6 +587,7 @@ class TypeResolution { OpenUnboundGenericTypeFn unboundTyOpener; HandlePlaceholderTypeReprFn placeholderHandler; OpenPackElementFn packElementOpener; + OpenRequirementFn requirementOpener; GenericSignature genericSig; private: @@ -591,11 +595,13 @@ class TypeResolution { TypeResolutionStage stage, TypeResolutionOptions options, OpenUnboundGenericTypeFn unboundTyOpener, HandlePlaceholderTypeReprFn placeholderHandler, - OpenPackElementFn packElementOpener) + OpenPackElementFn packElementOpener, + OpenRequirementFn requirementOpener) : dc(dc), stage(stage), options(options), unboundTyOpener(unboundTyOpener), placeholderHandler(placeholderHandler), - packElementOpener(packElementOpener), genericSig(genericSig) {} + packElementOpener(packElementOpener), + requirementOpener(requirementOpener), genericSig(genericSig) {} public: /// Form a type resolution for the structure of a type, which does not @@ -613,7 +619,8 @@ class TypeResolution { forInterface(DeclContext *dc, TypeResolutionOptions opts, OpenUnboundGenericTypeFn unboundTyOpener, HandlePlaceholderTypeReprFn placeholderHandler, - OpenPackElementFn packElementOpener); + OpenPackElementFn packElementOpener, + OpenRequirementFn requirementOpener = nullptr); /// Form a type resolution for an interface type, which is a complete /// description of the type using generic parameters. @@ -622,7 +629,8 @@ class TypeResolution { TypeResolutionOptions opts, OpenUnboundGenericTypeFn unboundTyOpener, HandlePlaceholderTypeReprFn placeholderHandler, - OpenPackElementFn packElementOpener); + OpenPackElementFn packElementOpener, + OpenRequirementFn requirementOpener = nullptr); /// Form a type resolution for a contextual type, which is a complete /// description of the type using the archetypes of the given generic @@ -633,14 +641,17 @@ class TypeResolution { OpenUnboundGenericTypeFn unboundTyOpener, HandlePlaceholderTypeReprFn placeholderHandler, OpenPackElementFn packElementOpener, + OpenRequirementFn requirementOpener = nullptr, SILTypeResolutionContext *silContext = nullptr); - static Type resolveContextualType( - TypeRepr *TyR, DeclContext *dc, GenericSignature genericSig, - TypeResolutionOptions opts, OpenUnboundGenericTypeFn unboundTyOpener, - HandlePlaceholderTypeReprFn placeholderHandler, - OpenPackElementFn packElementOpener, - SILTypeResolutionContext *silContext = nullptr); + static Type + resolveContextualType(TypeRepr *TyR, DeclContext *dc, + GenericSignature genericSig, TypeResolutionOptions opts, + OpenUnboundGenericTypeFn unboundTyOpener, + HandlePlaceholderTypeReprFn placeholderHandler, + OpenPackElementFn packElementOpener, + OpenRequirementFn requirementOpener = nullptr, + SILTypeResolutionContext *silContext = nullptr); public: TypeResolution withOptions(TypeResolutionOptions opts) const; @@ -672,6 +683,10 @@ class TypeResolution { return packElementOpener; } + OpenRequirementFn getRequirementOpener() const { + return requirementOpener; + } + /// Retrieves the generic signature for the context, or NULL if there is /// no generic signature to resolve types. GenericSignature getGenericSignature() const; diff --git a/lib/Sema/TypeOfReference.cpp b/lib/Sema/TypeOfReference.cpp index e44568bd9f878..9f8e4031b2868 100644 --- a/lib/Sema/TypeOfReference.cpp +++ b/lib/Sema/TypeOfReference.cpp @@ -110,7 +110,8 @@ Type ConstraintSystem::openUnboundGenericType(GenericTypeDecl *decl, DC, std::nullopt, [](auto) -> Type { llvm_unreachable("should not be used"); }, [](auto &, auto) -> Type { llvm_unreachable("should not be used"); }, - [](auto, auto) -> Type { llvm_unreachable("should not be used"); }) + [](auto, auto) -> Type { llvm_unreachable("should not be used"); }, + [](auto, auto) { /*will be called, but we already handled reqs*/ }) .applyUnboundGenericArguments(decl, parentTy, SourceLoc(), arguments); if (!parentTy && !isTypeResolution) { result = DC->mapTypeIntoContext(result); @@ -198,47 +199,111 @@ static void checkNestedTypeConstraints(ConstraintSystem &cs, Type type, checkNestedTypeConstraints(cs, parentTy, locator, preparedOverload); } -Type ConstraintSystem::replaceInferableTypesWithTypeVars( - Type type, ConstraintLocatorBuilder locator, - PreparedOverloadBuilder *preparedOverload) { - if (!type->hasUnboundGenericType() && !type->hasPlaceholder()) - return type; +namespace { +class InferableTypeOpener final { + ConstraintSystem &cs; + ConstraintLocatorBuilder locator; + PreparedOverloadBuilder *preparedOverload; - auto flags = TVO_CanBindToNoEscape | TVO_PrefersSubtypeBinding | TVO_CanBindToHole; - - type = type.transformRec([&](Type type) -> std::optional { - if (auto unbound = type->getAs()) { - return openUnboundGenericType(unbound->getDecl(), unbound->getParent(), - locator, /*isTypeResolution=*/false, - preparedOverload); - } else if (auto *placeholderTy = type->getAs()) { - if (auto *typeRepr = - placeholderTy->getOriginator().dyn_cast()) { - if (isa(typeRepr)) { - return Type( - createTypeVariable( - getConstraintLocator(locator, LocatorPathElt::PlaceholderType(typeRepr)), - flags, preparedOverload)); - } - } else if (auto *var = placeholderTy->getOriginator().dyn_cast()) { - if (var->getName().hasDollarPrefix()) { - auto *repr = - new (type->getASTContext()) PlaceholderTypeRepr(var->getLoc()); - return Type( - createTypeVariable( - getConstraintLocator(locator, LocatorPathElt::PlaceholderType(repr)), - flags, preparedOverload)); - } - } +public: + InferableTypeOpener(ConstraintSystem &cs, ConstraintLocatorBuilder locator, + PreparedOverloadBuilder *preparedOverload) + : cs(cs), locator(locator), preparedOverload(preparedOverload) {} + + TypeVariableType *createTypeVariable(ConstraintLocator *loc) { + auto flags = + TVO_CanBindToNoEscape | TVO_PrefersSubtypeBinding | TVO_CanBindToHole; + return cs.createTypeVariable(loc, flags, preparedOverload); + } + + Type transformUnboundGenericType(UnboundGenericType *unbound) { + return cs.openUnboundGenericType(unbound->getDecl(), unbound->getParent(), + locator, /*isTypeResolution=*/false, + preparedOverload); + } + + Type transformPlaceholderType(PlaceholderType *placeholderTy) { + auto originator = placeholderTy->getOriginator(); + if (auto *typeRepr = originator.dyn_cast()) { + if (isa(typeRepr)) { + auto *loc = cs.getConstraintLocator( + locator, LocatorPathElt::PlaceholderType(typeRepr)); + return createTypeVariable(loc); + } + } else if (auto *var = originator.dyn_cast()) { + if (var->getName().hasDollarPrefix()) { + auto *repr = + new (cs.getASTContext()) PlaceholderTypeRepr(var->getLoc()); + auto *loc = cs.getConstraintLocator( + locator, LocatorPathElt::PlaceholderType(repr)); + return createTypeVariable(loc); } + } + return placeholderTy; + } + + void openGenericTypeRequirements(GenericTypeDecl *D, SubstitutionMap subs) { + OpenGenericTypeRequirements openReqs(cs, locator, preparedOverload); + openReqs(D, QuerySubstitutionMap{subs}); + } + + Type transformBoundGenericType(BoundGenericType *genericTy) { + SmallVector genericArgs; + for (auto arg : genericTy->getGenericArgs()) + genericArgs.push_back(transform(arg)); + + auto substTy = BoundGenericType::get( + genericTy->getDecl(), transform(genericTy->getParent()), genericArgs); + + openGenericTypeRequirements(substTy->getDecl(), + substTy->getContextSubstitutionMap()); + return substTy; + } + + Type transformTypeAliasType(TypeAliasType *aliasTy) { + SmallVector genericArgs; + for (auto arg : aliasTy->getDirectGenericArgs()) + genericArgs.push_back(transform(arg)); + + auto substTy = TypeAliasType::get( + aliasTy->getDecl(), transform(aliasTy->getParent()), genericArgs, + transform(aliasTy->getSinglyDesugaredType())); + openGenericTypeRequirements(substTy->getDecl(), + substTy->getSubstitutionMap()); + return substTy; + } + + Type transform(Type type) { + if (!type) + return type; + if (!type->hasUnboundGenericType() && !type->hasPlaceholder()) + return type; + + return type.transformRec([&](Type type) -> std::optional { + if (!type->hasUnboundGenericType() && !type->hasPlaceholder()) + return type; + + auto *tyPtr = type.getPointer(); + if (auto unbound = dyn_cast(tyPtr)) + return transformUnboundGenericType(unbound); + if (auto *placeholderTy = dyn_cast(tyPtr)) + return transformPlaceholderType(placeholderTy); + if (auto *genericTy = dyn_cast(tyPtr)) + return transformBoundGenericType(genericTy); + if (auto *aliasTy = dyn_cast(tyPtr)) + return transformTypeAliasType(aliasTy); return std::nullopt; }); + } +}; +} // end anonymous namespace - if (!type) - return ErrorType::get(getASTContext()); - - return type; +Type ConstraintSystem::replaceInferableTypesWithTypeVars( + Type type, ConstraintLocatorBuilder locator, + PreparedOverloadBuilder *preparedOverload) { + InferableTypeOpener opener(*this, locator, preparedOverload); + return opener.transform(type); } namespace { diff --git a/test/Constraints/generics.swift b/test/Constraints/generics.swift index dab4c83408cdb..27bd5dc0bf4f2 100644 --- a/test/Constraints/generics.swift +++ b/test/Constraints/generics.swift @@ -452,6 +452,7 @@ class GenericClass {} func genericFunc(t: T) { _ = [T: GenericClass] // expected-error {{generic parameter 'A' could not be inferred}} // expected-note@-1 {{explicitly specify the generic arguments to fix this issue}} + // expected-error@-2 {{generic struct 'Dictionary' requires that 'T' conform to 'Hashable'}} } // https://github.com/apple/swift/issues/46113 diff --git a/test/Constraints/requirement_opening.swift b/test/Constraints/requirement_opening.swift new file mode 100644 index 0000000000000..df1900c4ba464 --- /dev/null +++ b/test/Constraints/requirement_opening.swift @@ -0,0 +1,54 @@ +// RUN: %target-typecheck-verify-swift + +struct K {} // expected-note 6{{'U' declared as parameter to type 'K'}} +protocol Q {} + +struct S1: ExpressibleByArrayLiteral { // expected-note 2{{where 'T' = 'K'}} + init(_ x: T) {} + init(arrayLiteral: T...) {} +} + +typealias R1 = S1 // expected-note {{where 'T' = 'K'}} expected-note 2{{where 'T' = 'K'}} + +func foo(_ x: K) { + let _ = [x] as S1 // expected-error {{generic struct 'S1' requires that 'K' conform to 'Q'}} + let _ = [x] as R1 // expected-error {{generic type alias 'R1' requires that 'K' conform to 'Q'}} + + let _: S1 = [x] // expected-error {{generic struct 'S1' requires that 'K' conform to 'Q'}} + // FIXME: We ought to be able to infer 'U' here. + let _: R1 = [x] // expected-error 2 {{generic type alias 'R1' requires that 'K' conform to 'Q'}} + // expected-error@-1 2 {{generic parameter 'U' could not be inferred}} +} + +protocol P2 { + associatedtype A +} + +struct S2: P2 {} // expected-note 3{{where 'A' = 'K'}} +typealias R2 = S2 // expected-note 2{{where 'A' = 'K'}} expected-note 2{{where 'A' = 'K'}} + +// Same as S2, but without the Q requirement. +struct S3: P2 {} +typealias R3 = S3 // expected-note {{where 'A' = 'K'}} expected-note {{where 'A' = 'K'}} + +func foo(_ y: T.A.Type) -> T {} +let _ = foo(K.self) as S2 // expected-error {{generic struct 'S2' requires that 'K' conform to 'Q'}} +let _ = foo(K.self) as R2 // expected-error {{generic type alias 'R2' requires that 'K' conform to 'Q'}} +let _ = foo(K.self) as R3 // expected-error {{generic type alias 'R3' requires that 'K' conform to 'Q'}} + +let _: S2 = foo(K.self) // expected-error {{generic struct 'S2' requires that 'K' conform to 'Q'}} +// FIXME: We ought to be able to infer 'U' here. +let _: R2 = foo(K.self) // expected-error 2{{generic type alias 'R2' requires that 'K' conform to 'Q'}} +// expected-error@-1 2 {{generic parameter 'U' could not be inferred}} +let _: R3 = foo(K.self) // expected-error {{generic type alias 'R3' requires that 'K' conform to 'Q'}} +// expected-error@-1 2 {{generic parameter 'U' could not be inferred}} + +func foo(_ x: T.Type, _ y: T.A.Type) {} +foo(S2<_>.self, K.self) // expected-error {{generic struct 'S2' requires that 'K' conform to 'Q'}} +foo(R2<_>.self, K.self) // expected-error {{generic type alias 'R2' requires that 'K' conform to 'Q'}} + +struct S4 { // expected-note {{where 'T' = 'Int'}} + init(_ x: T) {} +} + +_ = S4<_>(0) // expected-error {{generic struct 'S4' requires that 'Int' conform to 'Q'}} diff --git a/test/decl/nested/type_in_type.swift b/test/decl/nested/type_in_type.swift index 37cd5bff807d5..cbfee535b778d 100644 --- a/test/decl/nested/type_in_type.swift +++ b/test/decl/nested/type_in_type.swift @@ -383,7 +383,7 @@ struct Kitten : ExpressibleByCatLiteral {} struct Puppy : ExpressibleByDogLiteral {} struct Claws { // expected-note 3 {{'A' declared as parameter to type 'Claws'}} - struct Fangs { } // expected-note {{where 'B' = 'NotADog'}} + struct Fangs { } // expected-note 3 {{where 'B' = 'NotADog'}} } struct NotADog {} @@ -407,9 +407,14 @@ do { // expected-note@-2 {{explicitly specify the generic arguments to fix this issue}} {{36-36=<<#A: ExpressibleByCatLiteral#>>}} let _: Claws.Fangs = something() // expected-error@-1 {{generic parameter 'A' could not be inferred}} + // expected-error@-2 {{generic struct 'Fangs' requires that 'NotADog' conform to 'ExpressibleByDogLiteral'}} + + // FIXME: We get a duplicate diagnostic here because we don't differentiate + // constraint locators when opening generic requirements, we just use a + // locator for the top-level TypeRepr. _ = Claws.Fangs() // expected-error@-1 {{generic parameter 'A' could not be inferred}} - // expected-error@-2 {{generic struct 'Fangs' requires that 'NotADog' conform to 'ExpressibleByDogLiteral'}} + // expected-error@-2 2{{generic struct 'Fangs' requires that 'NotADog' conform to 'ExpressibleByDogLiteral'}} // expected-note@-3 {{explicitly specify the generic arguments to fix this issue}} {{12-12=<<#A: ExpressibleByCatLiteral#>>}} }