Skip to content

Commit fc1ed43

Browse files
committed
[CSFix] Implement missing existential coercion fix
1 parent 0e70841 commit fc1ed43

File tree

2 files changed

+119
-1
lines changed

2 files changed

+119
-1
lines changed

include/swift/Sema/CSFix.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3000,6 +3000,12 @@ class AddExplicitExistentialCoercion final : public ConstraintFix {
30003000

30013001
bool diagnose(const Solution &solution, bool asNote = false) const override;
30023002

3003+
static bool isRequired(
3004+
ConstraintSystem &cs, Type resultTy,
3005+
SmallVectorImpl<std::pair<TypeVariableType *, OpenedArchetypeType *>>
3006+
&openedExistentials,
3007+
ConstraintLocatorBuilder locator);
3008+
30033009
static AddExplicitExistentialCoercion *create(ConstraintSystem &cs,
30043010
Type resultTy,
30053011
ConstraintLocator *locator);

lib/Sema/CSFix.cpp

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2186,7 +2186,119 @@ IgnoreDefaultExprTypeMismatch::create(ConstraintSystem &cs, Type argType,
21862186

21872187
bool AddExplicitExistentialCoercion::diagnose(const Solution &solution,
21882188
bool asNote) const {
2189-
return false;
2189+
MissingExplicitExistentialCoercion failure(solution, ErasedResultType,
2190+
getLocator());
2191+
return failure.diagnose(asNote);
2192+
}
2193+
2194+
bool AddExplicitExistentialCoercion::isRequired(
2195+
ConstraintSystem &cs, Type resultTy,
2196+
SmallVectorImpl<std::pair<TypeVariableType *, OpenedArchetypeType *>>
2197+
&openedExistentials,
2198+
ConstraintLocatorBuilder locator) {
2199+
using ExistentialList =
2200+
SmallVectorImpl<std::pair<TypeVariableType *, OpenedArchetypeType *>> &;
2201+
2202+
struct CoercionChecker : public TypeWalker {
2203+
bool RequiresCoercion = false;
2204+
2205+
ConstraintSystem &cs;
2206+
ExistentialList OpenedExistentials;
2207+
2208+
CoercionChecker(ConstraintSystem &cs, ExistentialList openedExistentials)
2209+
: cs(cs), OpenedExistentials(openedExistentials) {}
2210+
2211+
Action walkToTypePre(Type componentTy) override {
2212+
// In cases where result references a member type, we need to check
2213+
// whether such type would is resolved to concrete or not.
2214+
if (auto *member = componentTy->getAs<DependentMemberType>()) {
2215+
Type memberBaseTy = member->getBase();
2216+
2217+
// Find the base of this member chain.
2218+
while (true) {
2219+
if (auto *memberTy = memberBaseTy->getAs<DependentMemberType>()) {
2220+
memberBaseTy = memberTy->getBase();
2221+
} else {
2222+
break;
2223+
}
2224+
}
2225+
2226+
auto typeVar = memberBaseTy->getAs<TypeVariableType>();
2227+
if (!typeVar)
2228+
return Action::SkipChildren;
2229+
2230+
// If the base is an opened existential type, let's see whether
2231+
// erase would produce an existential in this case and if so,
2232+
// we need to check whether any requirements are going to be lost
2233+
// in process.
2234+
2235+
auto opened =
2236+
llvm::find_if(OpenedExistentials, [&typeVar](const auto &entry) {
2237+
return typeVar == entry.first;
2238+
});
2239+
2240+
if (opened == OpenedExistentials.end())
2241+
return Action::SkipChildren;
2242+
2243+
auto erasedMemberTy = typeEraseOpenedExistentialReference(
2244+
Type(member), opened->second->getExistentialType(), opened->first,
2245+
TypePosition::Covariant);
2246+
2247+
// If result is an existential type and the base has `where` clauses
2248+
// associated with its associated types, the call needs a coercion.
2249+
if (erasedMemberTy->isExistentialType() &&
2250+
hasConstrainedAssociatedTypes(opened->second)) {
2251+
RequiresCoercion = true;
2252+
return Action::Stop;
2253+
}
2254+
2255+
return Action::SkipChildren;
2256+
}
2257+
2258+
// The case where there is a direct access to opened existential type
2259+
// e.g. `$T` or `[$T]`.
2260+
if (auto *typeVar = componentTy->getAs<TypeVariableType>()) {
2261+
auto opened =
2262+
llvm::find_if(OpenedExistentials, [&typeVar](const auto &entry) {
2263+
return typeVar == entry.first;
2264+
});
2265+
2266+
if (opened != OpenedExistentials.end()) {
2267+
RequiresCoercion |= hasConstrainedAssociatedTypes(opened->second);
2268+
return RequiresCoercion ? Action::Stop : Action::SkipChildren;
2269+
}
2270+
}
2271+
2272+
return Action::Continue;
2273+
}
2274+
2275+
private:
2276+
bool hasConstrainedAssociatedTypes(ArchetypeType *archetypeTy) {
2277+
assert(archetypeTy);
2278+
for (auto *protocol : archetypeTy->getConformsTo()) {
2279+
if (llvm::any_of(protocol->getAssociatedTypeMembers(),
2280+
[](const auto *assocTypeDecl) {
2281+
return bool(assocTypeDecl->getTrailingWhereClause());
2282+
}))
2283+
return true;
2284+
}
2285+
return false;
2286+
}
2287+
};
2288+
2289+
// First, let's check whether coercion is already there.
2290+
if (auto *anchor = getAsExpr(locator.getAnchor())) {
2291+
auto *parent = cs.getParentExpr(anchor);
2292+
// Support both `as` and `as!` coercions.
2293+
if (parent &&
2294+
(isa<CoerceExpr>(parent) || isa<ForcedCheckedCastExpr>(parent)))
2295+
return false;
2296+
}
2297+
2298+
CoercionChecker check(cs, openedExistentials);
2299+
resultTy.walk(check);
2300+
2301+
return check.RequiresCoercion;
21902302
}
21912303

21922304
AddExplicitExistentialCoercion *

0 commit comments

Comments
 (0)