Skip to content

Commit 568ed04

Browse files
committed
fix <rdar://problem/23271868> QoI: Non-descriptive error message in Swift when passing immutable unsafe pointer instead of mutable
Making diagnostics more specific when using & in a call argument as part of an Unsafe*Pointer conversion.
1 parent 0fd81af commit 568ed04

File tree

2 files changed

+69
-20
lines changed

2 files changed

+69
-20
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3856,20 +3856,48 @@ bool FailureDiagnosis::visitAssignExpr(AssignExpr *assignExpr) {
38563856
return false;
38573857
}
38583858

3859+
/// If the specific type is UnsafePointer<T>, UnsafeMutablePointer<T>, or
3860+
/// AutoreleasingUnsafeMutablePointer<T>, return T.
3861+
static Type getPointerElementType(Type ty) {
3862+
// Must be a generic type.
3863+
auto bgt = ty->getAs<BoundGenericType>();
3864+
if (!bgt) return Type();
3865+
3866+
// Must be UnsafeMutablePointer or UnsafePointer.
3867+
auto &ctx = bgt->getASTContext();
3868+
if (bgt->getDecl() != ctx.getUnsafeMutablePointerDecl() &&
3869+
bgt->getDecl() != ctx.getUnsafePointerDecl() &&
3870+
bgt->getDecl() != ctx.getAutoreleasingUnsafeMutablePointerDecl())
3871+
return Type();
3872+
3873+
return bgt->getGenericArgs()[0];
3874+
}
3875+
3876+
3877+
38593878
bool FailureDiagnosis::visitInOutExpr(InOutExpr *IOE) {
38603879
// If we have a contextual type, it must be an inout type.
38613880
auto contextualType = CS->getContextualType();
3862-
38633881
if (contextualType) {
3864-
if (!contextualType->is<InOutType>()) {
3882+
// If the contextual type is one of the UnsafePointer<T> types, then the
3883+
// contextual type of the subexpression must be T.
3884+
if (auto pointerEltType = getPointerElementType(contextualType)) {
3885+
// If the element type is Void, then we allow any input type, since
3886+
// everything is convertable to UnsafePointer<Void>
3887+
if (pointerEltType->isVoid())
3888+
contextualType = Type();
3889+
else
3890+
contextualType = pointerEltType;
3891+
} else if (contextualType->is<InOutType>()) {
3892+
contextualType = contextualType->getInOutObjectType();
3893+
} else {
38653894
// If the caller expected something inout, but we didn't have
38663895
// something of inout type, diagnose it.
38673896
diagnose(IOE->getLoc(), diag::extra_address_of, contextualType)
38683897
.highlight(IOE->getSourceRange())
38693898
.fixItRemove(IOE->getStartLoc());
38703899
return true;
38713900
}
3872-
contextualType = contextualType->getInOutObjectType();
38733901
}
38743902

38753903
auto subExpr = typeCheckChildIndependently(IOE->getSubExpr(), contextualType,

test/Parse/pointer_conversion.swift

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,22 @@ func mutablePointerArguments(p: UnsafeMutablePointer<Int>,
2525
var i: Int = 0
2626
var f: Float = 0
2727
takesMutablePointer(&i)
28-
takesMutablePointer(&f) // expected-error{{'&' used with non-inout argument of type 'UnsafeMutablePointer<Int>'}}
28+
takesMutablePointer(&f) // expected-error{{cannot convert value of type 'Float' to expected argument type 'Int'}}
2929
takesMutablePointer(i) // expected-error{{cannot convert value of type 'Int' to expected argument type 'UnsafeMutablePointer<Int>'}}
3030
takesMutablePointer(f) // expected-error{{cannot convert value of type 'Float' to expected argument type 'UnsafeMutablePointer<Int>'}}
3131
var ii: [Int] = [0, 1, 2]
3232
var ff: [Float] = [0, 1, 2]
3333
takesMutablePointer(&ii)
34-
takesMutablePointer(&ff) // expected-error{{'&' used with non-inout argument of type 'UnsafeMutablePointer<Int>'}}
34+
takesMutablePointer(&ff) // expected-error{{cannot convert value of type '[Float]' to expected argument type 'Int'}}
3535
takesMutablePointer(ii) // expected-error{{cannot convert value of type '[Int]' to expected argument type 'UnsafeMutablePointer<Int>'}}
3636
takesMutablePointer(ff) // expected-error{{cannot convert value of type '[Float]' to expected argument type 'UnsafeMutablePointer<Int>'}}
3737

38-
takesMutableArrayPointer(&i) // expected-error{{'&' used with non-inout argument of type 'UnsafeMutablePointer<[Int]>' (aka 'UnsafeMutablePointer<Array<Int>>')}}
38+
takesMutableArrayPointer(&i) // expected-error{{cannot convert value of type 'Int' to expected argument type '[Int]'}}
3939
takesMutableArrayPointer(&ii)
4040

4141
// We don't allow these conversions outside of function arguments.
42-
var x: UnsafeMutablePointer<Int> = &i // expected-error{{'&' used with non-inout argument of type 'UnsafeMutablePointer<Int>'}}
43-
x = &ii // expected-error{{'&' used with non-inout argument of type 'UnsafeMutablePointer<Int>'}}
42+
var x: UnsafeMutablePointer<Int> = &i // expected-error{{cannot pass immutable value of type 'Int' as inout argument}}
43+
x = &ii // expected-error{{cannot assign value of type '[Int]' to type 'Int'}}
4444
_ = x
4545
}
4646

@@ -69,9 +69,9 @@ func mutableVoidPointerArguments(p: UnsafeMutablePointer<Int>,
6969
takesMutableVoidPointer(ff) // expected-error{{cannot convert value of type '[Int]' to expected argument type 'UnsafeMutablePointer<Void>' (aka 'UnsafeMutablePointer<()>')}}
7070

7171
// We don't allow these conversions outside of function arguments.
72-
var x: UnsafeMutablePointer<Void> = &i // expected-error{{'&' used with non-inout argument of type 'UnsafeMutablePointer<Void>' (aka 'UnsafeMutablePointer<()>')}}
72+
var x: UnsafeMutablePointer<Void> = &i // expected-error{{cannot convert value of type 'inout Int' to specified type 'UnsafeMutablePointer<Void>' (aka 'UnsafeMutablePointer<()>')}}
7373
x = p // expected-error{{cannot assign value of type 'UnsafeMutablePointer<Int>' to type 'UnsafeMutablePointer<Void>' (aka 'UnsafeMutablePointer<()>')}}
74-
x = &ii // expected-error{{'&' used with non-inout argument of type 'UnsafeMutablePointer<Void>' (aka 'UnsafeMutablePointer<()>')}}
74+
x = &ii // expected-error{{cannot assign value of type 'inout [Int]' (aka 'inout Array<Int>') to type 'UnsafeMutablePointer<Void>' (aka 'UnsafeMutablePointer<()>')}}
7575
_ = x
7676
}
7777

@@ -86,19 +86,19 @@ func constPointerArguments(p: UnsafeMutablePointer<Int>,
8686
var i: Int = 0
8787
var f: Float = 0
8888
takesConstPointer(&i)
89-
takesConstPointer(&f) // expected-error{{'&' used with non-inout argument of type 'UnsafePointer<Int>'}}
89+
takesConstPointer(&f) // expected-error{{cannot convert value of type 'Float' to expected argument type 'Int'}}
9090
var ii: [Int] = [0, 1, 2]
9191
var ff: [Float] = [0, 1, 2]
9292
takesConstPointer(&ii)
93-
takesConstPointer(&ff) // expected-error{{'&' used with non-inout argument of type 'UnsafePointer<Int>'}}
93+
takesConstPointer(&ff) // expected-error{{cannot convert value of type '[Float]' to expected argument type 'Int'}}
9494
takesConstPointer(ii)
9595
takesConstPointer(ff) // expected-error{{cannot convert value of type '[Float]' to expected argument type 'UnsafePointer<Int>'}}
9696
takesConstPointer([0, 1, 2])
9797
// <rdar://problem/22308330> QoI: CSDiags doesn't handle array -> pointer impl conversions well
9898
takesConstPointer([0.0, 1.0, 2.0]) // expected-error{{cannot convert value of type 'Double' to expected element type 'Int'}}
9999

100100
// We don't allow these conversions outside of function arguments.
101-
var x: UnsafePointer<Int> = &i // expected-error{{'&' used with non-inout argument of type 'UnsafePointer<Int>'}}
101+
var x: UnsafePointer<Int> = &i // expected-error{{cannot pass immutable value of type 'Int' as inout argument}}
102102
x = ii // expected-error{{cannot assign value of type '[Int]' to type 'UnsafePointer<Int>'}}
103103
x = p // expected-error{{cannot assign value of type 'UnsafeMutablePointer<Int>' to type 'UnsafePointer<Int>'}}
104104
x = ap // expected-error{{cannot assign value of type 'AutoreleasingUnsafeMutablePointer<Int>' to type 'UnsafePointer<Int>'}}
@@ -132,7 +132,7 @@ func constVoidPointerArguments(p: UnsafeMutablePointer<Int>,
132132
takesConstVoidPointer([0.0, 1.0, 2.0]) // expected-error {{cannot convert value of type 'Double' to expected element type '()'}}
133133

134134
// We don't allow these conversions outside of function arguments.
135-
var x: UnsafePointer<Void> = &i // expected-error{{'&' used with non-inout argument of type 'UnsafePointer<Void>' (aka 'UnsafePointer<()>')}}
135+
var x: UnsafePointer<Void> = &i // expected-error{{cannot convert value of type 'inout Int' to specified type 'UnsafePointer<Void>' (aka 'UnsafePointer<()>')}}
136136
x = ii // expected-error{{cannot assign value of type '[Int]' to type 'UnsafePointer<Void>' (aka 'UnsafePointer<()>')}}
137137
x = p // expected-error{{cannot assign value of type 'UnsafeMutablePointer<Int>' to type 'UnsafePointer<Void>' (aka 'UnsafePointer<()>')}}
138138
x = fp // expected-error{{cannot assign value of type 'UnsafeMutablePointer<Float>' to type 'UnsafePointer<Void>' (aka 'UnsafePointer<()>')}}
@@ -152,9 +152,9 @@ func stringArguments(s: String) {
152152

153153
takesMutableVoidPointer(s) // expected-error{{cannot convert value of type 'String' to expected argument type 'UnsafeMutablePointer<Void>' (aka 'UnsafeMutablePointer<()>')}}
154154
takesMutableInt8Pointer(s) // expected-error{{cannot convert value of type 'String' to expected argument type 'UnsafeMutablePointer<Int8>'}}
155-
takesMutableInt8Pointer(&s) // expected-error{{'&' used with non-inout argument of type 'UnsafeMutablePointer<Int8>'}}
155+
takesMutableInt8Pointer(&s) // expected-error{{cannot convert value of type 'String' to expected argument type 'Int8'}}
156156
takesMutablePointer(s) // expected-error{{cannot convert value of type 'String' to expected argument type 'UnsafeMutablePointer<Int>'}}
157-
takesMutablePointer(&s) // expected-error{{'&' used with non-inout argument of type 'UnsafeMutablePointer<Int>'}}
157+
takesMutablePointer(&s) // expected-error{{cannot convert value of type 'String' to expected argument type 'Int'}}
158158
}
159159

160160
func autoreleasingPointerArguments(p: UnsafeMutablePointer<Int>,
@@ -169,14 +169,14 @@ func autoreleasingPointerArguments(p: UnsafeMutablePointer<Int>,
169169
takesAutoreleasingPointer(&c)
170170
takesAutoreleasingPointer(c) // expected-error{{cannot convert value of type 'C' to expected argument type 'AutoreleasingUnsafeMutablePointer<C>'}}
171171
var d: D = D()
172-
takesAutoreleasingPointer(&d) // expected-error{{'&' used with non-inout argument of type 'AutoreleasingUnsafeMutablePointer<C>'}}
172+
takesAutoreleasingPointer(&d) // expected-error{{cannot convert value of type 'D' to expected argument type 'C'}}
173173
takesAutoreleasingPointer(d) // expected-error{{cannot convert value of type 'D' to expected argument type 'AutoreleasingUnsafeMutablePointer<C>'}}
174174
var cc: [C] = [C(), C()]
175175
var dd: [D] = [D(), D()]
176-
takesAutoreleasingPointer(&cc) // expected-error{{'&' used with non-inout argument of type 'AutoreleasingUnsafeMutablePointer<C>'}}
177-
takesAutoreleasingPointer(&dd) // expected-error{{'&' used with non-inout argument of type 'AutoreleasingUnsafeMutablePointer<C>'}}
176+
takesAutoreleasingPointer(&cc) // expected-error{{cannot convert value of type '[C]' to expected argument type 'C'}}
177+
takesAutoreleasingPointer(&dd) // expected-error{{cannot convert value of type '[D]' to expected argument type 'C'}}
178178

179-
let _: AutoreleasingUnsafeMutablePointer<C> = &c // expected-error{{'&' used with non-inout argument of type 'AutoreleasingUnsafeMutablePointer<C>'}}
179+
let _: AutoreleasingUnsafeMutablePointer<C> = &c // expected-error{{cannot pass immutable value of type 'C' as inout argument}}
180180
}
181181

182182
func pointerConstructor(x: UnsafeMutablePointer<Int>) -> UnsafeMutablePointer<Float> {
@@ -215,3 +215,24 @@ func addressConversion(p: UnsafeMutablePointer<Int>, x: Int) {
215215
var x = x
216216
let _: Bool = p == &x
217217
}
218+
219+
// <rdar://problem/23271868> QoI: Non-descriptive error message in Swift when passing immutable unsafe pointer instead of mutable
220+
func f23271868() {
221+
var viewport: Int = 1 // intentionally incorrect type, not Int32
222+
func GLKProject(a : UnsafeMutablePointer<Int32>) {}
223+
GLKProject(&viewport) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Int32'}}
224+
225+
func GLKProjectUP(a : UnsafePointer<Int32>) {}
226+
func UP_Void(a : UnsafePointer<Void>) {}
227+
func UMP_Void(a : UnsafeMutablePointer<Void>) {}
228+
UP_Void(&viewport)
229+
UMP_Void(&viewport)
230+
231+
let cst = 42 // expected-note 2 {{change 'let' to 'var' to make it mutable}}
232+
UP_Void(&cst) // expected-error {{cannot pass immutable value as inout argument: 'cst' is a 'let' constant}}
233+
UMP_Void(&cst) // expected-error {{cannot pass immutable value as inout argument: 'cst' is a 'let' constant}}
234+
}
235+
236+
237+
238+

0 commit comments

Comments
 (0)