Skip to content

[Strict memory safety] Provide argument-specific diagnostics for calls #81120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 26, 2025
13 changes: 11 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -8341,6 +8341,15 @@ NOTE(note_reference_to_unsafe_decl,none,
NOTE(note_reference_to_unsafe_typed_decl,none,
"%select{reference|call}0 to %kind1 involves unsafe type %2",
(bool, const ValueDecl *, Type))
NOTE(note_unsafe_call_decl_argument_named,none,
"argument %1 in call to %kindbase0 has unsafe type %2",
(const ValueDecl *, Identifier, Type))
NOTE(note_unsafe_call_decl_argument_indexed,none,
"argument #%1 in call to %kindbase0 has unsafe type %2",
(const ValueDecl *, unsigned, Type))
NOTE(note_unsafe_call_argument_indexed,none,
"argument #%0 in call has unsafe type %1",
(unsigned, Type))
NOTE(note_reference_to_unsafe_through_typealias,none,
"reference to %kind0 whose underlying type involves unsafe type %1",
(const ValueDecl *, Type))
Expand Down Expand Up @@ -8402,9 +8411,9 @@ GROUPED_WARNING(unsafe_without_unsafe,StrictMemorySafety,none,
"expression uses unsafe constructs but is not marked with 'unsafe'", ())
GROUPED_WARNING(for_unsafe_without_unsafe,StrictMemorySafety,none,
"for-in loop uses unsafe constructs but is not marked with 'unsafe'", ())
GROUPED_WARNING(no_unsafe_in_unsafe,StrictMemorySafety,none,
WARNING(no_unsafe_in_unsafe,none,
"no unsafe operations occur within 'unsafe' expression", ())
GROUPED_WARNING(no_unsafe_in_unsafe_for,StrictMemorySafety,none,
WARNING(no_unsafe_in_unsafe_for,none,
"no unsafe operations occur within 'unsafe' for-in loop", ())
NOTE(make_subclass_unsafe,none,
"make class %0 '@unsafe' to allow unsafe overrides of safe superclass "
Expand Down
48 changes: 48 additions & 0 deletions include/swift/AST/UnsafeUse.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class UnsafeUse {
ReferenceToUnsafeThroughTypealias,
/// A call to an unsafe declaration.
CallToUnsafe,
/// An unsafe argument in a call.
CallArgument,
/// A @preconcurrency import.
PreconcurrencyImport,
/// A use of withoutActuallyEscaping that lacks enforcement that the
Expand Down Expand Up @@ -91,6 +93,15 @@ class UnsafeUse {

MakeTemporarilyEscapableExpr *temporarilyEscaping;

struct {
Expr *call;
const Decl *calleeDecl;
TypeBase *paramType;
const void *argumentName;
unsigned argumentIndex;
Expr *argument;
} callArgument;

const ImportDecl *importDecl;
} storage;

Expand Down Expand Up @@ -201,6 +212,19 @@ class UnsafeUse {
decl, type, location);
}

static UnsafeUse forCallArgument(
Expr *call, const Decl *calleeDecl, Type paramType,
Identifier argumentName, unsigned argumentIndex, Expr *argument) {
UnsafeUse result(CallArgument);
result.storage.callArgument.call = call;
result.storage.callArgument.calleeDecl = calleeDecl;
result.storage.callArgument.paramType = paramType.getPointer();
result.storage.callArgument.argumentName = argumentName.getAsOpaquePointer();
result.storage.callArgument.argumentIndex = argumentIndex;
result.storage.callArgument.argument = argument;
return result;
}

static UnsafeUse forTemporarilyEscaping(MakeTemporarilyEscapableExpr *expr) {
UnsafeUse result(TemporarilyEscaping);
result.storage.temporarilyEscaping = expr;
Expand Down Expand Up @@ -242,6 +266,9 @@ class UnsafeUse {
return SourceLoc(
llvm::SMLoc::getFromPointer((const char *)storage.entity.location));

case CallArgument:
return storage.callArgument.call->getLoc();

case TemporarilyEscaping:
return storage.temporarilyEscaping->getLoc();

Expand All @@ -257,6 +284,7 @@ class UnsafeUse {
case Witness:
case TemporarilyEscaping:
case PreconcurrencyImport:
case CallArgument:
// Cannot replace location.
return;

Expand Down Expand Up @@ -298,6 +326,9 @@ class UnsafeUse {
case CallToUnsafe:
return storage.entity.decl;

case CallArgument:
return storage.callArgument.calleeDecl;

case UnsafeConformance:
case TemporarilyEscaping:
return nullptr;
Expand Down Expand Up @@ -330,6 +361,7 @@ class UnsafeUse {
case ReferenceToUnsafeThroughTypealias:
case ReferenceToUnsafeStorage:
case CallToUnsafe:
case CallArgument:
case UnsafeConformance:
case PreconcurrencyImport:
case TemporarilyEscaping:
Expand Down Expand Up @@ -360,6 +392,9 @@ class UnsafeUse {
case CallToUnsafe:
return storage.entity.type;

case CallArgument:
return storage.callArgument.paramType;

case TemporarilyEscaping:
return storage.temporarilyEscaping->getOpaqueValue()->getType();
}
Expand All @@ -386,11 +421,24 @@ class UnsafeUse {
case ReferenceToUnsafeStorage:
case ReferenceToUnsafeThroughTypealias:
case CallToUnsafe:
case CallArgument:
case TemporarilyEscaping:
case PreconcurrencyImport:
return ProtocolConformanceRef::forInvalid();
}
}

/// Get information about the call argument.
///
/// Produces the argument name, argument index, and argument expression for
/// a unsafe use describing a call argument.
std::tuple<Identifier, unsigned, Expr *> getCallArgument() const {
assert(getKind() == CallArgument);
return std::make_tuple(
Identifier::getFromOpaquePointer(storage.callArgument.argumentName),
storage.callArgument.argumentIndex,
storage.callArgument.argument);
}
};

} // end namespace swift
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8149,7 +8149,7 @@ Expr *ExprRewriter::convertLiteralInPlace(
Diag<> brokenProtocolDiag, Diag<> brokenBuiltinProtocolDiag) {
// If coercing a literal to an unresolved type, we don't try to look up the
// witness members, just do it.
if (type->is<UnresolvedType>()) {
if (type->is<UnresolvedType>() || type->is<ErrorType>()) {
cs.setType(literal, type);
return literal;
}
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2699,6 +2699,7 @@ diagnoseDeclUnsafe(ConcreteDeclRef declRef, SourceRange R,

SourceLoc diagLoc = call ? call->getLoc() : R.Start;
enumerateUnsafeUses(declRef, diagLoc, call != nullptr,
/*skipTypeCheck=*/false,
[&](UnsafeUse unsafeUse) {
unsafeUses->push_back(unsafeUse);
return false;
Expand Down
Loading