Skip to content

Commit 7777e3a

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 (cherry picked from commit 3fd3ff0)
1 parent 2f4f3df commit 7777e3a

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
@@ -3658,6 +3658,12 @@ class ConstraintSystem {
36583658
ConstraintLocator *
36593659
getConstraintLocator(const ConstraintLocatorBuilder &builder);
36603660

3661+
/// Compute a constraint locator for an implicit value-to-value
3662+
/// conversion rooted at the given location.
3663+
ConstraintLocator *
3664+
getImplicitValueConversionLocator(ConstraintLocatorBuilder root,
3665+
ConversionRestrictionKind restriction);
3666+
36613667
/// Lookup and return parent associated with given expression.
36623668
Expr *getParentExpr(Expr *expr) {
36633669
if (auto result = getExprDepthAndParent(expr))

lib/Sema/CSApply.cpp

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

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

67906793
solution.overloadChoices.insert({memberLoc, overload});
67916794
}
@@ -8990,15 +8993,19 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) {
89908993
// If we're supposed to convert the expression to some particular type,
89918994
// do so now.
89928995
if (shouldCoerceToContextualType()) {
8993-
resultExpr =
8994-
Rewriter.coerceToType(resultExpr, solution.simplifyType(convertType),
8995-
cs.getConstraintLocator(resultExpr));
8996+
resultExpr = Rewriter.coerceToType(
8997+
resultExpr, solution.simplifyType(convertType),
8998+
cs.getConstraintLocator(resultExpr,
8999+
LocatorPathElt::ContextualType(
9000+
target.getExprContextualTypePurpose())));
89969001
} else if (cs.getType(resultExpr)->hasLValueType() &&
89979002
!target.isDiscardedExpr()) {
89989003
// We referenced an lvalue. Load it.
89999004
resultExpr = Rewriter.coerceToType(
90009005
resultExpr, cs.getType(resultExpr)->getRValueType(),
9001-
cs.getConstraintLocator(resultExpr));
9006+
cs.getConstraintLocator(resultExpr,
9007+
LocatorPathElt::ContextualType(
9008+
target.getExprContextualTypePurpose())));
90029009
}
90039010

90049011
if (!resultExpr)

lib/Sema/CSSimplify.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12455,28 +12455,26 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
1245512455
if (worseThanBestSolution())
1245612456
return SolutionKind::Error;
1245712457

12458-
auto *conversionLoc = getConstraintLocator(
12459-
/*anchor=*/ASTNode(), LocatorPathElt::ImplicitConversion(restriction));
12458+
auto *conversionLoc =
12459+
getImplicitValueConversionLocator(locator, restriction);
1246012460

1246112461
auto *applicationLoc =
1246212462
getConstraintLocator(conversionLoc, ConstraintLocator::ApplyFunction);
1246312463

1246412464
auto *memberLoc = getConstraintLocator(
1246512465
applicationLoc, ConstraintLocator::ConstructorMember);
1246612466

12467-
// Conversion has been already attempted for this direction
12468-
// and constructor choice has been recorded.
12469-
if (findSelectedOverloadFor(memberLoc))
12470-
return SolutionKind::Solved;
12471-
1247212467
// Allocate a single argument info to cover all possible
1247312468
// Double <-> CGFloat conversion locations.
12474-
if (!ArgumentLists.count(memberLoc)) {
12469+
auto *argumentsLoc =
12470+
getConstraintLocator(conversionLoc, ConstraintLocator::ApplyArgument);
12471+
12472+
if (!ArgumentLists.count(argumentsLoc)) {
1247512473
auto *argList = ArgumentList::createImplicit(
1247612474
getASTContext(), {Argument(SourceLoc(), Identifier(), nullptr)},
1247712475
/*firstTrailingClosureIndex=*/None,
1247812476
AllocationArena::ConstraintSolver);
12479-
ArgumentLists.insert({memberLoc, argList});
12477+
ArgumentLists.insert({argumentsLoc, argList});
1248012478
}
1248112479

1248212480
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)