Skip to content

Commit b97ef02

Browse files
committed
Opaque opaque types and compute substitutions in the constraint system
Opaque opaque types and record them within the "opened types" of the constraint system, then use that information to compute the set of substitutions needed for the opaque type declaration using the normal mechanism of the constraint solver. Record these substitutions within the underlying-to-opaque conversion. Use the recorded substitutions in the underlying-to-opaque conversion to set the underlying substitutions for the opaque type declaration itself, rather than reconstructing the substitutions in an ad hoc manner that does not account for structural opaque result types.
1 parent 9d22db4 commit b97ef02

File tree

7 files changed

+91
-66
lines changed

7 files changed

+91
-66
lines changed

include/swift/AST/Expr.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3003,8 +3003,13 @@ class LinearToDifferentiableFunctionExpr : public ImplicitConversionExpr {
30033003
/// (opaque type)" and "S<T> ---> S<(opaque type)>".
30043004
class UnderlyingToOpaqueExpr : public ImplicitConversionExpr {
30053005
public:
3006-
UnderlyingToOpaqueExpr(Expr *subExpr, Type ty)
3007-
: ImplicitConversionExpr(ExprKind::UnderlyingToOpaque, subExpr, ty) {}
3006+
/// The substitutions to be applied to the opaque type declaration to
3007+
/// produce the resulting type.
3008+
const SubstitutionMap substitutions;
3009+
3010+
UnderlyingToOpaqueExpr(Expr *subExpr, Type ty, SubstitutionMap substitutions)
3011+
: ImplicitConversionExpr(ExprKind::UnderlyingToOpaque, subExpr, ty),
3012+
substitutions(substitutions) {}
30083013

30093014
static bool classof(const Expr *E) {
30103015
return E->getKind() == ExprKind::UnderlyingToOpaque;

include/swift/Sema/ConstraintSystem.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3316,6 +3316,11 @@ class ConstraintSystem {
33163316
ArrayRef<ConstraintLocator::PathElement> path,
33173317
unsigned summaryFlags);
33183318

3319+
/// Retrieve a locator for opening the opaque archetype for the given
3320+
/// opaque type.
3321+
ConstraintLocator *getOpenOpaqueLocator(
3322+
ConstraintLocatorBuilder locator, OpaqueTypeDecl *opaqueDecl);
3323+
33193324
/// Retrive the constraint locator for the given anchor and
33203325
/// path, uniqued and automatically infer the summary flags
33213326
ConstraintLocator *

lib/Sema/CSApply.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7095,8 +7095,29 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
70957095
// below is just an approximate check since the above would be expensive to
70967096
// verify and still relies on the type checker ensuing `fromType` is
70977097
// compatible with any opaque archetypes.
7098-
if (toType->hasOpaqueArchetype())
7099-
return cs.cacheType(new (ctx) UnderlyingToOpaqueExpr(expr, toType));
7098+
if (toType->hasOpaqueArchetype()) {
7099+
// Find the opaque type declaration. We need its generic signature.
7100+
OpaqueTypeDecl *opaqueDecl = nullptr;
7101+
bool found = toType.findIf([&](Type type) {
7102+
if (auto opaqueType = type->getAs<OpaqueTypeArchetypeType>()) {
7103+
opaqueDecl = opaqueType->getDecl();
7104+
return true;
7105+
}
7106+
7107+
return false;
7108+
});
7109+
(void)found;
7110+
assert(found && "No opaque type archetype?");
7111+
7112+
// Compute the substitutions for the opaque type declaration.
7113+
auto opaqueLocator = solution.getConstraintSystem().getOpenOpaqueLocator(
7114+
locator, opaqueDecl);
7115+
SubstitutionMap substitutions = solution.computeSubstitutions(
7116+
opaqueDecl->getOpaqueInterfaceGenericSignature(), opaqueLocator);
7117+
assert(!substitutions.empty() && "Missing substitutions for opaque type");
7118+
return cs.cacheType(
7119+
new (ctx) UnderlyingToOpaqueExpr(expr, toType, substitutions));
7120+
}
71007121

71017122
llvm_unreachable("Unhandled coercion");
71027123
}

lib/Sema/CSGen.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,16 +1766,12 @@ namespace {
17661766
auto contextualPurpose = CS.getContextualTypePurpose(expr);
17671767

17681768
auto joinElementTypes = [&](Optional<Type> elementType) {
1769-
auto openedElementType = elementType.map([&](Type type) {
1770-
return CS.openOpaqueType(type, contextualPurpose, locator);
1771-
});
1772-
17731769
const auto elements = expr->getElements();
17741770
unsigned index = 0;
17751771

17761772
using Iterator = decltype(elements)::iterator;
17771773
CS.addJoinConstraint<Iterator>(
1778-
locator, elements.begin(), elements.end(), openedElementType,
1774+
locator, elements.begin(), elements.end(), elementType,
17791775
[&](const auto it) {
17801776
auto *locator = CS.getConstraintLocator(
17811777
expr, LocatorPathElt::TupleElement(index++));
@@ -1788,6 +1784,8 @@ namespace {
17881784
// Now that we know we're actually going to use the type, get the
17891785
// version for use in a constraint.
17901786
contextualType = CS.getContextualType(expr, /*forConstraint=*/true);
1787+
contextualType = CS.openOpaqueType(
1788+
contextualType, contextualPurpose, locator);
17911789
Optional<Type> arrayElementType =
17921790
ConstraintSystem::isArrayType(contextualType);
17931791
CS.addConstraint(ConstraintKind::LiteralConformsTo, contextualType,
@@ -1901,8 +1899,6 @@ namespace {
19011899
contextualType = CS.getContextualType(expr, /*forConstraint=*/true);
19021900
auto openedType =
19031901
CS.openOpaqueType(contextualType, contextualPurpose, locator);
1904-
openedType = CS.replaceInferableTypesWithTypeVars(
1905-
openedType, CS.getConstraintLocator(expr));
19061902
auto dictionaryKeyValue =
19071903
ConstraintSystem::isDictionaryType(openedType);
19081904
Type contextualDictionaryKeyType;

lib/Sema/ConstraintSystem.cpp

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,14 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator(
542542
return getConstraintLocator(anchor);
543543
}
544544

545+
ConstraintLocator *ConstraintSystem::getOpenOpaqueLocator(
546+
ConstraintLocatorBuilder locator, OpaqueTypeDecl *opaqueDecl) {
547+
// Use only the opaque type declaration.
548+
return getConstraintLocator(
549+
ASTNode(opaqueDecl),
550+
{ LocatorPathElt::OpenedOpaqueArchetype(opaqueDecl) }, 0);
551+
}
552+
545553
/// Extend the given depth map by adding depths for all of the subexpressions
546554
/// of the given expression.
547555
static void extendDepthMap(
@@ -829,25 +837,34 @@ Type ConstraintSystem::openType(Type type, OpenedTypeMap &replacements) {
829837

830838
Type ConstraintSystem::openOpaqueType(OpaqueTypeArchetypeType *opaque,
831839
ConstraintLocatorBuilder locator) {
832-
auto opaqueLocator = locator.withPathElement(
833-
LocatorPathElt::OpenedOpaqueArchetype(opaque->getDecl()));
840+
auto opaqueLocatorKey = getOpenOpaqueLocator(locator, opaque->getDecl());
841+
842+
// If we have already opened this opaque type, look in the known set of
843+
// replacements.
844+
auto knownReplacements = OpenedTypes.find(
845+
getConstraintLocator(opaqueLocatorKey));
846+
if (knownReplacements != OpenedTypes.end()) {
847+
auto param = opaque->getInterfaceType()->castTo<GenericTypeParamType>();
848+
for (const auto &replacement : knownReplacements->second) {
849+
if (replacement.first == param)
850+
return replacement.second;
851+
}
852+
853+
llvm_unreachable("Missing opaque type replacement");
854+
}
834855

835856
// Open the generic signature of the opaque decl, and bind the "outer" generic
836857
// params to our context. The remaining axes of freedom on the type variable
837858
// corresponding to the underlying type should be the constraints on the
838859
// underlying return type.
860+
auto opaqueLocator = locator.withPathElement(
861+
LocatorPathElt::OpenedOpaqueArchetype(opaque->getDecl()));
839862
OpenedTypeMap replacements;
840863
openGeneric(DC, opaque->getBoundSignature(), opaqueLocator, replacements);
841864

842-
auto underlyingTyVar = openType(opaque->getInterfaceType(), replacements);
843-
assert(underlyingTyVar);
844-
845-
for (auto param : DC->getGenericSignatureOfContext().getGenericParams()) {
846-
addConstraint(ConstraintKind::Bind, openType(param, replacements),
847-
DC->mapTypeIntoContext(param), opaqueLocator);
848-
}
865+
recordOpenedTypes(opaqueLocatorKey, replacements);
849866

850-
return underlyingTyVar;
867+
return openType(opaque->getInterfaceType(), replacements);
851868
}
852869

853870
Type ConstraintSystem::openOpaqueType(Type type, ContextualTypePurpose context,

lib/Sema/MiscDiagnostics.cpp

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2603,7 +2603,7 @@ class VarDeclUsageChecker : public ASTWalker {
26032603
/// An AST walker that determines the underlying type of an opaque return decl
26042604
/// from its associated function body.
26052605
class OpaqueUnderlyingTypeChecker : public ASTWalker {
2606-
using Candidate = std::pair<Expr *, Type>;
2606+
using Candidate = std::pair<Expr *, SubstitutionMap>;
26072607

26082608
ASTContext &Ctx;
26092609
AbstractFunctionDecl *Implementation;
@@ -2642,20 +2642,21 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
26422642
}
26432643

26442644
// Check whether all of the underlying type candidates match up.
2645-
// TODO [OPAQUE SUPPORT]: multiple opaque types
2646-
Type underlyingType = Candidates.front().second;
2645+
// TODO [OPAQUE SUPPORT]: diagnose multiple opaque types
2646+
SubstitutionMap underlyingSubs = Candidates.front().second;
2647+
SubstitutionMap canonicalUnderlyingSubs = underlyingSubs.getCanonical();
26472648
bool mismatch =
26482649
std::any_of(Candidates.begin() + 1, Candidates.end(),
26492650
[&](Candidate &otherCandidate) {
2650-
return !underlyingType->isEqual(otherCandidate.second);
2651+
return canonicalUnderlyingSubs != otherCandidate.second.getCanonical();
26512652
});
26522653
if (mismatch) {
26532654
Implementation->diagnose(
26542655
diag::opaque_type_mismatched_underlying_type_candidates);
26552656
for (auto candidate : Candidates) {
26562657
Ctx.Diags.diagnose(candidate.first->getLoc(),
26572658
diag::opaque_type_underlying_type_candidate_here,
2658-
candidate.second);
2659+
candidate.second.getReplacementTypes()[0]);
26592660
}
26602661
return;
26612662
}
@@ -2664,39 +2665,30 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
26642665
// in terms of the opaque type itself.
26652666
auto opaqueTypeInContext = Implementation->mapTypeIntoContext(
26662667
OpaqueDecl->getDeclaredInterfaceType());
2667-
auto isSelfReferencing = underlyingType.findIf([&](Type t) -> bool {
2668-
return t->isEqual(opaqueTypeInContext);
2669-
});
2670-
2671-
if (isSelfReferencing) {
2672-
Ctx.Diags.diagnose(Candidates.front().first->getLoc(),
2673-
diag::opaque_type_self_referential_underlying_type,
2674-
underlyingType);
2675-
return;
2668+
for (auto genericParam : OpaqueDecl->getOpaqueGenericParams()) {
2669+
auto underlyingType = Type(genericParam).subst(underlyingSubs);
2670+
auto isSelfReferencing = underlyingType.findIf([&](Type t) -> bool {
2671+
return t->isEqual(opaqueTypeInContext);
2672+
});
2673+
2674+
if (isSelfReferencing) {
2675+
Ctx.Diags.diagnose(Candidates.front().first->getLoc(),
2676+
diag::opaque_type_self_referential_underlying_type,
2677+
underlyingSubs.getReplacementTypes()[0]);
2678+
return;
2679+
}
26762680
}
2677-
2678-
// If we have one successful candidate, then save it as the underlying type
2679-
// of the opaque decl.
2680-
// Form a substitution map that defines it in terms of the other context
2681-
// generic parameters.
2682-
underlyingType = underlyingType->mapTypeOutOfContext();
2683-
auto underlyingSubs = SubstitutionMap::get(
2684-
OpaqueDecl->getOpaqueInterfaceGenericSignature(),
2685-
[&](SubstitutableType *t) -> Type {
2686-
if (t->isEqual(OpaqueDecl->getUnderlyingInterfaceType())) {
2687-
return underlyingType;
2688-
}
2689-
return Type(t);
2690-
},
2691-
LookUpConformanceInModule(OpaqueDecl->getModuleContext()));
2692-
2693-
OpaqueDecl->setUnderlyingTypeSubstitutions(underlyingSubs);
2681+
2682+
// If we have one successful candidate, then save it as the underlying
2683+
// substitutions of the opaque decl.
2684+
OpaqueDecl->setUnderlyingTypeSubstitutions(
2685+
underlyingSubs.mapReplacementTypesOutOfContext());
26942686
}
26952687

26962688
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
26972689
if (auto underlyingToOpaque = dyn_cast<UnderlyingToOpaqueExpr>(E)) {
26982690
Candidates.push_back(std::make_pair(underlyingToOpaque->getSubExpr(),
2699-
underlyingToOpaque->getSubExpr()->getType()));
2691+
underlyingToOpaque->substitutions));
27002692
return {false, E};
27012693
}
27022694
return {true, E};

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -544,19 +544,8 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD,
544544
if (auto opaque = var->getOpaqueResultTypeDecl()) {
545545
init->forEachChildExpr([&](Expr *expr) -> Expr * {
546546
if (auto coercionExpr = dyn_cast<UnderlyingToOpaqueExpr>(expr)) {
547-
auto underlyingType =
548-
coercionExpr->getSubExpr()->getType()->mapTypeOutOfContext();
549-
auto underlyingSubs = SubstitutionMap::get(
550-
opaque->getOpaqueInterfaceGenericSignature(),
551-
[&](SubstitutableType *t) -> Type {
552-
if (t->isEqual(opaque->getUnderlyingInterfaceType())) {
553-
return underlyingType;
554-
}
555-
return Type(t);
556-
},
557-
LookUpConformanceInModule(opaque->getModuleContext()));
558-
559-
opaque->setUnderlyingTypeSubstitutions(underlyingSubs);
547+
opaque->setUnderlyingTypeSubstitutions(
548+
coercionExpr->substitutions.mapReplacementTypesOutOfContext());
560549
}
561550
return expr;
562551
});

0 commit comments

Comments
 (0)