Skip to content

Commit 3fd3ff0

Browse files
committed
[TypeChecker] Adjust Double<->CGFloat conversion to always preserve its location
Unfortunately current approach of making a conversion independent of location doesn't work when conversion is required for multiple arguments to the same call because solver expects that either there are no Double<->CGFloat conversions, or one of them has already been applied which is not the case. The reason why locations weren't preserved in the first place is due to how a solution is applied to AST - AST is mutated first and then, if there are any conversions, they are applied to the already mutated version of original AST. This creates a problem for Double<->CGFloat which depends on an overload choice of injected call and it's impossible to find it based on the mutated AST. But it turns out that this is only an issue in two specific cases - conversions against contextual type and after optional injection. This situations could be mitigated by dropping parts of the locator which are unimportant for the Double<->CGFloat conversion - anchor in case of contextual and `OptionalPayload` element(s) in case of optional injection. Resolves: #59374
1 parent 14dab87 commit 3fd3ff0

File tree

5 files changed

+73
-30
lines changed

5 files changed

+73
-30
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3702,6 +3702,12 @@ class ConstraintSystem {
37023702
ConstraintLocator *
37033703
getConstraintLocator(const ConstraintLocatorBuilder &builder);
37043704

3705+
/// Compute a constraint locator for an implicit value-to-value
3706+
/// conversion rooted at the given location.
3707+
ConstraintLocator *
3708+
getImplicitValueConversionLocator(ConstraintLocatorBuilder root,
3709+
ConversionRestrictionKind restriction);
3710+
37053711
/// Lookup and return parent associated with given expression.
37063712
Expr *getParentExpr(Expr *expr) {
37073713
if (auto result = getExprDepthAndParent(expr))

lib/Sema/CSApply.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6779,10 +6779,13 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
67796779
callLocator, {ConstraintLocator::ApplyFunction,
67806780
ConstraintLocator::ConstructorMember});
67816781

6782-
auto overload = solution.getOverloadChoice(cs.getConstraintLocator(
6783-
ASTNode(), {LocatorPathElt::ImplicitConversion(conversionKind),
6784-
ConstraintLocator::ApplyFunction,
6785-
ConstraintLocator::ConstructorMember}));
6782+
ConstraintLocator *baseLoc =
6783+
cs.getImplicitValueConversionLocator(locator, conversionKind);
6784+
6785+
auto overload =
6786+
solution.getOverloadChoice(solution.getConstraintLocator(
6787+
baseLoc, {ConstraintLocator::ApplyFunction,
6788+
ConstraintLocator::ConstructorMember}));
67866789

67876790
solution.overloadChoices.insert({memberLoc, overload});
67886791
}
@@ -8992,15 +8995,19 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) {
89928995
// If we're supposed to convert the expression to some particular type,
89938996
// do so now.
89948997
if (shouldCoerceToContextualType()) {
8995-
resultExpr =
8996-
Rewriter.coerceToType(resultExpr, solution.simplifyType(convertType),
8997-
cs.getConstraintLocator(resultExpr));
8998+
resultExpr = Rewriter.coerceToType(
8999+
resultExpr, solution.simplifyType(convertType),
9000+
cs.getConstraintLocator(resultExpr,
9001+
LocatorPathElt::ContextualType(
9002+
target.getExprContextualTypePurpose())));
89989003
} else if (cs.getType(resultExpr)->hasLValueType() &&
89999004
!target.isDiscardedExpr()) {
90009005
// We referenced an lvalue. Load it.
90019006
resultExpr = Rewriter.coerceToType(
90029007
resultExpr, cs.getType(resultExpr)->getRValueType(),
9003-
cs.getConstraintLocator(resultExpr));
9008+
cs.getConstraintLocator(resultExpr,
9009+
LocatorPathElt::ContextualType(
9010+
target.getExprContextualTypePurpose())));
90049011
}
90059012

90069013
if (!resultExpr)

lib/Sema/CSSimplify.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12400,28 +12400,26 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
1240012400
if (worseThanBestSolution())
1240112401
return SolutionKind::Error;
1240212402

12403-
auto *conversionLoc = getConstraintLocator(
12404-
/*anchor=*/ASTNode(), LocatorPathElt::ImplicitConversion(restriction));
12403+
auto *conversionLoc =
12404+
getImplicitValueConversionLocator(locator, restriction);
1240512405

1240612406
auto *applicationLoc =
1240712407
getConstraintLocator(conversionLoc, ConstraintLocator::ApplyFunction);
1240812408

1240912409
auto *memberLoc = getConstraintLocator(
1241012410
applicationLoc, ConstraintLocator::ConstructorMember);
1241112411

12412-
// Conversion has been already attempted for this direction
12413-
// and constructor choice has been recorded.
12414-
if (findSelectedOverloadFor(memberLoc))
12415-
return SolutionKind::Solved;
12416-
1241712412
// Allocate a single argument info to cover all possible
1241812413
// Double <-> CGFloat conversion locations.
12419-
if (!ArgumentLists.count(memberLoc)) {
12414+
auto *argumentsLoc =
12415+
getConstraintLocator(conversionLoc, ConstraintLocator::ApplyArgument);
12416+
12417+
if (!ArgumentLists.count(argumentsLoc)) {
1242012418
auto *argList = ArgumentList::createImplicit(
1242112419
getASTContext(), {Argument(SourceLoc(), Identifier(), nullptr)},
1242212420
/*firstTrailingClosureIndex=*/None,
1242312421
AllocationArena::ConstraintSolver);
12424-
ArgumentLists.insert({memberLoc, argList});
12422+
ArgumentLists.insert({argumentsLoc, argList});
1242512423
}
1242612424

1242712425
auto *memberTypeLoc = getConstraintLocator(

lib/Sema/ConstraintSystem.cpp

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -424,21 +424,43 @@ ConstraintLocator *ConstraintSystem::getConstraintLocator(
424424
return getConstraintLocator(anchor, newPath);
425425
}
426426

427+
ConstraintLocator *ConstraintSystem::getImplicitValueConversionLocator(
428+
ConstraintLocatorBuilder root, ConversionRestrictionKind restriction) {
429+
SmallVector<LocatorPathElt, 4> path;
430+
auto anchor = root.getLocatorParts(path);
431+
{
432+
// Drop any value-to-optional conversions that were applied along the
433+
// way to reach this one.
434+
while (!path.empty()) {
435+
if (path.back().is<LocatorPathElt::OptionalPayload>()) {
436+
path.pop_back();
437+
continue;
438+
}
439+
break;
440+
}
441+
442+
// If the conversion is associated with a contextual type e.g.
443+
// `_: Double = CGFloat(1)` then drop `ContextualType` so that
444+
// it's easy to find when the underlying expression has been
445+
// rewritten.
446+
if (!path.empty() && path.back().is<LocatorPathElt::ContextualType>()) {
447+
anchor = ASTNode();
448+
path.clear();
449+
}
450+
}
451+
452+
return getConstraintLocator(/*base=*/getConstraintLocator(anchor, path),
453+
LocatorPathElt::ImplicitConversion(restriction));
454+
}
455+
427456
ConstraintLocator *ConstraintSystem::getCalleeLocator(
428457
ConstraintLocator *locator, bool lookThroughApply,
429458
llvm::function_ref<Type(Expr *)> getType,
430459
llvm::function_ref<Type(Type)> simplifyType,
431460
llvm::function_ref<Optional<SelectedOverload>(ConstraintLocator *)>
432461
getOverloadFor) {
433-
if (auto conversion =
434-
locator->findLast<LocatorPathElt::ImplicitConversion>()) {
435-
if (conversion->is(ConversionRestrictionKind::DoubleToCGFloat) ||
436-
conversion->is(ConversionRestrictionKind::CGFloatToDouble)) {
437-
return getConstraintLocator(
438-
ASTNode(), {*conversion, ConstraintLocator::ApplyFunction,
439-
ConstraintLocator::ConstructorMember});
440-
}
441-
}
462+
if (locator->findLast<LocatorPathElt::ImplicitConversion>())
463+
return locator;
442464

443465
auto anchor = locator->getAnchor();
444466
auto path = locator->getPath();
@@ -5323,6 +5345,9 @@ ConstraintSystem::getArgumentInfoLocator(ConstraintLocator *locator) {
53235345
if (anchor.isNull() && locator->getPath().empty())
53245346
return nullptr;
53255347

5348+
if (locator->findLast<LocatorPathElt::ImplicitConversion>())
5349+
return locator;
5350+
53265351
// Applies and unresolved member exprs can have callee locators that are
53275352
// dependent on the type of their function, which may not have been resolved
53285353
// yet. Therefore we need to handle them specially.

test/Constraints/implicit_double_cgfloat_conversion.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,18 +194,25 @@ func test_no_ambiguity_with_unary_operators(width: CGFloat, height: CGFloat) {
194194
}
195195

196196
func test_conversions_with_optional_promotion(d: Double, cgf: CGFloat) {
197-
func test_double(_: Double??) {}
198-
func test_cgfloat(_: CGFloat??) {}
197+
func test_double(_: Double??, _: Double???) {}
198+
func test_cgfloat(_: CGFloat??, _: CGFloat???) {}
199199

200200
// CHECK: function_ref @$sSd12CoreGraphicsEySdAA7CGFloatVcfC
201201
// CHECK-NEXT: apply
202202
// CHECK-NEXT: enum $Optional<Double>, #Optional.some!enumelt
203203
// CHECK-NEXT: enum $Optional<Optional<Double>>, #Optional.some!enumelt
204-
test_double(cgf)
204+
test_double(cgf, cgf)
205205

206206
// CHECK: function_ref @$s12CoreGraphics7CGFloatVyACSdcfC
207207
// CHECK-NEXT: apply
208208
// CHECK-NEXT: enum $Optional<CGFloat>, #Optional.some!enumelt
209209
// CHECK-NEXT: enum $Optional<Optional<CGFloat>>, #Optional.some!enumelt
210-
test_cgfloat(d)
210+
test_cgfloat(d, d)
211+
}
212+
213+
// https://github.com/apple/swift/issues/59374
214+
func test_multi_argument_conversion_with_optional(d: Double, cgf: CGFloat) {
215+
func test(_: Double, _: CGFloat?) {}
216+
217+
test(cgf, d) // Ok (CGFloat -> Double and Double? -> CGFloat?)
211218
}

0 commit comments

Comments
 (0)