Skip to content

Commit ef98b39

Browse files
authored
Merge pull request swiftlang#28141 from xedin/sr-8411
[TypeChecker] Disambiguite cases of implicit pointer conversions with…
2 parents 7e1ed77 + 3feb25c commit ef98b39

File tree

4 files changed

+61
-31
lines changed

4 files changed

+61
-31
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2215,7 +2215,6 @@ static bool isStringCompatiblePointerBaseType(ASTContext &ctx,
22152215
/// is potentially more optional than the second type with its number of
22162216
/// optionals.
22172217
static bool isPotentiallyMoreOptionalThan(Type type1, Type type2) {
2218-
22192218
SmallVector<Type, 2> optionals1;
22202219
Type objType1 = type1->lookThroughAllOptionalTypes(optionals1);
22212220
auto numOptionals1 = optionals1.size();
@@ -7682,11 +7681,17 @@ ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint(
76827681
return SolutionKind::Solved;
76837682
}
76847683

7685-
static Type getBaseTypeForPointer(ConstraintSystem &cs, TypeBase *type) {
7686-
auto pointeeTy = type->lookThroughSingleOptionalType()
7687-
->getAnyPointerElementType();
7684+
static llvm::PointerIntPair<Type, 3, unsigned>
7685+
getBaseTypeForPointer(TypeBase *type) {
7686+
unsigned unwrapCount = 0;
7687+
while (auto objectTy = type->getOptionalObjectType()) {
7688+
type = objectTy.getPointer();
7689+
++unwrapCount;
7690+
}
7691+
7692+
auto pointeeTy = type->getAnyPointerElementType();
76887693
assert(pointeeTy);
7689-
return pointeeTy;
7694+
return {pointeeTy, unwrapCount};
76907695
}
76917696

76927697
void ConstraintSystem::addRestrictedConstraint(
@@ -7879,22 +7884,26 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
78797884
auto t2 = type2->getDesugaredType();
78807885

78817886
auto baseType1 = getFixedTypeRecursive(*isArrayType(obj1), false);
7882-
auto baseType2 = getBaseTypeForPointer(*this, t2);
7887+
auto ptr2 = getBaseTypeForPointer(t2);
78837888

7884-
return matchPointerBaseTypes(baseType1, baseType2);
7889+
increaseScore(SK_ValueToOptional, ptr2.getInt());
7890+
7891+
return matchPointerBaseTypes(baseType1, ptr2.getPointer());
78857892
}
78867893

78877894
// String ===> UnsafePointer<[U]Int8>
78887895
case ConversionRestrictionKind::StringToPointer: {
78897896
addContextualScore();
78907897

7891-
auto baseType2 = getBaseTypeForPointer(*this, type2->getDesugaredType());
7892-
7898+
auto ptr2 = getBaseTypeForPointer(type2->getDesugaredType());
7899+
7900+
increaseScore(SK_ValueToOptional, ptr2.getInt());
7901+
78937902
// The pointer element type must be void or a byte-sized type.
78947903
// TODO: Handle different encodings based on pointer element type, such as
78957904
// UTF16 for [U]Int16 or UTF32 for [U]Int32. For now we only interop with
78967905
// Int8 pointers using UTF8 encoding.
7897-
baseType2 = getFixedTypeRecursive(baseType2, false);
7906+
auto baseType2 = getFixedTypeRecursive(ptr2.getPointer(), false);
78987907
// If we haven't resolved the element type, generate constraints.
78997908
if (baseType2->isTypeVariableOrMember()) {
79007909
if (flags.contains(TMF_GenerateConstraints)) {
@@ -7934,22 +7943,24 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
79347943
addContextualScore();
79357944

79367945
auto t2 = type2->getDesugaredType();
7937-
7946+
79387947
auto baseType1 = type1->getInOutObjectType();
7939-
auto baseType2 = getBaseTypeForPointer(*this, t2);
7948+
auto ptr2 = getBaseTypeForPointer(t2);
79407949

7941-
return matchPointerBaseTypes(baseType1, baseType2);
7950+
increaseScore(SK_ValueToOptional, ptr2.getInt());
7951+
7952+
return matchPointerBaseTypes(baseType1, ptr2.getPointer());
79427953
}
79437954

79447955
// T <p U ===> UnsafeMutablePointer<T> <a UnsafeMutablePointer<U>
79457956
case ConversionRestrictionKind::PointerToPointer: {
79467957
auto t1 = type1->getDesugaredType();
79477958
auto t2 = type2->getDesugaredType();
7948-
7949-
Type baseType1 = getBaseTypeForPointer(*this, t1);
7950-
Type baseType2 = getBaseTypeForPointer(*this, t2);
79517959

7952-
return matchPointerBaseTypes(baseType1, baseType2);
7960+
auto ptr1 = getBaseTypeForPointer(t1);
7961+
auto ptr2 = getBaseTypeForPointer(t2);
7962+
7963+
return matchPointerBaseTypes(ptr1.getPointer(), ptr2.getPointer());
79537964
}
79547965

79557966
// T < U or T is bridged to V where V < U ===> Array<T> <c Array<U>

test/Constraints/optional.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,3 +373,24 @@ func rdar_53238058() {
373373
// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
374374
}
375375
}
376+
377+
// SR-8411 - Inconsistent ambiguity with optional and non-optional inout-to-pointer
378+
func sr8411() {
379+
struct S {
380+
init(_ x: UnsafeMutablePointer<Int>) {}
381+
init(_ x: UnsafeMutablePointer<Int>?) {}
382+
383+
static func foo(_ x: UnsafeMutablePointer<Int>) {}
384+
static func foo(_ x: UnsafeMutablePointer<Int>?) {}
385+
386+
static func bar(_ x: UnsafeMutablePointer<Int>, _ y: Int) {}
387+
static func bar(_ x: UnsafeMutablePointer<Int>?, _ y: Int) {}
388+
}
389+
390+
var foo = 0
391+
392+
_ = S(&foo) // Ok
393+
_ = S.init(&foo) // Ok
394+
_ = S.foo(&foo) // Ok
395+
_ = S.bar(&foo, 42) // Ok
396+
}

test/stdlib/UnsafePointerDiagnostics.swift

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,18 +152,17 @@ func unsafePointerInitEphemeralConversions() {
152152
// expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer<Int>' produces a pointer valid only for the duration of the call to '+'}}
153153
// expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
154154

155-
// FIXME(SR-8411): This is currently ambiguous. The reason why we don't get
156-
// the usual "no exact matches in call to initializer" error is we cannot
157-
// currently diagnose ambiguities between solutions with multiple fixes, so we
158-
// leave it up to CSDiag.
159-
_ = UnsafePointer.init(&foo) // expected-error {{ambiguous reference to member 'init(_:)'}}
155+
_ = UnsafePointer.init(&foo) // expected-error {{initialization of 'UnsafePointer<Int>' results in a dangling pointer}}
156+
// expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer<Int>' produces a pointer valid only for the duration of the call to 'init(_:)'}}
157+
// expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
160158

161159
_ = UnsafePointer<Int8>("") // expected-error {{initialization of 'UnsafePointer<Int8>' results in a dangling pointer}}
162160
// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer<Int8>' produces a pointer valid only for the duration of the call to 'init(_:)'}}
163161
// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}}
164162

165-
// FIXME(SR-8411): This is currently ambiguous.
166-
_ = UnsafePointer<Int8>.init("") // expected-error {{no exact matches in call to initializer}}
163+
_ = UnsafePointer<Int8>.init("") // expected-error {{initialization of 'UnsafePointer<Int8>' results in a dangling pointer}}
164+
// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer<Int8>' produces a pointer valid only for the duration of the call to 'init(_:)'}}
165+
// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}}
167166

168167
_ = UnsafePointer<Int8>(str) // expected-error {{initialization of 'UnsafePointer<Int8>' results in a dangling pointer}}
169168
// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer<Int8>' produces a pointer valid only for the duration of the call to 'init(_:)'}}

test/stdlib/UnsafePointerDiagnostics_warning.swift

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,17 @@ func unsafePointerInitEphemeralConversions() {
1717
// expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer<Int>' produces a pointer valid only for the duration of the call to '+'}}
1818
// expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
1919

20-
// FIXME(SR-8411): This is currently ambiguous. The reason why we don't get
21-
// the usual "no exact matches in call to initializer" error is we cannot
22-
// currently diagnose ambiguities between solutions with multiple fixes, so we
23-
// leave it up to CSDiag.
24-
_ = UnsafePointer.init(&foo) // expected-error {{ambiguous reference to member 'init(_:)'}}
20+
_ = UnsafePointer.init(&foo) // expected-warning {{initialization of 'UnsafePointer<Int>' results in a dangling pointer}}
21+
// expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer<Int>' produces a pointer valid only for the duration of the call to 'init(_:)'}}
22+
// expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
2523

2624
_ = UnsafePointer<Int8>("") // expected-warning {{initialization of 'UnsafePointer<Int8>' results in a dangling pointer}}
2725
// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer<Int8>' produces a pointer valid only for the duration of the call to 'init(_:)'}}
2826
// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}}
2927

30-
// FIXME(SR-8411): This is currently ambiguous.
31-
_ = UnsafePointer<Int8>.init("") // expected-error {{no exact matches in call to initializer}}
28+
_ = UnsafePointer<Int8>.init("") // expected-warning {{initialization of 'UnsafePointer<Int8>' results in a dangling pointer}}
29+
// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer<Int8>' produces a pointer valid only for the duration of the call to 'init(_:)'}}
30+
// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}}
3231

3332
_ = UnsafePointer<Int8>(str) // expected-warning {{initialization of 'UnsafePointer<Int8>' results in a dangling pointer}}
3433
// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer<Int8>' produces a pointer valid only for the duration of the call to 'init(_:)'}}

0 commit comments

Comments
 (0)