[Strict memory safety] Adjust "unsafe" location for string interpolations

String interpolations can end up being unsafe in the call to
appendInterpolation when it's provided with unsafe types. Move the
location of the proposed "unsafe" out to the string interpolation
itself in these cases, which properly suppresses the warning.

Fixes rdar://151799777.
This commit is contained in:
Doug Gregor
2025-06-02 12:36:36 -07:00
parent e1e9f04398
commit 6ba48f2738
2 changed files with 28 additions and 8 deletions

View File

@@ -3645,9 +3645,9 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
}
/// Find the top location where we should put the await
static Expr *walkToAnchor(Expr *e, llvm::DenseMap<Expr *, Expr *> &parentMap,
bool isInterpolatedString,
bool stopAtAutoClosure) {
Expr *walkToAnchor(Expr *e, llvm::DenseMap<Expr *, Expr *> &parentMap,
InterpolatedStringLiteralExpr *interpolatedString,
bool stopAtAutoClosure, EffectKind effect) {
llvm::SmallPtrSet<Expr *, 4> visited;
Expr *parent = e;
Expr *lastParent = e;
@@ -3662,8 +3662,20 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
if (parent && !isAnchorTooEarly(parent)) {
return parent;
}
if (isInterpolatedString) {
if (interpolatedString) {
assert(parent == nullptr && "Expected to be at top of expression");
// If the last parent we found is a call to appendInterpolation, adjust
// the anchor location to the interpolated string itself.
if (effect == EffectKind::Unsafe) {
if (auto callExpr = dyn_cast<CallExpr>(lastParent)) {
if (auto calleeDecl = callExpr->getCalledValue()) {
if (calleeDecl->getName().matchesRef(Ctx.Id_appendInterpolation))
return interpolatedString;
}
}
}
if (ArgumentList *args = lastParent->getArgs()) {
if (Expr *unaryArg = args->getUnlabeledUnaryExpr())
return unaryArg;
@@ -4322,8 +4334,9 @@ private:
Flags.has(ContextFlags::InAsyncLet))) {
Expr *expr = E.dyn_cast<Expr*>();
Expr *anchor = walkToAnchor(expr, parentMap,
CurContext.isWithinInterpolatedString(),
/*stopAtAutoClosure=*/true);
CurContext.getInterpolatedString(),
/*stopAtAutoClosure=*/true,
EffectKind::Async);
if (Flags.has(ContextFlags::StmtExprCoversAwait))
classification.setDowngradeToWarning(true);
if (uncoveredAsync.find(anchor) == uncoveredAsync.end())
@@ -4348,8 +4361,9 @@ private:
if (!Flags.has(ContextFlags::IsUnsafeCovered)) {
Expr *expr = E.dyn_cast<Expr*>();
Expr *anchor = walkToAnchor(expr, parentMap,
CurContext.isWithinInterpolatedString(),
/*stopAtAutoClosure=*/false);
CurContext.getInterpolatedString(),
/*stopAtAutoClosure=*/false,
EffectKind::Unsafe);
// We don't diagnose uncovered unsafe uses within the next/nextElement
// call, because they're handled already by the for-in loop checking.

View File

@@ -376,3 +376,9 @@ protocol CustomAssociated: Associated { }
extension SomeClass: CustomAssociated {
typealias Associated = SomeClassWrapper // expected-note{{unsafe type 'SomeClass.Associated' (aka 'SomeClassWrapper') cannot satisfy safe associated type 'Associated'}}
}
func testInterpolation(ptr: UnsafePointer<Int>) {
_ = "Hello \(unsafe ptr)" // expected-warning{{expression uses unsafe constructs but is not marked with 'unsafe'}}{{7-7=unsafe }}
// expected-note@-1{{reference to unsafe type 'UnsafePointer<Int>'}}
// expected-note@-2{{argument #0 in call to instance method 'appendInterpolation' has unsafe type 'UnsafePointer<Int>'}}
}