Skip to content

Commit 421f39a

Browse files
committed
Sema: Fix some edge cases with opaque return types in structural position
Move the check where we insert UnderlyingToOpaqueExpr earlier in coerceToType(). Otherwise, returning a metatype or function type would attempt to perform a conversion instead of building a UnderlyingToOpaqueExpr. Fixes part of swiftlang#60038.
1 parent 5c412b4 commit 421f39a

File tree

2 files changed

+92
-30
lines changed

2 files changed

+92
-30
lines changed

lib/Sema/CSApply.cpp

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6875,6 +6875,60 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
68756875
}
68766876
}
68776877

6878+
// Use an opaque type to abstract a value of the underlying concrete type.
6879+
// The full check here would be that `toType` and `fromType` are structurally
6880+
// equal except in any position where `toType` has an opaque archetype. The
6881+
// below is just an approximate check since the above would be expensive to
6882+
// verify and still relies on the type checker ensuing `fromType` is
6883+
// compatible with any opaque archetypes.
6884+
if (toType->hasOpaqueArchetype() &&
6885+
cs.getConstraintLocator(locator)->isForContextualType()) {
6886+
// Find the opaque type declaration. We need its generic signature.
6887+
OpaqueTypeDecl *opaqueDecl = nullptr;
6888+
bool found = toType.findIf([&](Type type) {
6889+
if (auto opaqueType = type->getAs<OpaqueTypeArchetypeType>()) {
6890+
opaqueDecl = opaqueType->getDecl();
6891+
return true;
6892+
}
6893+
6894+
return false;
6895+
});
6896+
(void)found;
6897+
assert(found && "No opaque type archetype?");
6898+
6899+
// Compute the substitutions for the opaque type declaration.
6900+
auto opaqueLocator = solution.getConstraintSystem().getOpenOpaqueLocator(
6901+
locator, opaqueDecl);
6902+
SubstitutionMap substitutions = solution.computeSubstitutions(
6903+
opaqueDecl->getOpaqueInterfaceGenericSignature(), opaqueLocator);
6904+
6905+
// If we don't have substitutions, this is an opaque archetype from
6906+
// another declaration being manipulated, and not an erasure of a
6907+
// concrete type to an opaque type inside its defining declaration.
6908+
if (!substitutions.empty()) {
6909+
// Compute the underlying type by replacing all opaque archetypes with
6910+
// the fixed type of their opened type.
6911+
auto underlyingType = toType.subst(
6912+
[&](SubstitutableType *type) -> Type {
6913+
if (auto *opaqueType = type->getAs<OpaqueTypeArchetypeType>()) {
6914+
if (opaqueType->getDecl() == opaqueDecl) {
6915+
return opaqueType->getInterfaceType().subst(substitutions);
6916+
}
6917+
}
6918+
return type;
6919+
},
6920+
LookUpConformanceInModule(cs.DC->getParentModule()),
6921+
SubstFlags::SubstituteOpaqueArchetypes);
6922+
6923+
// Coerce the result expression to the underlying type.
6924+
// FIXME: Wrong locator?
6925+
auto *subExpr = coerceToType(expr, underlyingType, locator);
6926+
6927+
return cs.cacheType(
6928+
new (ctx) UnderlyingToOpaqueExpr(subExpr, toType, substitutions));
6929+
}
6930+
}
6931+
68786932
// Handle "from specific" coercions before "catch all" coercions.
68796933
auto desugaredFromType = fromType->getDesugaredType();
68806934
switch (desugaredFromType->getKind()) {
@@ -7260,36 +7314,6 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
72607314
if (fromType->hasUnresolvedType() || toType->hasUnresolvedType())
72617315
return cs.cacheType(new (ctx) UnresolvedTypeConversionExpr(expr, toType));
72627316

7263-
// Use an opaque type to abstract a value of the underlying concrete type.
7264-
// The full check here would be that `toType` and `fromType` are structurally
7265-
// equal except in any position where `toType` has an opaque archetype. The
7266-
// below is just an approximate check since the above would be expensive to
7267-
// verify and still relies on the type checker ensuing `fromType` is
7268-
// compatible with any opaque archetypes.
7269-
if (toType->hasOpaqueArchetype()) {
7270-
// Find the opaque type declaration. We need its generic signature.
7271-
OpaqueTypeDecl *opaqueDecl = nullptr;
7272-
bool found = toType.findIf([&](Type type) {
7273-
if (auto opaqueType = type->getAs<OpaqueTypeArchetypeType>()) {
7274-
opaqueDecl = opaqueType->getDecl();
7275-
return true;
7276-
}
7277-
7278-
return false;
7279-
});
7280-
(void)found;
7281-
assert(found && "No opaque type archetype?");
7282-
7283-
// Compute the substitutions for the opaque type declaration.
7284-
auto opaqueLocator = solution.getConstraintSystem().getOpenOpaqueLocator(
7285-
locator, opaqueDecl);
7286-
SubstitutionMap substitutions = solution.computeSubstitutions(
7287-
opaqueDecl->getOpaqueInterfaceGenericSignature(), opaqueLocator);
7288-
assert(!substitutions.empty() && "Missing substitutions for opaque type");
7289-
return cs.cacheType(
7290-
new (ctx) UnderlyingToOpaqueExpr(expr, toType, substitutions));
7291-
}
7292-
72937317
llvm_unreachable("Unhandled coercion");
72947318
}
72957319

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %target-swift-emit-silgen -disable-availability-checking %s
2+
3+
public protocol P {}
4+
5+
extension P {
6+
// This will only typecheck and SILGen if we emit the UnderlyingToOpaqueExpr
7+
// instead of a MetatypeConversionExpr
8+
public func f1() -> (some P).Type {
9+
return Self.self
10+
}
11+
12+
// This will only typecheck and SILGen if we emit the UnderlyingToOpaqueExpr
13+
// instead of a FunctionConversionExpr
14+
public func f2() -> () -> (some P) {
15+
let fn = { self }
16+
return fn
17+
}
18+
19+
// FIXME: This still doesn't work because the '() -> (some P)' propagates
20+
// backwards into the closure literal's return type
21+
/* public func f3() -> () -> (some P) {
22+
return { self }
23+
} */
24+
25+
// Optionals already worked, but let's just be sure
26+
public func f4() -> (some P)? {
27+
return self
28+
}
29+
30+
// Make sure we can handle a function conversion *and* opaque type erasure;
31+
// here we first convert (Any) -> (Self) to (Int) -> (Self) before erasing
32+
// to (Int) -> (some P)
33+
public func f5() -> (Int) -> (some P) {
34+
let fn: (Any) -> (Self) = { _ in self }
35+
return fn
36+
}
37+
}
38+

0 commit comments

Comments
 (0)