mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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.
This commit is contained in:
@@ -35,7 +35,7 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) {
|
|||||||
unsigned index = static_cast<unsigned>(kind);
|
unsigned index = static_cast<unsigned>(kind);
|
||||||
CurrentScore.Data[index] += value;
|
CurrentScore.Data[index] += value;
|
||||||
|
|
||||||
if (getASTContext().LangOpts.DebugConstraintSolver) {
|
if (getASTContext().LangOpts.DebugConstraintSolver && value > 0) {
|
||||||
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
||||||
if (solverState)
|
if (solverState)
|
||||||
log.indent(solverState->depth * 2);
|
log.indent(solverState->depth * 2);
|
||||||
|
|||||||
@@ -7933,14 +7933,15 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) {
|
|||||||
|
|
||||||
// Record the fix.
|
// Record the fix.
|
||||||
|
|
||||||
// If this is just a warning it's shouldn't affect the solver.
|
// If this is just a warning, it shouldn't affect the solver. Otherwise,
|
||||||
if (!fix->isWarning()) {
|
// increase the score.
|
||||||
// Otherswise increase the score. If this would make the current
|
if (!fix->isWarning())
|
||||||
// solution worse than the best solution we've seen already, stop now.
|
|
||||||
increaseScore(SK_Fix, impact);
|
increaseScore(SK_Fix, impact);
|
||||||
if (worseThanBestSolution())
|
|
||||||
return true;
|
// If we've made the current solution worse than the best solution we've seen
|
||||||
}
|
// already, stop now.
|
||||||
|
if (worseThanBestSolution())
|
||||||
|
return true;
|
||||||
|
|
||||||
if (isAugmentingFix(fix)) {
|
if (isAugmentingFix(fix)) {
|
||||||
// Always useful, unless duplicate of exactly the same fix and location.
|
// Always useful, unless duplicate of exactly the same fix and location.
|
||||||
@@ -8084,30 +8085,13 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
|
|||||||
// note of the fix.
|
// note of the fix.
|
||||||
auto conversion = theFix->getConversionKind();
|
auto conversion = theFix->getConversionKind();
|
||||||
switch (isConversionEphemeral(conversion, locator)) {
|
switch (isConversionEphemeral(conversion, locator)) {
|
||||||
case ConversionEphemeralness::Ephemeral: {
|
case ConversionEphemeralness::Ephemeral:
|
||||||
if (recordFix(fix))
|
// Record the fix with an impact of zero. This ensures that non-ephemeral
|
||||||
|
// diagnostics don't impact solver behavior.
|
||||||
|
if (recordFix(fix, /*impact*/ 0))
|
||||||
return SolutionKind::Error;
|
return SolutionKind::Error;
|
||||||
|
|
||||||
// FIXME: Currently, as a performance hack, we short-circuit disjunctions
|
|
||||||
// such that we favour array-to-pointer conversions over inout-to-pointer
|
|
||||||
// conversions. However we don't do this when fixes are involved.
|
|
||||||
// Therefore in order to improve diagnostics for cases where both
|
|
||||||
// array-to-pointer and inout-to-pointer are viable with fixes, increase
|
|
||||||
// the score of inout-to-pointer by 1. This means we'll complain about
|
|
||||||
// the use of array-to-pointer, which is more likely to be what the user
|
|
||||||
// was trying to do.
|
|
||||||
//
|
|
||||||
// Ideally, we would either have a proper ranking rule that favors
|
|
||||||
// array-to-pointer over inout-to-pointer (not just within a disjunction),
|
|
||||||
// or we would have some logic to merge solutions with fixes that would
|
|
||||||
// be better diagnosed by a single fix rather than an ambiguity error
|
|
||||||
// with notes from fixes.
|
|
||||||
if (!theFix->isWarning() &&
|
|
||||||
conversion == ConversionRestrictionKind::InoutToPointer)
|
|
||||||
increaseScore(SK_ValueToPointerConversion);
|
|
||||||
|
|
||||||
return SolutionKind::Solved;
|
return SolutionKind::Solved;
|
||||||
}
|
|
||||||
case ConversionEphemeralness::NonEphemeral:
|
case ConversionEphemeralness::NonEphemeral:
|
||||||
return SolutionKind::Solved;
|
return SolutionKind::Solved;
|
||||||
case ConversionEphemeralness::Unresolved:
|
case ConversionEphemeralness::Unresolved:
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public var globalFragile: Int8 = 0
|
|||||||
public var overloadedVar = 0
|
public var overloadedVar = 0
|
||||||
|
|
||||||
@_fixed_layout
|
@_fixed_layout
|
||||||
public var overloadedVarOnlyOneViable = 0
|
public var overloadedVarOnlyOneResilient = 0
|
||||||
|
|
||||||
@_fixed_layout
|
@_fixed_layout
|
||||||
public var overloadedVarDifferentTypes = 0
|
public var overloadedVarDifferentTypes = 0
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
@_fixed_layout
|
@_fixed_layout
|
||||||
public var overloadedVar = 0
|
public var overloadedVar = 0
|
||||||
|
|
||||||
// Resilient, therefore not viable.
|
// Resilient, therefore produces ephemeral pointer.
|
||||||
public var overloadedVarOnlyOneViable = 0
|
public var overloadedVarOnlyOneResilient = 0
|
||||||
|
|
||||||
@_fixed_layout
|
@_fixed_layout
|
||||||
public var overloadedVarDifferentTypes = ""
|
public var overloadedVarDifferentTypes = ""
|
||||||
|
|||||||
@@ -411,7 +411,10 @@ func testNonEphemeralInDotMember() {
|
|||||||
|
|
||||||
func testNonEphemeralWithVarOverloads() {
|
func testNonEphemeralWithVarOverloads() {
|
||||||
takesRaw(&overloadedVar) // expected-error {{ambiguous use of 'overloadedVar'}}
|
takesRaw(&overloadedVar) // expected-error {{ambiguous use of 'overloadedVar'}}
|
||||||
takesRaw(&overloadedVarOnlyOneViable)
|
|
||||||
|
// Even though only one of the overloads produces an ephemeral pointer, the
|
||||||
|
// diagnostic doesn't affect solver behaviour, so we diagnose an ambiguity.
|
||||||
|
takesRaw(&overloadedVarOnlyOneResilient) // expected-error {{ambiguous use of 'overloadedVarOnlyOneResilient'}}
|
||||||
|
|
||||||
takesRaw(&overloadedVarDifferentTypes) // expected-error {{ambiguous use of 'overloadedVarDifferentTypes'}}
|
takesRaw(&overloadedVarDifferentTypes) // expected-error {{ambiguous use of 'overloadedVarDifferentTypes'}}
|
||||||
|
|
||||||
@@ -488,3 +491,17 @@ func testAmbiguity() {
|
|||||||
var arr = [1, 2, 3]
|
var arr = [1, 2, 3]
|
||||||
takesPointerOverload(&arr) // expected-error {{no exact matches in call to global function 'takesPointerOverload(x:_:)'}}
|
takesPointerOverload(&arr) // expected-error {{no exact matches in call to global function 'takesPointerOverload(x:_:)'}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func takesPointerOverload2(@_nonEphemeral _ ptr: UnsafePointer<Int>) {}
|
||||||
|
func takesPointerOverload2<T>(_ x: T?) {}
|
||||||
|
func takesPointerOverload2(_ x: Any) {}
|
||||||
|
func takesPointerOverload2(_ x: [Int]?) {}
|
||||||
|
|
||||||
|
func testNonEphemeralErrorDoesntAffectOverloadResolution() {
|
||||||
|
// Make sure we always pick the pointer overload, even though the other
|
||||||
|
// overloads are all viable.
|
||||||
|
var arr = [1, 2, 3]
|
||||||
|
takesPointerOverload2(arr) // expected-error {{cannot pass '[Int]' to parameter; argument #1 must be a pointer that outlives the call to 'takesPointerOverload2'}}
|
||||||
|
// expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer<Int>' produces a pointer valid only for the duration of the call to 'takesPointerOverload2'}}
|
||||||
|
// expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}}
|
||||||
|
}
|
||||||
|
|||||||
@@ -411,10 +411,7 @@ func testNonEphemeralInDotMember() {
|
|||||||
|
|
||||||
func testNonEphemeralWithVarOverloads() {
|
func testNonEphemeralWithVarOverloads() {
|
||||||
takesRaw(&overloadedVar) // expected-error {{ambiguous use of 'overloadedVar'}}
|
takesRaw(&overloadedVar) // expected-error {{ambiguous use of 'overloadedVar'}}
|
||||||
|
takesRaw(&overloadedVarOnlyOneResilient) // expected-error {{ambiguous use of 'overloadedVarOnlyOneResilient'}}
|
||||||
// Because non-ephemeral violations are only a warning in this mode, we need to consider both overloads.
|
|
||||||
takesRaw(&overloadedVarOnlyOneViable) // expected-error {{ambiguous use of 'overloadedVarOnlyOneViable'}}
|
|
||||||
|
|
||||||
takesRaw(&overloadedVarDifferentTypes) // expected-error {{ambiguous use of 'overloadedVarDifferentTypes'}}
|
takesRaw(&overloadedVarDifferentTypes) // expected-error {{ambiguous use of 'overloadedVarDifferentTypes'}}
|
||||||
|
|
||||||
func takesIntPtr(@_nonEphemeral _ ptr: UnsafePointer<Int>) {}
|
func takesIntPtr(@_nonEphemeral _ ptr: UnsafePointer<Int>) {}
|
||||||
|
|||||||
Reference in New Issue
Block a user