Skip to content

Commit df3f45a

Browse files
authored
Merge pull request #78198 from DougGregor/unsafe-fixes
Collected fixes for `@unsafe` and the Fix-Its it emits
2 parents 349c3f1 + 62d8659 commit df3f45a

File tree

12 files changed

+183
-54
lines changed

12 files changed

+183
-54
lines changed

include/swift/AST/DeclAttr.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ CONTEXTUAL_DECL_ATTR(weak, ReferenceOwnership,
429429
49)
430430
CONTEXTUAL_DECL_ATTR_ALIAS(unowned, ReferenceOwnership)
431431
SIMPLE_DECL_ATTR(rethrows, Rethrows,
432-
OnFunc | OnConstructor | RejectByParser | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove,
432+
OnFunc | OnConstructor | DeclModifier | RejectByParser | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove,
433433
57)
434434
CONTEXTUAL_SIMPLE_DECL_ATTR(indirect, Indirect,
435435
DeclModifier | OnEnum | OnEnumElement | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
@@ -505,7 +505,7 @@ SIMPLE_DECL_ATTR(sensitive, Sensitive,
505505

506506
SIMPLE_DECL_ATTR(unsafe, Unsafe,
507507
OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType |
508-
OnExtension | OnTypeAlias | UserInaccessible |
508+
OnExtension | OnTypeAlias | OnEnumElement | UserInaccessible |
509509
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
510510
160)
511511

lib/AST/Attr.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,12 +861,26 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options,
861861
DA->print(Printer, Options, D);
862862
}
863863

864+
static bool attributeIsNotAtStart(const DeclAttribute *attr) {
865+
switch (attr->getKind()) {
866+
case DeclAttrKind::Rethrows:
867+
case DeclAttrKind::Reasync:
868+
return true;
869+
870+
default:
871+
return false;
872+
}
873+
}
874+
864875
SourceLoc DeclAttributes::getStartLoc(bool forModifiers) const {
865876
if (isEmpty())
866877
return SourceLoc();
867878

868879
const DeclAttribute *lastAttr = nullptr;
869880
for (auto attr : *this) {
881+
if (attributeIsNotAtStart(attr))
882+
continue;
883+
870884
if (attr->getRangeWithAt().Start.isValid() &&
871885
(!forModifiers || attr->isDeclModifier()))
872886
lastAttr = attr;
@@ -1584,6 +1598,18 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
15841598
break;
15851599
}
15861600

1601+
case DeclAttrKind::Safe: {
1602+
auto *attr = cast<SafeAttr>(this);
1603+
Printer.printAttrName("@safe");
1604+
Printer << "(unchecked";
1605+
if (!attr->message.empty()) {
1606+
Printer << ", message: ";
1607+
Printer.printEscapedStringLiteral(attr->message);
1608+
}
1609+
Printer << ")";
1610+
break;
1611+
}
1612+
15871613
#define SIMPLE_DECL_ATTR(X, CLASS, ...) case DeclAttrKind::CLASS:
15881614
#include "swift/AST/DeclAttr.def"
15891615
llvm_unreachable("handled above");

lib/AST/Decl.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4335,7 +4335,14 @@ SourceLoc Decl::getAttributeInsertionLoc(bool forModifier) const {
43354335
return SourceLoc();
43364336

43374337
SourceLoc resultLoc = getAttrs().getStartLoc(forModifier);
4338-
return resultLoc.isValid() ? resultLoc : introDecl->getStartLoc();
4338+
if (resultLoc.isInvalid())
4339+
return introDecl->getStartLoc();
4340+
4341+
SourceLoc startLoc = introDecl->getStartLoc();
4342+
if (!forModifier && getASTContext().SourceMgr.isBefore(startLoc, resultLoc))
4343+
return startLoc;
4344+
4345+
return resultLoc;
43394346
}
43404347

43414348
/// Returns true if \p VD needs to be treated as publicly-accessible

lib/Basic/Edit.cpp

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "llvm/Support/raw_ostream.h"
1414
#include "swift/Basic/Edit.h"
1515
#include "swift/Basic/SourceManager.h"
16+
#include <algorithm>
1617

1718
using namespace swift;
1819

@@ -34,8 +35,42 @@ void SourceEdits::addEdit(SourceManager &SM, CharSourceRange Range,
3435

3536
void swift::
3637
writeEditsInJson(const SourceEdits &AllEdits, llvm::raw_ostream &OS) {
37-
OS << "[\n";
38-
for (auto &Edit : AllEdits.getEdits()) {
38+
// Sort the edits so they occur from the last to the first within a given
39+
// source file. That's the order in which applying non-overlapping edits
40+
// will succeed.
41+
std::vector<SourceEdits::Edit> allEdits(AllEdits.getEdits().begin(),
42+
AllEdits.getEdits().end());
43+
std::sort(allEdits.begin(), allEdits.end(),
44+
[&](const SourceEdits::Edit &lhs, const SourceEdits::Edit &rhs) {
45+
// Sort first based on the path. This keeps the edits for a given
46+
// file together.
47+
if (lhs.Path < rhs.Path)
48+
return true;
49+
else if (lhs.Path > rhs.Path)
50+
return false;
51+
52+
// Then sort based on offset, with larger offsets coming earlier.
53+
return lhs.Offset > rhs.Offset;
54+
});
55+
56+
// Remove duplicate edits.
57+
allEdits.erase(
58+
std::unique(allEdits.begin(), allEdits.end(),
59+
[&](const SourceEdits::Edit &lhs, const SourceEdits::Edit &rhs) {
60+
return lhs.Path == rhs.Path && lhs.Text == rhs.Text &&
61+
lhs.Offset == rhs.Offset && lhs.Length == rhs.Length;
62+
}),
63+
allEdits.end());
64+
65+
OS << "[";
66+
bool first = true;
67+
for (auto &Edit : allEdits) {
68+
if (first) {
69+
first = false;
70+
} else {
71+
OS << ",";
72+
}
73+
OS << "\n";
3974
OS << " {\n";
4075
OS << " \"file\": \"";
4176
OS.write_escaped(Edit.Path) << "\",\n";
@@ -44,9 +79,9 @@ writeEditsInJson(const SourceEdits &AllEdits, llvm::raw_ostream &OS) {
4479
OS << " \"remove\": " << Edit.Length << ",\n";
4580
if (!Edit.Text.empty()) {
4681
OS << " \"text\": \"";
47-
OS.write_escaped(Edit.Text) << "\",\n";
82+
OS.write_escaped(Edit.Text) << "\"\n";
4883
}
49-
OS << " },\n";
84+
OS << " }";
5085
}
51-
OS << "]\n";
86+
OS << "\n]\n";
5287
}

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -942,11 +942,28 @@ class AvailabilityScopeBuilder : private ASTWalker {
942942
return true;
943943
}
944944

945+
/// Determine whether the given declaration has the @safe attribute.
946+
static bool declHasSafeAttr(Decl *decl) {
947+
if (decl->getAttrs().hasAttribute<SafeAttr>())
948+
return true;
949+
950+
if (auto accessor = dyn_cast<AccessorDecl>(decl))
951+
return declHasSafeAttr(accessor->getStorage());
952+
953+
// Attributes for pattern binding declarations are on the first variable.
954+
if (auto pbd = dyn_cast<PatternBindingDecl>(decl)) {
955+
if (auto var = pbd->getAnchoringVarDecl(0))
956+
return declHasSafeAttr(var);
957+
}
958+
959+
return false;
960+
}
961+
945962
void buildContextsForBodyOfDecl(Decl *D) {
946963
// Are we already constrained by the deployment target and the declaration
947964
// doesn't explicitly allow unsafe constructs in its definition, adding
948965
// new contexts won't change availability.
949-
bool allowsUnsafe = D->getAttrs().hasAttribute<SafeAttr>();
966+
bool allowsUnsafe = declHasSafeAttr(D);
950967
if (isCurrentScopeContainedByDeploymentTarget() && !allowsUnsafe)
951968
return;
952969

@@ -1836,8 +1853,8 @@ static bool isTypeLevelDeclForAvailabilityFixit(const Decl *D) {
18361853

18371854
bool IsModuleScopeContext = D->getDeclContext()->isModuleScopeContext();
18381855

1839-
// We consider global functions to be "type level"
1840-
if (isa<FuncDecl>(D)) {
1856+
// We consider global functions, type aliases, and macros to be "type level"
1857+
if (isa<FuncDecl>(D) || isa<MacroDecl>(D) || isa<TypeAliasDecl>(D)) {
18411858
return IsModuleScopeContext;
18421859
}
18431860

test/Macros/macro_availability_macosx.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ struct X { }
1010

1111
@freestanding(expression) macro m1() -> X = #externalMacro(module: "A", type: "B") // expected-error{{'X' is only available in macOS 12.0 or newer}}
1212
// expected-warning@-1{{external macro implementation type 'A.B' could not be found for macro 'm1()'}}
13+
// expected-note@-2{{add @available attribute to enclosing macro}}
1314

1415
@available(macOS 12.0, *)
1516
@freestanding(expression) macro m2() -> X = #externalMacro(module: "A", type: "B")

test/Unsafe/safe.swift

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
func unsafeFunction() { }
88

99
@unsafe
10-
struct UnsafeType { } // expected-note{{unsafe struct 'UnsafeType' declared here}}
10+
struct UnsafeType { } // expected-note 3{{unsafe struct 'UnsafeType' declared here}}
1111

1212
@safe(unchecked)
1313
func f() {
@@ -25,6 +25,37 @@ func h(_: UnsafeType) { // expected-warning{{reference to unsafe struct 'UnsafeT
2525
unsafeFunction()
2626
}
2727

28+
// expected-note@+1 {{make global function 'rethrowing' @unsafe to indicate that its use is not memory-safe}}{{1-1=@unsafe }}
29+
func rethrowing(body: (UnsafeType) throws -> Void) rethrows { } // expected-warning{{reference to unsafe struct 'UnsafeType' [Unsafe]}}
30+
31+
class HasStatics {
32+
// expected-note@+1{{make static method 'f' @unsafe to indicate that its use is not memory-safe}}{{3-3=@unsafe }}
33+
static internal func f(_: UnsafeType) { } // expected-warning{{reference to unsafe struct 'UnsafeType' [Unsafe]}}
34+
35+
36+
}
37+
38+
@unsafe
39+
func unsafeInt() -> Int { 5 }
40+
41+
struct HasProperties {
42+
@safe(unchecked) var computed: Int {
43+
unsafeInt()
44+
}
45+
46+
@unsafe var computedUnsafe: Int {
47+
unsafeInt()
48+
}
49+
50+
@safe(unchecked) static var blah: Int = {
51+
unsafeInt()
52+
}()
53+
54+
@unsafe static var blahUnsafe: Int = {
55+
unsafeInt()
56+
}()
57+
}
58+
2859
// Parsing issues
2960
@safe // expected-error{{expected '(' in 'safe' attribute}}
3061
func bad1() { }

test/Unsafe/unsafe.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ struct SuperHolder {
6161
// -----------------------------------------------------------------------
6262
// Inheritance of @unsafe
6363
// -----------------------------------------------------------------------
64-
@unsafe class UnsafeSuper { // expected-note 3{{'UnsafeSuper' declared here}}
64+
@unsafe class UnsafeSuper { // expected-note 5{{'UnsafeSuper' declared here}}
6565
func f() { } // expected-note{{unsafe instance method 'f' declared here}}
6666
};
6767

@@ -88,3 +88,17 @@ func testMe(
8888

8989
_ = getPointers() // expected-warning{{call to global function 'getPointers' involves unsafe type 'PointerType'}}
9090
}
91+
92+
// -----------------------------------------------------------------------
93+
// Various declaration kinds
94+
// -----------------------------------------------------------------------
95+
// expected-note@+1{{make type alias 'SuperUnsafe' @unsafe to indicate that its use is not memory-safe}}{{1-1=@unsafe }}
96+
typealias SuperUnsafe = UnsafeSuper // expected-warning{{reference to unsafe class 'UnsafeSuper' [Unsafe]}}
97+
@unsafe typealias SuperUnsafe2 = UnsafeSuper
98+
99+
enum HasUnsafeThings {
100+
// expected-note@+1{{make enum case 'one' @unsafe to indicate that its use is not memory-safe}}
101+
case one(UnsafeSuper) // expected-warning{{reference to unsafe class 'UnsafeSuper' [Unsafe]}}
102+
103+
@unsafe case two(UnsafeSuper)
104+
}

0 commit comments

Comments
 (0)