//===--- OpenedExistentials.cpp - Utilities for existential types ---------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file defines common utilities for existential opening and some related // things, such as the checks around covariant `Self` in class conformances. // //===----------------------------------------------------------------------===// #include "OpenedExistentials.h" #include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/Decl.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Types.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Assertions.h" using namespace swift; GenericParameterReferenceInfo & GenericParameterReferenceInfo::operator|=(const GenericParameterReferenceInfo &other) { DirectRefs |= other.DirectRefs; DepMemberTyRefs |= other.DepMemberTyRefs; HasCovariantGenericParamResult |= other.HasCovariantGenericParamResult; return *this; } /// Forward declaration. static GenericParameterReferenceInfo findGenericParameterReferencesRec(CanGenericSignature, GenericTypeParamType *, GenericTypeParamType *, Type, TypePosition, bool); /// Determine whether a function type with the given result type may have /// a covariant generic parameter type result. This is true if the result type /// is either a function type, or a generic parameter, possibly wrapped in some /// level of optionality. static bool canResultTypeHaveCovariantGenericParameterResult(Type resultTy) { if (resultTy->is()) return true; resultTy = resultTy->lookThroughAllOptionalTypes(); return resultTy->is(); } /// Report references to the given generic parameter within the given function /// type using the given generic signature. /// /// \param position The current position in terms of variance. /// \param skipParamIndex The index of the parameter that shall be skipped. static GenericParameterReferenceInfo findGenericParameterReferencesInFunction( CanGenericSignature genericSig, GenericTypeParamType *origParam, GenericTypeParamType *openedParam, const AnyFunctionType *fnType, TypePosition position, bool canBeCovariantResult, std::optional skipParamIndex) { // If there are no type parameters, we're done. if (!isa(fnType) && !fnType->hasTypeParameter()) return GenericParameterReferenceInfo(); auto inputInfo = GenericParameterReferenceInfo(); const auto params = fnType->getParams(); for (const auto paramIdx : indices(params)) { // If this is the parameter we were supposed to skip, do so. if (skipParamIndex && paramIdx == *skipParamIndex) continue; const auto ¶m = params[paramIdx]; // inout types are invariant. if (param.isInOut()) { inputInfo |= ::findGenericParameterReferencesRec( genericSig, origParam, openedParam, param.getPlainType(), TypePosition::Invariant, /*canBeCovariantResult=*/false); continue; } // Parameters are contravariant, but if we're prior to the skipped // parameter treat them as invariant because we're not allowed to // reference the parameter at all. TypePosition paramPos = position.flipped(); if (skipParamIndex && paramIdx < *skipParamIndex) paramPos = TypePosition::Invariant; inputInfo |= ::findGenericParameterReferencesRec( genericSig, origParam, openedParam, param.getParameterType(), paramPos, /*canBeCovariantResult=*/false); } canBeCovariantResult = // &= does not short-circuit. canBeCovariantResult && canResultTypeHaveCovariantGenericParameterResult(fnType->getResult()); const auto resultInfo = ::findGenericParameterReferencesRec( genericSig, origParam, openedParam, fnType->getResult(), position, canBeCovariantResult); return inputInfo |= resultInfo; } /// Report references to the given generic parameter within the given type /// using the given generic signature. /// /// \param position The current position in terms of variance. static GenericParameterReferenceInfo findGenericParameterReferencesRec(CanGenericSignature genericSig, GenericTypeParamType *origParam, GenericTypeParamType *openedParam, Type type, TypePosition position, bool canBeCovariantResult) { // If there are no type parameters, we're done. if (!type->getCanonicalType()->hasTypeParameter()) return GenericParameterReferenceInfo(); // Tuples preserve variance. if (auto tuple = type->getAs()) { auto info = GenericParameterReferenceInfo(); for (auto &elt : tuple->getElements()) { info |= findGenericParameterReferencesRec( genericSig, origParam, openedParam, elt.getType(), position, /*canBeCovariantResult=*/false); } return info; } // Function types preserve variance in the result type, and flip variance in // the parameter type. if (auto funcTy = type->getAs()) { return findGenericParameterReferencesInFunction( genericSig, origParam, openedParam, funcTy, position, canBeCovariantResult, /*skipParamIndex=*/std::nullopt); } // Metatypes preserve variance. if (auto metaTy = type->getAs()) { return findGenericParameterReferencesRec(genericSig, origParam, openedParam, metaTy->getInstanceType(), position, canBeCovariantResult); } // Optionals preserve variance. if (auto optType = type->getOptionalObjectType()) { return findGenericParameterReferencesRec( genericSig, origParam, openedParam, optType, position, canBeCovariantResult); } // DynamicSelfType preserves variance. if (auto selfType = type->getAs()) { return findGenericParameterReferencesRec(genericSig, origParam, openedParam, selfType->getSelfType(), position, /*canBeCovariantResult=*/false); } if (auto *const nominal = type->getAs()) { auto info = GenericParameterReferenceInfo(); // Don't forget to look in the parent. if (const auto parent = nominal->getParent()) { info |= findGenericParameterReferencesRec( genericSig, origParam, openedParam, parent, TypePosition::Invariant, /*canBeCovariantResult=*/false); } // Most bound generic types are invariant. if (auto *const bgt = type->getAs()) { if (bgt->isArray()) { // Swift.Array preserves variance in its 'Value' type. info |= findGenericParameterReferencesRec( genericSig, origParam, openedParam, bgt->getGenericArgs().front(), position, /*canBeCovariantResult=*/false); } else if (bgt->isDictionary()) { // Swift.Dictionary preserves variance in its 'Element' type. info |= findGenericParameterReferencesRec( genericSig, origParam, openedParam, bgt->getGenericArgs().front(), TypePosition::Invariant, /*canBeCovariantResult=*/false); info |= findGenericParameterReferencesRec( genericSig, origParam, openedParam, bgt->getGenericArgs().back(), position, /*canBeCovariantResult=*/false); } else { for (const auto ¶mType : bgt->getGenericArgs()) { info |= findGenericParameterReferencesRec( genericSig, origParam, openedParam, paramType, TypePosition::Invariant, /*canBeCovariantResult=*/false); } } } return info; } // If the signature of an opaque result type has a same-type constraint // that references Self, it's invariant. if (auto opaque = type->getAs()) { auto info = GenericParameterReferenceInfo(); auto opaqueSig = opaque->getDecl()->getOpaqueInterfaceGenericSignature(); for (const auto &req : opaqueSig.getRequirements()) { switch (req.getKind()) { case RequirementKind::SameShape: llvm_unreachable("Same-shape requirement not supported here"); case RequirementKind::Conformance: case RequirementKind::Layout: continue; case RequirementKind::SameType: info |= findGenericParameterReferencesRec( genericSig, origParam, openedParam, req.getFirstType(), TypePosition::Invariant, /*canBeCovariantResult=*/false); LLVM_FALLTHROUGH; case RequirementKind::Superclass: info |= findGenericParameterReferencesRec( genericSig, origParam, openedParam, req.getSecondType(), TypePosition::Invariant, /*canBeCovariantResult=*/false); break; } } return info; } if (auto *existential = type->getAs()) type = existential->getConstraintType(); // Protocol compositions are invariant. if (auto *comp = type->getAs()) { auto info = GenericParameterReferenceInfo(); for (auto member : comp->getMembers()) { info |= findGenericParameterReferencesRec( genericSig, origParam, openedParam, member, TypePosition::Invariant, /*canBeCovariantResult=*/false); } return info; } // Packs are invariant. if (auto *pack = type->getAs()) { auto info = GenericParameterReferenceInfo(); // FIXME: Source compatibility remedy to allow existential opening in // the following case: // ``` // protocol P {} // struct S {} // func foo(_: T, _: S? = nil) {} // let p: any P // foo(p) // ``` // // for (auto arg : pack->getElementTypes()) { // info |= findGenericParameterReferencesRec( // genericSig, origParam, openedParam, arg, // TypePosition::Invariant, /*canBeCovariantResult=*/false); // } (void)pack; return info; } // Pack expansions are invariant. if (auto *expansion = type->getAs()) { return findGenericParameterReferencesRec( genericSig, origParam, openedParam, expansion->getPatternType(), TypePosition::Invariant, /*canBeCovariantResult=*/false); } // Specifically ignore parameterized protocols because we can erase them to // the upper bound. if (type->is()) { return GenericParameterReferenceInfo(); } // Everything else should be a type parameter. if (!type->isTypeParameter()) { ABORT([&](auto &out) { out << "Unhandled type:\n"; type->dump(out); }); } if (!type->getRootGenericParam()->isEqual(origParam)) { return GenericParameterReferenceInfo(); } // A direct reference to 'Self'. if (type->is()) { if (position == TypePosition::Covariant && canBeCovariantResult) return GenericParameterReferenceInfo::forCovariantGenericParamResult(); return GenericParameterReferenceInfo::forDirectRef(position); } if (origParam != openedParam) { // Replace the original parameter with the parameter in the opened // signature. type = type.subst( [&](SubstitutableType *type) { ASSERT(type->isEqual(origParam)); return openedParam; }, MakeAbstractConformanceForGenericType()); } if (genericSig) { // If the type parameter is beyond the domain of the opened // signature, ignore it. if (!genericSig->isValidTypeParameter(type)) { return GenericParameterReferenceInfo(); } if (auto reducedTy = genericSig.getReducedType(type)) { if (!reducedTy->isEqual(type)) { // Note: origParam becomes openedParam for the recursive call, // because concreteTy is written in terms of genericSig and not // the signature of the old origParam. return findGenericParameterReferencesRec( CanGenericSignature(), openedParam, openedParam, reducedTy, position, canBeCovariantResult); } } } // A reference to an associated type rooted on 'Self'. return GenericParameterReferenceInfo::forDependentMemberTypeRef(position); } GenericParameterReferenceInfo swift::findGenericParameterReferences(const ValueDecl *value, CanGenericSignature sig, GenericTypeParamType *origParam, GenericTypeParamType *openedParam, std::optional skipParamIndex) { if (isa(value)) return GenericParameterReferenceInfo(); auto type = value->getInterfaceType(); // Skip invalid declarations. if (type->hasError()) return GenericParameterReferenceInfo(); // For functions and subscripts, take skipParamIndex into account. if (isa(value) || isa(value)) { // And for a method, skip the 'self' parameter. if (value->hasCurriedSelf()) type = type->castTo()->getResult(); return ::findGenericParameterReferencesInFunction( sig, origParam, openedParam, type->castTo(), TypePosition::Covariant, /*canBeCovariantResult=*/true, skipParamIndex); } return ::findGenericParameterReferencesRec(sig, origParam, openedParam, type, TypePosition::Covariant, /*canBeCovariantResult=*/true); } GenericParameterReferenceInfo swift::findExistentialSelfReferences( const ValueDecl *value) { auto *dc = value->getDeclContext(); ASSERT(dc->getSelfProtocolDecl()); auto sig = dc->getGenericSignatureOfContext().getCanonicalSignature(); auto genericParam = dc->getSelfInterfaceType()->castTo(); return findGenericParameterReferences(value, sig, genericParam, genericParam, std::nullopt); } bool HasSelfOrAssociatedTypeRequirementsRequest::evaluate( Evaluator &evaluator, ProtocolDecl *decl) const { // ObjC protocols do not require `any`. if (decl->isObjC()) return false; for (auto member : decl->getMembers()) { // Existential types require `any` if the protocol has an associated type. if (isa(member)) return true; // For value members, look at their type signatures. if (auto valueMember = dyn_cast(member)) { const auto info = findExistentialSelfReferences(valueMember); if (info.hasNonCovariantRef() || info.hasDependentMemberTypeRef()) { return true; } } } // Check whether any of the inherited protocols require `any`. for (auto proto : decl->getInheritedProtocols()) { if (proto->hasSelfOrAssociatedTypeRequirements()) return true; } return false; } /// A protocol member accessed with an existential value might have generic /// constraints that require the ability to spell an opened archetype in order /// to be satisfied. Such are /// - superclass requirements, when the object is a non-'Self'-rooted type /// parameter, and the subject is dependent on 'Self', e.g. U : G /// - same-type requirements, when one side is dependent on 'Self', and the /// other is a non-'Self'-rooted type parameter, e.g. U.Element == Self. /// /// Because opened archetypes are not part of the surface language, these /// constraints render the member inaccessible. static bool doesMemberHaveUnfulfillableConstraintsWithExistentialBase( OpenedExistentialSignature existentialSig, const ValueDecl *member) { const auto sig = member->getInnermostDeclContext()->getGenericSignatureOfContext(); // Fast path: the member is generic only over 'Self'. if (sig.getGenericParams().size() == 1) { return false; } class IsDependentOnOpenedExistentialSelf : public TypeWalker { OpenedExistentialSignature existentialSig; public: explicit IsDependentOnOpenedExistentialSelf(OpenedExistentialSignature existentialSig) : existentialSig(existentialSig) {} Action walkToTypePre(Type ty) override { // We're looking at the interface type of a protocol member, so it's written // in terms of `Self` (tau_0_0) and possibly type parameters at higher depth: // // if (!ty->isTypeParameter()) { return Action::Continue; } if (ty->getRootGenericParam()->getDepth() > 0) { return Action::SkipNode; } // Ok, we found a type parameter rooted in `Self`. Replace `Self` with the // opened Self type in the existential signature, which looks like this: // // <..., Self where ..., Self: P> ty = ty.subst( [&](SubstitutableType *type) -> Type { return existentialSig.SelfType; }, MakeAbstractConformanceForGenericType()); // Make sure this is valid first. if (!existentialSig.OpenedSig->isValidTypeParameter(ty)) { return Action::SkipNode; } // If the existential type constrains Self.U to a type from the outer // context, then the reduced type of Self.U in the existential signature // will no longer contain Self. ty = existentialSig.OpenedSig.getReducedType(ty); if (!ty.findIf([&](Type t) -> bool { if (auto *paramTy = t->getAs()) return paramTy->isEqual(existentialSig.SelfType); return false; })) { return Action::SkipNode; } // Ok, we found a type that depends on the opened existential Self. return Action::Stop; } } isDependentOnSelf(existentialSig); for (const auto &req : sig.getRequirements()) { switch (req.getKind()) { case RequirementKind::Superclass: { if (req.getFirstType()->getRootGenericParam()->getDepth() > 0 && req.getSecondType().walk(isDependentOnSelf)) { return true; } break; } case RequirementKind::SameType: case RequirementKind::SameShape: { const auto isNonSelfRootedTypeParam = [](Type ty) { return ty->isTypeParameter() && ty->getRootGenericParam()->getDepth() > 0; }; if ((isNonSelfRootedTypeParam(req.getFirstType()) && req.getSecondType().walk(isDependentOnSelf)) || (isNonSelfRootedTypeParam(req.getSecondType()) && req.getFirstType().walk(isDependentOnSelf))) { return true; } break; } case RequirementKind::Conformance: case RequirementKind::Layout: break; } } return false; } ExistentialMemberAccessLimitation swift::isMemberAvailableOnExistential(Type baseTy, const ValueDecl *member) { auto *dc = member->getDeclContext(); if (!dc->getSelfProtocolDecl()) { return ExistentialMemberAccessLimitation::None; } auto &ctx = member->getASTContext(); auto existentialSig = ctx.getOpenedExistentialSignature(baseTy); auto origParam = dc->getSelfInterfaceType()->castTo(); auto openedParam = existentialSig.SelfType->castTo(); // An accessor or non-storage member is not available if its interface type // contains a non-covariant reference to a 'Self'-rooted type parameter in the // context of the base type's existential signature. auto info = findGenericParameterReferences( member, existentialSig.OpenedSig, origParam, openedParam, std::nullopt); auto result = ExistentialMemberAccessLimitation::None; if (!info) { // Nothing to do. } else if (info.hasRef(TypePosition::Invariant)) { // An invariant reference is decisive. result = ExistentialMemberAccessLimitation::Unsupported; } else if (isa(member)) { // Anything non-covariant is decisive for functions. if (info.hasRef(TypePosition::Contravariant)) { result = ExistentialMemberAccessLimitation::Unsupported; } } else { const auto isGetterUnavailable = info.hasRef(TypePosition::Contravariant); auto isSetterUnavailable = true; if (isa(member)) { // For properties, the setter is unavailable if the interface type has a // covariant reference, which becomes contravariant is the setter. isSetterUnavailable = info.hasRef(TypePosition::Covariant); } else { // For subscripts specifically, we must scan the setter directly because // whether a covariant reference in the interface type becomes // contravariant in the setter depends on the location of the reference // (in the indices or the result type). auto *setter = cast(member)->getAccessor(AccessorKind::Set); const auto setterInfo = setter ? findGenericParameterReferences( setter, existentialSig.OpenedSig, origParam, openedParam, std::nullopt) : GenericParameterReferenceInfo(); isSetterUnavailable = setterInfo.hasRef(TypePosition::Contravariant); } if (isGetterUnavailable && isSetterUnavailable) { result = ExistentialMemberAccessLimitation::Unsupported; } else if (isGetterUnavailable) { result = ExistentialMemberAccessLimitation::WriteOnly; } else if (isSetterUnavailable) { result = ExistentialMemberAccessLimitation::ReadOnly; } } // If the member access is not supported whatsoever, we are done. if (result == ExistentialMemberAccessLimitation::Unsupported) return result; // Before proceeding with the result, see if we find a generic requirement // that cannot be satisfied; if we do, the member is unavailable after all. if (doesMemberHaveUnfulfillableConstraintsWithExistentialBase(existentialSig, member)) { return ExistentialMemberAccessLimitation::Unsupported; } return result; } std::optional> swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx, Type paramTy, Type argTy) { if (!callee) return std::nullopt; // Only applies to functions and subscripts. if (!isa(callee) && !isa(callee)) return std::nullopt; // Special semantics prohibit opening existentials. switch (TypeChecker::getDeclTypeCheckingSemantics(callee)) { case DeclTypeCheckingSemantics::OpenExistential: case DeclTypeCheckingSemantics::TypeOf: // type(of:) and _openExistential handle their own opening. return std::nullopt; case DeclTypeCheckingSemantics::Normal: case DeclTypeCheckingSemantics::WithoutActuallyEscaping: break; } // C++ function templates require specialization, which is not possible with // opened existential archetypes, so do not open. if (isa_and_nonnull(callee->getClangDecl())) return std::nullopt; // The actual parameter type needs to involve a type variable, otherwise // type inference won't be possible. if (!paramTy->hasTypeVariable()) return std::nullopt; auto param = getParameterAt(callee, paramIdx); if (!param) return std::nullopt; // If the parameter is non-generic variadic, don't open. if (param->isVariadic()) return std::nullopt; // The rvalue argument type needs to be an existential type or metatype // thereof. const auto rValueArgTy = argTy->getWithoutSpecifierType(); if (!rValueArgTy->isAnyExistentialType()) return std::nullopt; GenericTypeParamType *genericParam; TypeVariableType *typeVar; Type bindingTy; std::tie(genericParam, typeVar, bindingTy) = [=] { // Look through an inout and optional type. Type genericParam = param->getInterfaceType() ->getInOutObjectType() ->lookThroughSingleOptionalType(); Type typeVar = paramTy->getInOutObjectType()->lookThroughSingleOptionalType(); Type bindingTy = rValueArgTy; // Look through a metatype. if (genericParam->is()) { genericParam = genericParam->getMetatypeInstanceType(); typeVar = typeVar->getMetatypeInstanceType(); bindingTy = bindingTy->getMetatypeInstanceType(); } return std::tuple(genericParam->getAs(), typeVar->getAs(), bindingTy); }(); // The should have reached a type variable and corresponding generic // parameter. if (!typeVar || !genericParam) return std::nullopt; // Only allow opening the innermost generic parameters. auto genericContext = callee->getAsGenericContext(); if (!genericContext || !genericContext->isGeneric()) return std::nullopt; auto genericSig = callee->getInnermostDeclContext() ->getGenericSignatureOfContext().getCanonicalSignature(); if (genericParam->getDepth() < genericSig->getMaxDepth()) return std::nullopt; // The binding could be an existential metatype. Get the instance type for // conformance checks and to build an opened existential signature. If the // instance type is not an existential type, i.e., the metatype is nested, // bail out. const Type existentialTy = bindingTy->getMetatypeInstanceType(); if (!existentialTy->isExistentialType()) return std::nullopt; auto &ctx = callee->getASTContext(); // If the existential argument conforms to all of protocol requirements on // the formal parameter's type, don't open unless ImplicitOpenExistentials is // enabled. // If all of the conformance requirements on the formal parameter's type // are self-conforming, don't open. if (!ctx.LangOpts.hasFeature(Feature::ImplicitOpenExistentials)) { bool containsNonSelfConformance = false; for (auto proto : genericSig->getRequiredProtocols(genericParam)) { auto conformance = lookupExistentialConformance( existentialTy, proto); if (conformance.isInvalid()) { containsNonSelfConformance = true; break; } } if (!containsNonSelfConformance) return std::nullopt; } auto existentialSig = ctx.getOpenedExistentialSignature(existentialTy); // Ensure that the formal parameter is only used in covariant positions, // because it won't match anywhere else. auto referenceInfo = findGenericParameterReferences( callee, existentialSig.OpenedSig, genericParam, existentialSig.SelfType->castTo(), /*skipParamIdx=*/paramIdx); if (referenceInfo.hasNonCovariantRef()) return std::nullopt; return std::pair(typeVar, bindingTy); } /// For each occurrence of a type **type** in `refTy` that satisfies /// `predicateFn` in covariant position, **type** is erased to an /// existential using `eraseFn`. static Type typeEraseExistentialSelfReferences( Type refTy, TypePosition outermostPosition, llvm::function_ref containsFn, llvm::function_ref predicateFn, llvm::function_ref eraseFn) { if (!containsFn(refTy)) return refTy; return refTy.transformWithPosition( outermostPosition, [&](TypeBase *t, TypePosition currPos) -> std::optional { if (!containsFn(t)) { return Type(t); } if (t->is()) { const auto instanceTy = t->getMetatypeInstanceType(); auto erasedTy = typeEraseExistentialSelfReferences( instanceTy, currPos, containsFn, predicateFn, eraseFn); if (instanceTy.getPointer() == erasedTy.getPointer()) { return Type(t); } // - If the output instance type is an existential, but the input is // not, wrap the output in an existential metatype. // // X.Type → X → any Y → any Y.Type // // - Otherwise, both are existential or the output instance type is // not existential; wrap the output in a singleton metatype. if (erasedTy->isAnyExistentialType() && !erasedTy->isConstraintType() && !(instanceTy->isAnyExistentialType() && !instanceTy->isConstraintType())) { return Type(ExistentialMetatypeType::get(erasedTy)); } return Type(MetatypeType::get(erasedTy)); } // Opaque types whose substitutions involve this type parameter are // erased to their upper bound. if (auto opaque = dyn_cast(t)) { for (auto replacementType : opaque->getSubstitutions().getReplacementTypes()) { auto erasedReplacementType = typeEraseExistentialSelfReferences( replacementType, TypePosition::Covariant, containsFn, predicateFn, eraseFn); if (erasedReplacementType.getPointer() != replacementType.getPointer()) return opaque->getExistentialType(); } } // Parameterized protocol types whose arguments involve this type // parameter are erased to the base type. if (auto parameterized = dyn_cast(t)) { for (auto argType : parameterized->getArgs()) { auto erasedArgType = typeEraseExistentialSelfReferences( argType, TypePosition::Covariant, containsFn, predicateFn, eraseFn); if (erasedArgType.getPointer() != argType.getPointer()) return parameterized->getBaseType(); } } if (!predicateFn(t)) { // Recurse. return std::nullopt; } auto erasedTy = eraseFn(t, currPos); if (!erasedTy) return Type(t); return erasedTy; }); } Type swift::typeEraseOpenedExistentialReference( Type type, Type existentialBaseType, TypeVariableType *openedTypeVar, TypePosition outermostPosition) { auto existentialSig = type->getASTContext().getOpenedExistentialSignature( existentialBaseType); auto applyOuterSubstitutions = [&](Type t) -> Type { if (t->hasTypeParameter()) { if (auto outerSubs = existentialSig.Generalization) { unsigned depth = existentialSig.OpenedSig->getMaxDepth(); OuterSubstitutions replacer{outerSubs, depth}; return t.subst(replacer, replacer); } } return t; }; auto erase = [&](Type paramTy, TypePosition currPos) -> Type { switch (currPos) { case TypePosition::Covariant: break; case TypePosition::Contravariant: case TypePosition::Invariant: case TypePosition::Shape: return Type(); } // The upper bounds of 'Self' is the existential base type. if (paramTy->is()) return existentialBaseType; return applyOuterSubstitutions( existentialSig.OpenedSig->getExistentialType(paramTy)); }; return typeEraseExistentialSelfReferences( type, outermostPosition, /*containsFn=*/[](Type t) { return t->hasTypeVariable(); }, /*predicateFn=*/[](Type t) { return t->isTypeVariableOrMember(); }, /*eraseFn=*/[&](Type t, TypePosition currPos) -> Type { bool found = false; auto paramTy = t.transformRec([&](Type t) -> std::optional { if (t.getPointer() == openedTypeVar) { found = true; return existentialSig.SelfType; } return std::nullopt; }); if (!found) return Type(); assert(paramTy->isTypeParameter()); // This can happen with invalid code. if (!existentialSig.OpenedSig->isValidTypeParameter(paramTy)) { return Type(t); } // Check if this existential fixes this `Self`-rooted type to something // in the existential's outer generic signature. Type reducedTy = existentialSig.OpenedSig.getReducedType(paramTy); if (!reducedTy->isEqual(paramTy)) { reducedTy = applyOuterSubstitutions(reducedTy); auto erasedTy = typeEraseExistentialSelfReferences( reducedTy, currPos, [&](Type t) { return t->hasTypeParameter(); }, [&](Type t) { return t->isTypeParameter(); }, [&](Type t, TypePosition currPos) { return erase(t, currPos); }); if (erasedTy.getPointer() == reducedTy.getPointer()) { return Type(t); } return erasedTy; } return erase(paramTy, currPos); }); } Type swift::typeEraseOpenedArchetypesFromEnvironment( Type type, GenericEnvironment *env) { assert(env->getKind() == GenericEnvironment::Kind::OpenedExistential); return typeEraseExistentialSelfReferences( type, TypePosition::Covariant, /*containsFn=*/[](Type t) { return t->hasOpenedExistential(); }, /*predicateFn=*/[](Type t) { return t->is(); }, /*eraseFn=*/[&](Type t, TypePosition currPos) { auto *openedTy = t->castTo(); if (openedTy->getGenericEnvironment() == env) return openedTy->getExistentialType(); return Type(); }); }