Skip to content

Commit 10d62a1

Browse files
authored
[CSDiagnostics] Allow non-ephemeral diag to refer to argument… (#28084)
[CSDiagnostics] Allow non-ephemeral diag to refer to argument labels
2 parents 3ecc13b + 96e5f2a commit 10d62a1

File tree

7 files changed

+102
-36
lines changed

7 files changed

+102
-36
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -401,18 +401,18 @@ ERROR(cannot_convert_argument_value_generic,none,
401401

402402
// @_nonEphemeral conversion diagnostics
403403
ERROR(cannot_pass_type_to_non_ephemeral,none,
404-
"cannot pass %0 to parameter; argument #%1 must be a pointer that "
405-
"outlives the call%select{| to %3}2", (Type, unsigned, bool, DeclName))
404+
"cannot pass %0 to parameter; argument %1 must be a pointer that "
405+
"outlives the call%select{| to %3}2", (Type, StringRef, bool, DeclName))
406406
WARNING(cannot_pass_type_to_non_ephemeral_warning,none,
407-
"passing %0 to parameter, but argument #%1 should be a pointer that "
408-
"outlives the call%select{| to %3}2", (Type, unsigned, bool, DeclName))
407+
"passing %0 to parameter, but argument %1 should be a pointer that "
408+
"outlives the call%select{| to %3}2", (Type, StringRef, bool, DeclName))
409409
ERROR(cannot_use_inout_non_ephemeral,none,
410-
"cannot use inout expression here; argument #%0 must be a pointer that "
411-
"outlives the call%select{| to %2}1", (unsigned, bool, DeclName))
410+
"cannot use inout expression here; argument %0 must be a pointer that "
411+
"outlives the call%select{| to %2}1", (StringRef, bool, DeclName))
412412
WARNING(cannot_use_inout_non_ephemeral_warning,none,
413-
"inout expression creates a temporary pointer, but argument #%0 should "
413+
"inout expression creates a temporary pointer, but argument %0 should "
414414
"be a pointer that outlives the call%select{| to %2}1",
415-
(unsigned, bool, DeclName))
415+
(StringRef, bool, DeclName))
416416
ERROR(cannot_construct_dangling_pointer,none,
417417
"initialization of %0 results in a dangling %select{|buffer }1pointer",
418418
(Type, unsigned))

lib/Sema/CSDiagnostics.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,8 @@ FailureDiagnostic::getFunctionArgApplyInfo(ConstraintLocator *locator) const {
286286
auto argIdx = applyArgElt->getArgIdx();
287287
auto paramIdx = applyArgElt->getParamIdx();
288288

289-
return FunctionArgApplyInfo(argExpr, argIdx, getType(argExpr), paramIdx,
290-
fnInterfaceType, fnType, callee);
289+
return FunctionArgApplyInfo(cs, argExpr, argIdx, getType(argExpr),
290+
paramIdx, fnInterfaceType, fnType, callee);
291291
}
292292

293293
Type FailureDiagnostic::restoreGenericParameters(
@@ -5680,21 +5680,24 @@ bool NonEphemeralConversionFailure::diagnoseAsError() {
56805680
return true;
56815681

56825682
// Otherwise, emit a more general diagnostic.
5683+
SmallString<8> scratch;
5684+
auto argDesc = getArgDescription(scratch);
5685+
56835686
auto *argExpr = getArgExpr();
56845687
if (isa<InOutExpr>(argExpr)) {
56855688
auto diagID = DowngradeToWarning
56865689
? diag::cannot_use_inout_non_ephemeral_warning
56875690
: diag::cannot_use_inout_non_ephemeral;
56885691

5689-
emitDiagnostic(argExpr->getLoc(), diagID, getArgPosition(), getCallee(),
5692+
emitDiagnostic(argExpr->getLoc(), diagID, argDesc, getCallee(),
56905693
getCalleeFullName())
56915694
.highlight(argExpr->getSourceRange());
56925695
} else {
56935696
auto diagID = DowngradeToWarning
56945697
? diag::cannot_pass_type_to_non_ephemeral_warning
56955698
: diag::cannot_pass_type_to_non_ephemeral;
56965699

5697-
emitDiagnostic(argExpr->getLoc(), diagID, getArgType(), getArgPosition(),
5700+
emitDiagnostic(argExpr->getLoc(), diagID, getArgType(), argDesc,
56985701
getCallee(), getCalleeFullName())
56995702
.highlight(argExpr->getSourceRange());
57005703
}

lib/Sema/CSDiagnostics.h

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ class FailureDiagnostic {
212212
/// Provides information about the application of a function argument to a
213213
/// parameter.
214214
class FunctionArgApplyInfo {
215+
ConstraintSystem &CS;
215216
Expr *ArgExpr;
216217
unsigned ArgIdx;
217218
Type ArgType;
@@ -223,11 +224,12 @@ class FunctionArgApplyInfo {
223224
const ValueDecl *Callee;
224225

225226
public:
226-
FunctionArgApplyInfo(Expr *argExpr, unsigned argIdx, Type argType,
227-
unsigned paramIdx, Type fnInterfaceType,
227+
FunctionArgApplyInfo(ConstraintSystem &cs, Expr *argExpr, unsigned argIdx,
228+
Type argType, unsigned paramIdx, Type fnInterfaceType,
228229
FunctionType *fnType, const ValueDecl *callee)
229-
: ArgExpr(argExpr), ArgIdx(argIdx), ArgType(argType), ParamIdx(paramIdx),
230-
FnInterfaceType(fnInterfaceType), FnType(fnType), Callee(callee) {}
230+
: CS(cs), ArgExpr(argExpr), ArgIdx(argIdx), ArgType(argType),
231+
ParamIdx(paramIdx), FnInterfaceType(fnInterfaceType), FnType(fnType),
232+
Callee(callee) {}
231233

232234
/// \returns The argument being applied.
233235
Expr *getArgExpr() const { return ArgExpr; }
@@ -247,6 +249,45 @@ class FunctionArgApplyInfo {
247249
return withSpecifier ? ArgType : ArgType->getWithoutSpecifierType();
248250
}
249251

252+
/// \returns The label for the argument being applied.
253+
Identifier getArgLabel() const {
254+
auto *parent = CS.getParentExpr(ArgExpr);
255+
if (auto *te = dyn_cast<TupleExpr>(parent))
256+
return te->getElementName(ArgIdx);
257+
258+
assert(isa<ParenExpr>(parent));
259+
return Identifier();
260+
}
261+
262+
/// \returns A textual description of the argument suitable for diagnostics.
263+
/// For an argument with an unambiguous label, this will the label. Otherwise
264+
/// it will be its position in the argument list.
265+
StringRef getArgDescription(SmallVectorImpl<char> &scratch) const {
266+
llvm::raw_svector_ostream stream(scratch);
267+
268+
// Use the argument label only if it's unique within the argument list.
269+
auto argLabel = getArgLabel();
270+
auto useArgLabel = [&]() -> bool {
271+
if (argLabel.empty())
272+
return false;
273+
274+
if (auto *te = dyn_cast<TupleExpr>(CS.getParentExpr(ArgExpr)))
275+
return llvm::count(te->getElementNames(), argLabel) == 1;
276+
277+
return false;
278+
};
279+
280+
if (useArgLabel()) {
281+
stream << "'";
282+
stream << argLabel;
283+
stream << "'";
284+
} else {
285+
stream << "#";
286+
stream << getArgPosition();
287+
}
288+
return StringRef(scratch.data(), scratch.size());
289+
}
290+
250291
/// \returns The interface type for the function being applied. Note that this
251292
/// may not a function type, for example it could be a generic parameter.
252293
Type getFnInterfaceType() const { return FnInterfaceType; }
@@ -1847,6 +1888,13 @@ class ArgumentMismatchFailure : public ContextualFailure {
18471888
return Info->getArgType(withSpecifier);
18481889
}
18491890

1891+
/// \returns A textual description of the argument suitable for diagnostics.
1892+
/// For an argument with an unambiguous label, this will the label. Otherwise
1893+
/// it will be its position in the argument list.
1894+
StringRef getArgDescription(SmallVectorImpl<char> &scratch) const {
1895+
return Info->getArgDescription(scratch);
1896+
}
1897+
18501898
/// \returns The interface type for the function being applied.
18511899
Type getFnInterfaceType() const { return Info->getFnInterfaceType(); }
18521900

test/IDE/infer_import_as_member.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,10 @@ func testNonEphemeralInitParams(x: Double) {
126126
var x = x
127127

128128
_ = IAMPointerStruct(ptr1: &x, ptr2: &x)
129-
// expected-error@-1 {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}}
129+
// expected-error@-1 {{cannot use inout expression here; argument 'ptr1' must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}}
130130
// expected-note@-2 {{implicit argument conversion from 'Double' to 'UnsafeMutablePointer<Double>?' produces a pointer valid only for the duration of the call to 'init(ptr1:ptr2:)'}}
131131
// expected-note@-3 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
132-
// expected-error@-4 {{cannot use inout expression here; argument #2 must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}}
132+
// expected-error@-4 {{cannot use inout expression here; argument 'ptr2' must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}}
133133
// expected-note@-5 {{implicit argument conversion from 'Double' to 'UnsafeMutablePointer<Double>?' produces a pointer valid only for the duration of the call to 'init(ptr1:ptr2:)'}}
134134
// expected-note@-6 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
135135

test/IDE/newtype.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,15 +242,15 @@ func testNonEphemeralInitParams(x: OpaquePointer) {
242242
// expected-note@-1 {{implicit argument conversion from 'OpaquePointer' to 'UnsafeMutablePointer<OpaquePointer>' produces a pointer valid only for the duration of the call}}
243243
// expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
244244

245-
_ = TRefRef(rawValue: &x) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'init(rawValue:)'}}
245+
_ = TRefRef(rawValue: &x) // expected-warning {{inout expression creates a temporary pointer, but argument 'rawValue' should be a pointer that outlives the call to 'init(rawValue:)'}}
246246
// expected-note@-1 {{implicit argument conversion from 'OpaquePointer' to 'UnsafeMutablePointer<OpaquePointer>' produces a pointer valid only for the duration of the call}}
247247
// expected-note@-2 {{use 'withUnsafeMutablePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
248248

249249
_ = ConstTRefRef(&x) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'init(_:)'}}
250250
// expected-note@-1 {{implicit argument conversion from 'OpaquePointer' to 'UnsafePointer<OpaquePointer>' produces a pointer valid only for the duration of the call}}
251251
// expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
252252

253-
_ = ConstTRefRef(rawValue: &x) // expected-warning {{inout expression creates a temporary pointer, but argument #1 should be a pointer that outlives the call to 'init(rawValue:)'}}
253+
_ = ConstTRefRef(rawValue: &x) // expected-warning {{inout expression creates a temporary pointer, but argument 'rawValue' should be a pointer that outlives the call to 'init(rawValue:)'}}
254254
// expected-note@-1 {{implicit argument conversion from 'OpaquePointer' to 'UnsafePointer<OpaquePointer>' produces a pointer valid only for the duration of the call}}
255255
// expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
256256
}

test/Sema/diag_non_ephemeral.swift

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -374,29 +374,29 @@ func testNonEphemeralInMembers() {
374374
// expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer<Int>' produces a pointer valid only for the duration of the call to 'takesConstStaticAndReturns'}}
375375
// expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}}
376376

377-
S2.takesMutableRawStatic(ptr: &local) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRawStatic(_:ptr:)'}}
377+
S2.takesMutableRawStatic(ptr: &local) // expected-error {{cannot use inout expression here; argument 'ptr' must be a pointer that outlives the call to 'takesMutableRawStatic(_:ptr:)'}}
378378
// expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRawStatic(_:ptr:)'}}
379379
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
380380

381-
S2.takesMutableRawStatic("", ptr: &local) // expected-error {{cannot use inout expression here; argument #2 must be a pointer that outlives the call to 'takesMutableRawStatic(_:ptr:)'}}
381+
S2.takesMutableRawStatic("", ptr: &local) // expected-error {{cannot use inout expression here; argument 'ptr' must be a pointer that outlives the call to 'takesMutableRawStatic(_:ptr:)'}}
382382
// expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRawStatic(_:ptr:)'}}
383383
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
384384

385385
var s2 = S2()
386386
s2.takesMutableRaw()
387-
s2.takesMutableRaw(ptr: &local) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw(ptr:)'}}
387+
s2.takesMutableRaw(ptr: &local) // expected-error {{cannot use inout expression here; argument 'ptr' must be a pointer that outlives the call to 'takesMutableRaw(ptr:)'}}
388388
// expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw(ptr:)'}}
389389
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
390390

391-
_ = s2[takesConstInt8: ""] // expected-error {{cannot pass 'String' to parameter; argument #1 must be a pointer that outlives the call to 'subscript(takesConstInt8:)'}}
391+
_ = s2[takesConstInt8: ""] // expected-error {{cannot pass 'String' to parameter; argument 'takesConstInt8' must be a pointer that outlives the call to 'subscript(takesConstInt8:)'}}
392392
// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer<Int8>' produces a pointer valid only for the duration of the call to 'subscript(takesConstInt8:)'}}
393393
// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}}
394394

395-
s2[takesConstInt8: ""] += 1 // expected-error {{cannot pass 'String' to parameter; argument #1 must be a pointer that outlives the call to 'subscript(takesConstInt8:)'}}
395+
s2[takesConstInt8: ""] += 1 // expected-error {{cannot pass 'String' to parameter; argument 'takesConstInt8' must be a pointer that outlives the call to 'subscript(takesConstInt8:)'}}
396396
// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer<Int8>' produces a pointer valid only for the duration of the call to 'subscript(takesConstInt8:)'}}
397397
// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}}
398398

399-
_ = \S2.[takesConstInt8: ""] // expected-error {{cannot pass 'String' to parameter; argument #1 must be a pointer that outlives the call to 'subscript(takesConstInt8:)'}}
399+
_ = \S2.[takesConstInt8: ""] // expected-error {{cannot pass 'String' to parameter; argument 'takesConstInt8' must be a pointer that outlives the call to 'subscript(takesConstInt8:)'}}
400400
// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer<Int8>' produces a pointer valid only for the duration of the call to 'subscript(takesConstInt8:)'}}
401401
// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}}
402402
}
@@ -451,11 +451,11 @@ enum E {
451451
func testNonEphemeralInMemberwiseInits() {
452452
var local = 0
453453

454-
_ = S3(ptr1: &topLevelS, ptr2: &local) // expected-error {{cannot use inout expression here; argument #2 must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}}
454+
_ = S3(ptr1: &topLevelS, ptr2: &local) // expected-error {{cannot use inout expression here; argument 'ptr2' must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}}
455455
// expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer?' produces a pointer valid only for the duration of the call to 'init(ptr1:ptr2:)'}}
456456
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
457457

458-
_ = S3.init(ptr1: &local, ptr2: &topLevelS) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}}
458+
_ = S3.init(ptr1: &local, ptr2: &topLevelS) // expected-error {{cannot use inout expression here; argument 'ptr1' must be a pointer that outlives the call to 'init(ptr1:ptr2:)'}}
459459
// expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'init(ptr1:ptr2:)'}}
460460
// expected-note@-2 {{use 'withUnsafeMutableBytes' in order to explicitly convert argument to buffer pointer valid for a defined scope}}
461461

@@ -505,3 +505,18 @@ func testNonEphemeralErrorDoesntAffectOverloadResolution() {
505505
// expected-note@-1 {{implicit argument conversion from '[Int]' to 'UnsafePointer<Int>' produces a pointer valid only for the duration of the call to 'takesPointerOverload2'}}
506506
// expected-note@-2 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}}
507507
}
508+
509+
func takesTwoPointers(@_nonEphemeral ptr _: UnsafePointer<Int>, @_nonEphemeral ptr _: UnsafePointer<Int>) {}
510+
511+
func testArgumentLabelReferencing() {
512+
// Because takesTwoPointers has two argument labels with the same name, refer
513+
// to the argument by position.
514+
var arr = [1, 2, 3]
515+
takesTwoPointers(ptr: arr, ptr: arr)
516+
// expected-error@-1 {{cannot pass '[Int]' to parameter; argument #1 must be a pointer that outlives the call to 'takesTwoPointers(ptr:ptr:)'}}
517+
// expected-note@-2 {{implicit argument conversion from '[Int]' to 'UnsafePointer<Int>' produces a pointer valid only for the duration of the call to 'takesTwoPointers(ptr:ptr:)'}}
518+
// expected-note@-3 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}}
519+
// expected-error@-4 {{cannot pass '[Int]' to parameter; argument #2 must be a pointer that outlives the call to 'takesTwoPointers(ptr:ptr:)'}}
520+
// expected-note@-5 {{implicit argument conversion from '[Int]' to 'UnsafePointer<Int>' produces a pointer valid only for the duration of the call to 'takesTwoPointers(ptr:ptr:)'}}
521+
// expected-note@-6 {{use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}}
522+
}

0 commit comments

Comments
 (0)