Skip to content

Commit 4d5f090

Browse files
committed
Fix the computation of thunk types for functions involving element archetypes
The old code assumed that thunk signatures would only possibly refer to a single opened existential archetype. To generalize this to opened element archetypes, it needs to both support multiple archetypes and, of course, support the new kind of local archetype. So here's a bunch of code to clone element signatures and support multiple local archetypes. It fell out to also make this support multiple opened existential archetypes, if we ever have code patterns that require that.
1 parent 3342d67 commit 4d5f090

File tree

1 file changed

+194
-44
lines changed

1 file changed

+194
-44
lines changed

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 194 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2640,34 +2640,176 @@ CanSILFunctionType swift::getNativeSILFunctionType(
26402640
substConstant, reqtSubs, witnessMethodConformance);
26412641
}
26422642

2643+
namespace {
2644+
struct LocalArchetypeRequirementCollector {
2645+
const ASTContext &Context;
2646+
unsigned Depth;
2647+
2648+
/// The lists of new parameters and requirements to add to the signature.
2649+
SmallVector<GenericTypeParamType *, 2> Params;
2650+
SmallVector<Requirement, 2> Requirements;
2651+
2652+
/// The list of contextual types from the original function to use as
2653+
/// substitutions for the new parameters; parallel to Params.
2654+
SmallVector<Type, 4> ParamSubs;
2655+
2656+
/// A mapping of local archetypes to the corresponding type parameters
2657+
/// created for them.
2658+
llvm::DenseMap<CanType, Type> ParamsForLocalArchetypes;
2659+
2660+
/// The set of element environments we've processed.
2661+
llvm::SmallPtrSet<GenericEnvironment*, 4> ElementEnvs;
2662+
2663+
LocalArchetypeRequirementCollector(const ASTContext &ctx, unsigned depth)
2664+
: Context(ctx), Depth(depth) {}
2665+
2666+
void collect(CanLocalArchetypeType archetype) {
2667+
if (auto openedExistential = dyn_cast<OpenedArchetypeType>(archetype)) {
2668+
collect(openedExistential);
2669+
} else {
2670+
collect(cast<ElementArchetypeType>(archetype));
2671+
}
2672+
}
2673+
2674+
void collect(CanOpenedArchetypeType archetype) {
2675+
auto param = addParameter(archetype);
2676+
2677+
assert(archetype->isRoot());
2678+
auto constraint = archetype->getExistentialType();
2679+
if (auto existential = constraint->getAs<ExistentialType>())
2680+
constraint = existential->getConstraintType();
2681+
2682+
addRequirement(RequirementKind::Conformance, param, constraint);
2683+
}
2684+
2685+
void collect(CanElementArchetypeType archetype) {
2686+
size_t startingIndex = Params.size();
2687+
2688+
// Check whether we've already handled this environment.
2689+
// This can happen with opened-element environments because
2690+
// they can open multiple archetypes.
2691+
auto env = archetype->getGenericEnvironment();
2692+
if (!ElementEnvs.insert(env).second) return;
2693+
2694+
// Add a parameter for each of the opened elements in this environment.
2695+
auto sig = env->getGenericSignature();
2696+
auto elementParams = sig.getInnermostGenericParams();
2697+
#ifndef NDEBUG
2698+
unsigned nextIndex = 0;
2699+
#endif
2700+
for (auto elementParam : elementParams) {
2701+
assert(elementParam->getIndex() == nextIndex++);
2702+
auto elementArchetype = env->mapTypeIntoContext(elementParam);
2703+
addParameter(cast<LocalArchetypeType>(CanType(elementArchetype)));
2704+
}
2705+
2706+
// Clone the element requirements.
2707+
2708+
// The element parameters should all have the same depth, and their
2709+
// index values are dense and in order from 0..<N in that depth.
2710+
// We asserted that above, and we'll use it below to map them to
2711+
// their new parameters in Params.
2712+
auto elementDepth = elementParams.front()->getDepth();
2713+
2714+
// Helper function: does the given type refer to an opened element
2715+
// parameter from the opened element generic signature?
2716+
auto refersToElementType = [=](Type type) {
2717+
return type.findIf([&](Type t) {
2718+
if (auto *param = t->getAs<GenericTypeParamType>())
2719+
return (param->getDepth() == elementDepth);
2720+
return false;
2721+
});
2722+
};
2723+
// Helper function: replace references to opened element parameters
2724+
// with one of the type parameters we just created for this
2725+
// environment.
2726+
auto rewriteElementType = [=](Type type) {
2727+
return type.transformRec([&](Type t) -> Optional<Type> {
2728+
if (auto *param = t->getAs<GenericTypeParamType>()) {
2729+
if (param->getDepth() == elementDepth)
2730+
return Type(Params[startingIndex + param->getIndex()]);
2731+
}
2732+
return None;
2733+
});
2734+
};
2735+
2736+
for (auto req : sig.getRequirements()) {
2737+
switch (req.getKind()) {
2738+
case RequirementKind::SameShape:
2739+
// These never involve element types.
2740+
break;
2741+
case RequirementKind::Conformance:
2742+
if (refersToElementType(req.getFirstType())) {
2743+
addRequirement(RequirementKind::Conformance,
2744+
rewriteElementType(req.getFirstType()),
2745+
req.getSecondType());
2746+
}
2747+
break;
2748+
case RequirementKind::Superclass:
2749+
case RequirementKind::SameType:
2750+
if (refersToElementType(req.getFirstType()) ||
2751+
refersToElementType(req.getSecondType())) {
2752+
addRequirement(req.getKind(),
2753+
rewriteElementType(req.getFirstType()),
2754+
rewriteElementType(req.getSecondType()));
2755+
}
2756+
break;
2757+
break;
2758+
case RequirementKind::Layout:
2759+
if (refersToElementType(req.getFirstType())) {
2760+
addRequirement(RequirementKind::Layout,
2761+
rewriteElementType(req.getFirstType()),
2762+
req.getLayoutConstraint());
2763+
}
2764+
break;
2765+
}
2766+
}
2767+
}
2768+
2769+
GenericTypeParamType *addParameter(CanLocalArchetypeType localArchetype) {
2770+
auto *param = GenericTypeParamType::get(/*pack*/ false, Depth,
2771+
Params.size(), Context);
2772+
Params.push_back(param);
2773+
ParamSubs.push_back(localArchetype);
2774+
ParamsForLocalArchetypes.insert(std::make_pair(localArchetype, param));
2775+
return param;
2776+
}
2777+
2778+
template <class... Args>
2779+
void addRequirement(Args &&... args) {
2780+
Requirements.emplace_back(std::forward<Args>(args)...);
2781+
}
2782+
};
2783+
} // end anonymous namespace
2784+
26432785
/// Build a generic signature and environment for a re-abstraction thunk.
26442786
///
26452787
/// Most thunks share the generic environment with their original function.
26462788
/// The one exception is if the thunk type involves an open existential,
26472789
/// in which case we "promote" the opened existential to a new generic parameter.
26482790
///
2649-
/// \param SGF - the parent function
2650-
/// \param openedExistential - the opened existential to promote to a generic
2651-
// parameter, if any
2791+
/// \param localArchetypes - the list of local archetypes to promote
2792+
/// into the signature, if any
26522793
/// \param inheritGenericSig - whether to inherit the generic signature from the
26532794
/// parent function.
26542795
/// \param genericEnv - the new generic environment
2655-
/// \param contextSubs - map old archetypes to new archetypes
2796+
/// \param contextSubs - map non-local archetypes from the original function
2797+
/// to archetypes in the thunk
26562798
/// \param interfaceSubs - map interface types to old archetypes
26572799
static CanGenericSignature
26582800
buildThunkSignature(SILFunction *fn,
26592801
bool inheritGenericSig,
2660-
OpenedArchetypeType *openedExistential,
2802+
ArrayRef<CanLocalArchetypeType> localArchetypes,
26612803
GenericEnvironment *&genericEnv,
26622804
SubstitutionMap &contextSubs,
26632805
SubstitutionMap &interfaceSubs,
2664-
ArchetypeType *&newArchetype) {
2806+
llvm::DenseMap<ArchetypeType*, Type> &contextLocalArchetypes) {
26652807
auto *mod = fn->getModule().getSwiftModule();
26662808
auto &ctx = mod->getASTContext();
26672809

2668-
// If there's no opened existential, we just inherit the generic environment
2669-
// from the parent function.
2670-
if (openedExistential == nullptr) {
2810+
// If there are no local archetypes, we just inherit the generic
2811+
// environment from the parent function.
2812+
if (localArchetypes.empty()) {
26712813
auto genericSig =
26722814
fn->getLoweredFunctionType()->getInvocationGenericSignature();
26732815
genericEnv = fn->getGenericEnvironment();
@@ -2677,7 +2819,7 @@ buildThunkSignature(SILFunction *fn,
26772819
}
26782820

26792821
// Add the existing generic signature.
2680-
int depth = 0;
2822+
unsigned depth = 0;
26812823
GenericSignature baseGenericSig;
26822824
if (inheritGenericSig) {
26832825
if (auto genericSig =
@@ -2687,25 +2829,26 @@ buildThunkSignature(SILFunction *fn,
26872829
}
26882830
}
26892831

2690-
// Add a new generic parameter to replace the opened existential.
2691-
auto *newGenericParam =
2692-
GenericTypeParamType::get(/*isParameterPack*/ false, depth, 0, ctx);
2693-
2694-
assert(openedExistential->isRoot());
2695-
auto constraint = openedExistential->getExistentialType();
2696-
if (auto existential = constraint->getAs<ExistentialType>())
2697-
constraint = existential->getConstraintType();
2832+
// Add new generic parameters to replace the local archetypes.
2833+
LocalArchetypeRequirementCollector collector(ctx, depth);
26982834

2699-
Requirement newRequirement(RequirementKind::Conformance, newGenericParam,
2700-
constraint);
2835+
for (auto archetype : localArchetypes) {
2836+
collector.collect(archetype);
2837+
}
27012838

27022839
auto genericSig = buildGenericSignature(ctx, baseGenericSig,
2703-
{ newGenericParam },
2704-
{ newRequirement });
2840+
collector.Params,
2841+
collector.Requirements);
27052842
genericEnv = genericSig.getGenericEnvironment();
27062843

2707-
newArchetype = genericEnv->mapTypeIntoContext(newGenericParam)
2708-
->castTo<ArchetypeType>();
2844+
// Map the local archetypes to their new parameter types.
2845+
for (auto localArchetype : localArchetypes) {
2846+
auto param =
2847+
collector.ParamsForLocalArchetypes.find(localArchetype)->second;
2848+
auto thunkArchetype = genericEnv->mapTypeIntoContext(param);
2849+
contextLocalArchetypes.insert(std::make_pair(localArchetype,
2850+
thunkArchetype));
2851+
}
27092852

27102853
// Calculate substitutions to map the caller's archetypes to the thunk's
27112854
// archetypes.
@@ -2723,8 +2866,11 @@ buildThunkSignature(SILFunction *fn,
27232866
interfaceSubs = SubstitutionMap::get(
27242867
genericSig,
27252868
[&](SubstitutableType *type) -> Type {
2726-
if (type->isEqual(newGenericParam))
2727-
return openedExistential;
2869+
if (auto param = dyn_cast<GenericTypeParamType>(type)) {
2870+
if (param->getDepth() == depth) {
2871+
return collector.ParamSubs[param->getIndex()];
2872+
}
2873+
}
27282874
return fn->mapTypeIntoContext(type);
27292875
},
27302876
MakeAbstractConformanceForGenericType());
@@ -2768,18 +2914,16 @@ CanSILFunctionType swift::buildSILFunctionThunkType(
27682914
if (withoutActuallyEscaping)
27692915
extInfoBuilder = extInfoBuilder.withNoEscape(false);
27702916

2771-
// Does the thunk type involve archetypes other than opened existentials?
2917+
// Does the thunk type involve archetypes other than local archetypes?
27722918
bool hasArchetypes = false;
2773-
// Does the thunk type involve an open existential type?
2774-
CanOpenedArchetypeType openedExistential;
2919+
// Does the thunk type involve a local archetype type?
2920+
SmallVector<CanLocalArchetypeType, 8> localArchetypes;
27752921
auto archetypeVisitor = [&](CanType t) {
27762922
if (auto archetypeTy = dyn_cast<ArchetypeType>(t)) {
2777-
if (auto opened = dyn_cast<OpenedArchetypeType>(archetypeTy)) {
2778-
const auto root = opened.getRoot();
2779-
assert((openedExistential == CanArchetypeType() ||
2780-
openedExistential == root) &&
2781-
"one too many open existentials");
2782-
openedExistential = root;
2923+
if (auto opened = dyn_cast<LocalArchetypeType>(archetypeTy)) {
2924+
auto root = opened.getRoot();
2925+
if (llvm::find(localArchetypes, root) == localArchetypes.end())
2926+
localArchetypes.push_back(root);
27832927
} else {
27842928
hasArchetypes = true;
27852929
}
@@ -2790,32 +2934,38 @@ CanSILFunctionType swift::buildSILFunctionThunkType(
27902934
// generic parameters.
27912935
CanGenericSignature genericSig;
27922936
SubstitutionMap contextSubs;
2793-
ArchetypeType *newArchetype = nullptr;
2937+
llvm::DenseMap<ArchetypeType*, Type> contextLocalArchetypes;
27942938

27952939
if (expectedType->hasArchetype() || sourceType->hasArchetype()) {
27962940
expectedType.visit(archetypeVisitor);
27972941
sourceType.visit(archetypeVisitor);
27982942

27992943
genericSig = buildThunkSignature(fn,
28002944
hasArchetypes,
2801-
openedExistential,
2945+
localArchetypes,
28022946
genericEnv,
28032947
contextSubs,
28042948
interfaceSubs,
2805-
newArchetype);
2949+
contextLocalArchetypes);
28062950
}
28072951

28082952
auto substTypeHelper = [&](SubstitutableType *type) -> Type {
2809-
// FIXME: Type::subst should not pass in non-root archetypes.
2810-
// Consider only root archetypes.
2811-
if (auto *archetype = dyn_cast<ArchetypeType>(type)) {
2953+
// If it's a local archetype, do an ad-hoc mapping through the
2954+
// map produced by buildThunkSignature.
2955+
if (auto *archetype = dyn_cast<LocalArchetypeType>(type)) {
2956+
assert(!contextLocalArchetypes.empty());
2957+
2958+
// Decline to map non-root archetypes; subst() will come back
2959+
// to us later and ask about the root.
28122960
if (!archetype->isRoot())
28132961
return Type();
2814-
}
28152962

2816-
if (CanType(type) == openedExistential)
2817-
return newArchetype;
2963+
auto it = contextLocalArchetypes.find(archetype);
2964+
assert(it != contextLocalArchetypes.end());
2965+
return it->second;
2966+
}
28182967

2968+
// Otherwise, use the context substitutions.
28192969
return Type(type).subst(contextSubs);
28202970
};
28212971
auto substConformanceHelper =

0 commit comments

Comments
 (0)