Skip to content

Commit e7fe2a7

Browse files
committed
Prevent non-ephemeral fix from affecting overload resolution
This commit changes the behaviour of the error for passing a temporary pointer conversion to an @_nonEphemeral parameter such that it doesn't affect overload resolution. This is done by recording the fix with an impact of zero, meaning that we don't touch the solution's score. In addition, this change means we no longer need to perform the ranking hack where we favour array-to-pointer, as the disjunction short-circuiting will continue to happen even with the fix recorded.
1 parent 8cccbe0 commit e7fe2a7

File tree

6 files changed

+35
-37
lines changed

6 files changed

+35
-37
lines changed

lib/Sema/CSRanking.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) {
3535
unsigned index = static_cast<unsigned>(kind);
3636
CurrentScore.Data[index] += value;
3737

38-
if (getASTContext().LangOpts.DebugConstraintSolver) {
38+
if (getASTContext().LangOpts.DebugConstraintSolver && value > 0) {
3939
auto &log = getASTContext().TypeCheckerDebug->getStream();
4040
if (solverState)
4141
log.indent(solverState->depth * 2);

lib/Sema/CSSimplify.cpp

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7933,14 +7933,15 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) {
79337933

79347934
// Record the fix.
79357935

7936-
// If this is just a warning it's shouldn't affect the solver.
7937-
if (!fix->isWarning()) {
7938-
// Otherswise increase the score. If this would make the current
7939-
// solution worse than the best solution we've seen already, stop now.
7936+
// If this is just a warning, it shouldn't affect the solver. Otherwise,
7937+
// increase the score.
7938+
if (!fix->isWarning())
79407939
increaseScore(SK_Fix, impact);
7941-
if (worseThanBestSolution())
7942-
return true;
7943-
}
7940+
7941+
// If we've made the current solution worse than the best solution we've seen
7942+
// already, stop now.
7943+
if (worseThanBestSolution())
7944+
return true;
79447945

79457946
if (isAugmentingFix(fix)) {
79467947
// Always useful, unless duplicate of exactly the same fix and location.
@@ -8084,30 +8085,13 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
80848085
// note of the fix.
80858086
auto conversion = theFix->getConversionKind();
80868087
switch (isConversionEphemeral(conversion, locator)) {
8087-
case ConversionEphemeralness::Ephemeral: {
8088-
if (recordFix(fix))
8088+
case ConversionEphemeralness::Ephemeral:
8089+
// Record the fix with an impact of zero. This ensures that non-ephemeral
8090+
// diagnostics don't impact solver behavior.
8091+
if (recordFix(fix, /*impact*/ 0))
80898092
return SolutionKind::Error;
80908093

8091-
// FIXME: Currently, as a performance hack, we short-circuit disjunctions
8092-
// such that we favour array-to-pointer conversions over inout-to-pointer
8093-
// conversions. However we don't do this when fixes are involved.
8094-
// Therefore in order to improve diagnostics for cases where both
8095-
// array-to-pointer and inout-to-pointer are viable with fixes, increase
8096-
// the score of inout-to-pointer by 1. This means we'll complain about
8097-
// the use of array-to-pointer, which is more likely to be what the user
8098-
// was trying to do.
8099-
//
8100-
// Ideally, we would either have a proper ranking rule that favors
8101-
// array-to-pointer over inout-to-pointer (not just within a disjunction),
8102-
// or we would have some logic to merge solutions with fixes that would
8103-
// be better diagnosed by a single fix rather than an ambiguity error
8104-
// with notes from fixes.
8105-
if (!theFix->isWarning() &&
8106-
conversion == ConversionRestrictionKind::InoutToPointer)
8107-
increaseScore(SK_ValueToPointerConversion);
8108-
81098094
return SolutionKind::Solved;
8110-
}
81118095
case ConversionEphemeralness::NonEphemeral:
81128096
return SolutionKind::Solved;
81138097
case ConversionEphemeralness::Unresolved:

test/Sema/Inputs/diag_non_ephemeral_module1.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public var globalFragile: Int8 = 0
3333
public var overloadedVar = 0
3434

3535
@_fixed_layout
36-
public var overloadedVarOnlyOneViable = 0
36+
public var overloadedVarOnlyOneResilient = 0
3737

3838
@_fixed_layout
3939
public var overloadedVarDifferentTypes = 0

test/Sema/Inputs/diag_non_ephemeral_module2.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
@_fixed_layout
33
public var overloadedVar = 0
44

5-
// Resilient, therefore not viable.
6-
public var overloadedVarOnlyOneViable = 0
5+
// Resilient, therefore produces ephemeral pointer.
6+
public var overloadedVarOnlyOneResilient = 0
77

88
@_fixed_layout
99
public var overloadedVarDifferentTypes = ""

test/Sema/diag_non_ephemeral.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,10 @@ func testNonEphemeralInDotMember() {
411411

412412
func testNonEphemeralWithVarOverloads() {
413413
takesRaw(&overloadedVar) // expected-error {{ambiguous use of 'overloadedVar'}}
414-
takesRaw(&overloadedVarOnlyOneViable)
414+
415+
// Even though only one of the overloads produces an ephemeral pointer, the
416+
// diagnostic doesn't affect solver behaviour, so we diagnose an ambiguity.
417+
takesRaw(&overloadedVarOnlyOneResilient) // expected-error {{ambiguous use of 'overloadedVarOnlyOneResilient'}}
415418

416419
takesRaw(&overloadedVarDifferentTypes) // expected-error {{ambiguous use of 'overloadedVarDifferentTypes'}}
417420

@@ -488,3 +491,17 @@ func testAmbiguity() {
488491
var arr = [1, 2, 3]
489492
takesPointerOverload(&arr) // expected-error {{no exact matches in call to global function 'takesPointerOverload(x:_:)'}}
490493
}
494+
495+
func takesPointerOverload2(@_nonEphemeral _ ptr: UnsafePointer<Int>) {}
496+
func takesPointerOverload2<T>(_ x: T?) {}
497+
func takesPointerOverload2(_ x: Any) {}
498+
func takesPointerOverload2(_ x: [Int]?) {}
499+
500+
func testNonEphemeralErrorDoesntAffectOverloadResolution() {
501+
// Make sure we always pick the pointer overload, even though the other
502+
// overloads are all viable.
503+
var arr = [1, 2, 3]
504+
takesPointerOverload2(arr) // expected-error {{cannot pass '[Int]' to parameter; argument #1 must be a pointer that outlives the call to 'takesPointerOverload2'}}
505+
// expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer<Int>' produces a pointer valid only for the duration of the call to 'takesPointerOverload2'}}
506+
// expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}}
507+
}

test/Sema/diag_non_ephemeral_warning.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,7 @@ func testNonEphemeralInDotMember() {
411411

412412
func testNonEphemeralWithVarOverloads() {
413413
takesRaw(&overloadedVar) // expected-error {{ambiguous use of 'overloadedVar'}}
414-
415-
// Because non-ephemeral violations are only a warning in this mode, we need to consider both overloads.
416-
takesRaw(&overloadedVarOnlyOneViable) // expected-error {{ambiguous use of 'overloadedVarOnlyOneViable'}}
417-
414+
takesRaw(&overloadedVarOnlyOneResilient) // expected-error {{ambiguous use of 'overloadedVarOnlyOneResilient'}}
418415
takesRaw(&overloadedVarDifferentTypes) // expected-error {{ambiguous use of 'overloadedVarDifferentTypes'}}
419416

420417
func takesIntPtr(@_nonEphemeral _ ptr: UnsafePointer<Int>) {}

0 commit comments

Comments
 (0)