Skip to content

Commit 47541d8

Browse files
committed
[CS] Visit all fixed bindings for constraint re-activation
Start visiting transitive fixed bindings for type variables, and stop visiting adjacencies for `gatherConstraint`'s `AllMentions` mode. This improves performance and fixes a correctness issue with the old implementation where we could fail to re-activate a coercion constraint, and then let invalid code get past Sema, causing either miscompiles or crashes later down the pipeline. Unfortunately this change requires us to temporarily drop the non-ephemeral fix for a couple of fairly obscure cases where the overload hasn't yet been resolved. The logic was previously relying on stale adjacency state in order to re-activate the fix when the overload is bound, but it's not connected on the constraint graph. We need to find a way to connect constraints to unresolved overloads they depend on. Resolves SR-12369.
1 parent 0df4449 commit 47541d8

File tree

8 files changed

+97
-78
lines changed

8 files changed

+97
-78
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9184,16 +9184,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
91849184
ConstraintFix *fix, Type type1, Type type2, ConstraintKind matchKind,
91859185
TypeMatchOptions flags, ConstraintLocatorBuilder locator) {
91869186

9187-
// Local function to form an unsolved result.
9188-
auto formUnsolved = [&] {
9189-
if (flags.contains(TMF_GenerateConstraints)) {
9190-
addUnsolvedConstraint(Constraint::createFixed(
9191-
*this, matchKind, fix, type1, type2, getConstraintLocator(locator)));
9192-
return SolutionKind::Solved;
9193-
}
9194-
return SolutionKind::Unsolved;
9195-
};
9196-
91979187
// Try with the fix.
91989188
TypeMatchOptions subflags =
91999189
getDefaultDecompositionOptions(flags) | TMF_ApplyingFix;
@@ -9317,13 +9307,14 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
93179307
return SolutionKind::Error;
93189308

93199309
return SolutionKind::Solved;
9310+
case ConversionEphemeralness::Unresolved:
93209311
case ConversionEphemeralness::NonEphemeral:
9312+
// FIXME: The unresolved case should form an unsolved constraint rather
9313+
// than being treated as fully solved. This will require a way to connect
9314+
// the unsolved constraint to the type variable for the unresolved
9315+
// overload such that the fix gets re-activated when the overload is
9316+
// bound.
93219317
return SolutionKind::Solved;
9322-
case ConversionEphemeralness::Unresolved:
9323-
// It's possible we don't yet have enough information to know whether
9324-
// the conversion is ephemeral or not, for example if we're dealing with
9325-
// an overload that hasn't yet been resolved.
9326-
return formUnsolved();
93279318
}
93289319
}
93299320

lib/Sema/ConstraintGraph.cpp

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -566,33 +566,40 @@ llvm::TinyPtrVector<Constraint *> ConstraintGraph::gatherConstraints(
566566
acceptConstraintFn(constraint);
567567
};
568568

569-
// Add constraints for the given adjacent type variable.
570569
llvm::SmallPtrSet<TypeVariableType *, 4> typeVars;
570+
llvm::SmallPtrSet<Constraint *, 8> visitedConstraints;
571+
572+
if (kind == GatheringKind::AllMentions) {
573+
// If we've been asked for "all mentions" of a type variable, search for
574+
// constraints involving both it and its fixed bindings.
575+
depthFirstSearch(
576+
*this, typeVar,
577+
[&](TypeVariableType *typeVar) {
578+
return typeVars.insert(typeVar).second;
579+
},
580+
[&](Constraint *constraint) {
581+
if (acceptConstraint(constraint))
582+
constraints.push_back(constraint);
583+
584+
// Don't recurse into the constraint's type variables.
585+
return false;
586+
},
587+
visitedConstraints);
588+
return constraints;
589+
}
571590

572-
// Local function to add constraints
573-
llvm::SmallPtrSet<Constraint *, 4> visitedConstraints;
574-
auto addConstraintsOfAdjacency = [&](TypeVariableType *adjTypeVar) {
575-
ArrayRef<TypeVariableType *> adjTypeVarsToVisit;
576-
switch (kind) {
577-
case GatheringKind::EquivalenceClass:
578-
adjTypeVarsToVisit = adjTypeVar;
579-
break;
580-
581-
case GatheringKind::AllMentions:
582-
adjTypeVarsToVisit
583-
= (*this)[CS.getRepresentative(adjTypeVar)].getEquivalenceClass();
584-
break;
585-
}
591+
// Otherwise only search in the type var's equivalence class and immediate
592+
// fixed bindings.
586593

587-
for (auto adjTypeVarEquiv : adjTypeVarsToVisit) {
588-
if (!typeVars.insert(adjTypeVarEquiv).second)
589-
continue;
594+
// Local function to add constraints.
595+
auto addTypeVarConstraints = [&](TypeVariableType *adjTypeVar) {
596+
if (!typeVars.insert(adjTypeVar).second)
597+
return;
590598

591-
for (auto constraint : (*this)[adjTypeVarEquiv].getConstraints()) {
592-
if (visitedConstraints.insert(constraint).second &&
593-
acceptConstraint(constraint))
594-
constraints.push_back(constraint);
595-
}
599+
for (auto constraint : (*this)[adjTypeVar].getConstraints()) {
600+
if (visitedConstraints.insert(constraint).second &&
601+
acceptConstraint(constraint))
602+
constraints.push_back(constraint);
596603
}
597604
};
598605

@@ -602,31 +609,17 @@ llvm::TinyPtrVector<Constraint *> ConstraintGraph::gatherConstraints(
602609
if (!typeVars.insert(typeVar).second)
603610
continue;
604611

605-
for (auto constraint : (*this)[typeVar].getConstraints()) {
612+
auto &node = (*this)[typeVar];
613+
614+
for (auto constraint : node.getConstraints()) {
606615
if (visitedConstraints.insert(constraint).second &&
607616
acceptConstraint(constraint))
608617
constraints.push_back(constraint);
609618
}
610619

611-
auto &node = (*this)[typeVar];
612-
613620
for (auto adjTypeVar : node.getFixedBindings()) {
614-
addConstraintsOfAdjacency(adjTypeVar);
621+
addTypeVarConstraints(adjTypeVar);
615622
}
616-
617-
switch (kind) {
618-
case GatheringKind::EquivalenceClass:
619-
break;
620-
621-
case GatheringKind::AllMentions:
622-
// Retrieve the constraints from adjacent bindings.
623-
for (auto adjTypeVar : node.getAdjacencies()) {
624-
addConstraintsOfAdjacency(adjTypeVar);
625-
}
626-
627-
break;
628-
}
629-
630623
}
631624

632625
return constraints;

lib/Sema/ConstraintGraph.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,13 @@ class ConstraintGraph {
222222
/// Describes which constraints \c gatherConstraints should gather.
223223
enum class GatheringKind {
224224
/// Gather constraints associated with all of the variables within the
225-
/// same equivalence class as the given type variable.
225+
/// same equivalence class as the given type variable, as well as its
226+
/// immediate fixed bindings.
226227
EquivalenceClass,
227228
/// Gather all constraints that mention this type variable or type variables
228-
/// that it is equivalent to.
229+
/// that it is a fixed binding of. Unlike EquivalenceClass, this looks
230+
/// through transitive fixed bindings. This can be used to find all the
231+
/// constraints that may be affected when binding a type variable.
229232
AllMentions,
230233
};
231234

test/Constraints/bridging.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ func bridgeTupleToAnyObject() {
373373

374374
// Array defaulting and bridging type checking error per rdar://problem/54274245
375375
func rdar54274245(_ arr: [Any]?) {
376-
_ = (arr ?? []) as [NSObject]
376+
_ = (arr ?? []) as [NSObject] // expected-error {{type of expression is ambiguous without more context}}
377377
}
378378

379379
// rdar://problem/60501780 - failed to infer NSString as a value type of a dictionary

test/Constraints/casts.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,40 @@ func test_tuple_casts_no_warn() {
238238
_ = arr as! [(a: Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(a: Foo, Foo)]' always fails}}
239239
_ = tup as! (a: Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(a: Foo, Foo)' always fails}}
240240
}
241+
242+
infix operator ^^^
243+
func ^^^ <T> (lhs: T?, rhs: @autoclosure () -> T) -> T { lhs! }
244+
func ^^^ <T> (lhs: T?, rhs: @autoclosure () -> T?) -> T? { lhs! }
245+
246+
func ohno<T>(_ x: T) -> T? { nil }
247+
248+
// SR-12369: Make sure we don't drop the coercion constraint.
249+
func test_coercions_with_overloaded_operator(str: String, optStr: String?, veryOptString: String????) {
250+
_ = (str ?? "") as String // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}}
251+
_ = (optStr ?? "") as String
252+
253+
_ = (str ?? "") as Int // expected-error {{cannot convert value of type 'String' to type 'Int' in coercion}}
254+
_ = (optStr ?? "") as Int // expected-error {{cannot convert value of type 'String' to type 'Int' in coercion}}
255+
_ = (optStr ?? "") as Int? // expected-error {{cannot convert value of type 'String' to type 'Int?' in coercion}}
256+
257+
_ = (str ^^^ "") as Int // expected-error {{cannot convert value of type 'String' to type 'Int' in coercion}}
258+
_ = (optStr ^^^ "") as Int // expected-error {{cannot convert value of type 'String' to type 'Int' in coercion}}
259+
_ = (optStr ^^^ "") as Int? // expected-error {{cannot convert value of type 'String' to type 'Int?' in coercion}}
260+
261+
_ = ([] ?? []) as String // expected-error {{cannot convert value of type '[Any]' to type 'String' in coercion}}
262+
_ = ([""] ?? []) as [Int: Int] // expected-error {{cannot convert value of type '[String]' to type '[Int : Int]' in coercion}}
263+
_ = (["": ""] ?? [:]) as [Int] // expected-error {{cannot convert value of type '[String : String]' to type '[Int]' in coercion}}
264+
_ = (["": ""] ?? [:]) as Set<Int> // expected-error {{cannot convert value of type '[String : String]' to type 'Set<Int>' in coercion}}
265+
_ = (["": ""] ?? [:]) as Int? // expected-error {{cannot convert value of type '[String : String]' to type 'Int?' in coercion}}
266+
267+
_ = ("" ?? "" ?? "") as String // expected-warning 2{{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}}
268+
_ = ((veryOptString ?? "") ?? "") as String??
269+
_ = ((((veryOptString ?? "") ?? "") ?? "") ?? "") as String
270+
271+
_ = ("" ?? "" ?? "") as Float // expected-error {{cannot convert value of type 'String' to type 'Float' in coercion}}
272+
_ = ((veryOptString ?? "") ?? "") as [String] // expected-error {{cannot convert value of type 'String??' to type '[String]' in coercion}}
273+
_ = ((((veryOptString ?? "") ?? "") ?? "") ?? "") as [String] // expected-error {{cannot convert value of type 'String' to type '[String]' in coercion}}
274+
275+
_ = ohno(ohno(ohno(str))) as String???
276+
_ = ohno(ohno(ohno(str))) as Int // expected-error {{cannot convert value of type 'String???' to type 'Int' in coercion}}
277+
}

test/Sema/diag_non_ephemeral.swift

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,8 @@ takesMutableRaw(&ResilientStruct.staticStoredProperty, 5) // expected-error {{ca
197197
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}}
198198
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
199199

200-
takesMutableRaw(&type(of: topLevelResilientS).staticStoredProperty, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}}
201-
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}}
202-
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
200+
// FIXME: This should also produce an error.
201+
takesMutableRaw(&type(of: topLevelResilientS).staticStoredProperty, 5)
203202

204203
// - Resilient struct or class bases
205204

@@ -221,9 +220,8 @@ takesRaw(&topLevelP.property) // expected-error {{cannot use inout expression he
221220
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
222221
// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
223222

224-
takesRaw(&type(of: topLevelP).staticProperty) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}}
225-
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
226-
// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
223+
// FIXME: This should also produce an error.
224+
takesRaw(&type(of: topLevelP).staticProperty)
227225

228226
takesRaw(&topLevelP[]) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesRaw'}}
229227
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
@@ -404,9 +402,9 @@ func testNonEphemeralInMembers() {
404402
func testNonEphemeralInDotMember() {
405403
func takesMutableS2(@_nonEphemeral _ ptr: UnsafeMutablePointer<S2>) {}
406404
takesMutableS2(&.selfProp)
407-
takesMutableS2(&.selfPropWithObserver) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableS2'}}
408-
// expected-note@-1 {{implicit argument conversion from 'S2' to 'UnsafeMutablePointer<S2>' produces a pointer valid only for the duration of the call to 'takesMutableS2'}}
409-
// expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
405+
406+
// FIXME: This should also produce an error.
407+
takesMutableS2(&.selfPropWithObserver)
410408
}
411409

412410
func testNonEphemeralWithVarOverloads() {

test/Sema/diag_non_ephemeral_warning.swift

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,8 @@ takesMutableRaw(&ResilientStruct.staticStoredProperty, 5) // expected-warning {{
197197
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}}
198198
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
199199

200-
takesMutableRaw(&type(of: topLevelResilientS).staticStoredProperty, 5) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableRaw'}}
201-
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}}
202-
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
200+
// FIXME: This should also produce a warning.
201+
takesMutableRaw(&type(of: topLevelResilientS).staticStoredProperty, 5)
203202

204203
// - Resilient struct or class bases
205204

@@ -221,9 +220,8 @@ takesRaw(&topLevelP.property) // expected-warning {{inout expression creates a t
221220
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
222221
// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
223222

224-
takesRaw(&type(of: topLevelP).staticProperty) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}}
225-
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
226-
// expected-note@-2 {{use 'withUnsafeBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
223+
// FIXME: This should also produce a warning.
224+
takesRaw(&type(of: topLevelP).staticProperty)
227225

228226
takesRaw(&topLevelP[]) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesRaw'}}
229227
// expected-note@-1 {{implicit argument conversion from 'Int8' to 'UnsafeRawPointer' produces a pointer valid only for the duration of the call to 'takesRaw'}}
@@ -404,9 +402,9 @@ func testNonEphemeralInMembers() {
404402
func testNonEphemeralInDotMember() {
405403
func takesMutableS2(@_nonEphemeral _ ptr: UnsafeMutablePointer<S2>) {}
406404
takesMutableS2(&.selfProp)
407-
takesMutableS2(&.selfPropWithObserver) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'takesMutableS2'}}
408-
// expected-note@-1 {{implicit argument conversion from 'S2' to 'UnsafeMutablePointer<S2>' produces a pointer valid only for the duration of the call to 'takesMutableS2'}}
409-
// expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
405+
406+
// FIXME: This should also produce a warning.
407+
takesMutableS2(&.selfPropWithObserver)
410408
}
411409

412410
func testNonEphemeralWithVarOverloads() {

validation-test/Sema/type_checker_perf/slow/sr139.swift renamed to validation-test/Sema/type_checker_perf/fast/sr139.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@
33

44
// SR-139:
55
// Infinite recursion parsing bitwise operators
6-
// expected-error@+1 {{unable to type-check this expression in reasonable time}}
76
let x = UInt32(0x1FF)&0xFF << 24 | UInt32(0x1FF)&0xFF << 16 | UInt32(0x1FF)&0xFF << 8 | (UInt32(0x1FF)&0xFF)
87

0 commit comments

Comments
 (0)