Skip to content

Commit 7d6e377

Browse files
authored
Merge pull request #78332 from DougGregor/strict-safety-improvements
Strict safety improvements
2 parents 5c24df2 + 0d8b67e commit 7d6e377

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+878
-305
lines changed

include/swift/AST/ASTContext.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/Evaluator.h"
2323
#include "swift/AST/Identifier.h"
2424
#include "swift/AST/Import.h"
25+
#include "swift/AST/ProtocolConformanceOptions.h"
2526
#include "swift/AST/SILOptions.h"
2627
#include "swift/AST/SearchPathOptions.h"
2728
#include "swift/AST/Type.h"
@@ -1299,8 +1300,7 @@ class ASTContext final {
12991300
SourceLoc loc,
13001301
DeclContext *dc,
13011302
ProtocolConformanceState state,
1302-
bool isUnchecked,
1303-
bool isPreconcurrency,
1303+
ProtocolConformanceOptions options,
13041304
SourceLoc preconcurrencyLoc = SourceLoc());
13051305

13061306
/// Produce a self-conformance for the given protocol.

include/swift/AST/Decl.h

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "swift/AST/Initializer.h"
3535
#include "swift/AST/LayoutConstraint.h"
3636
#include "swift/AST/LifetimeAnnotation.h"
37+
#include "swift/AST/ProtocolConformanceOptions.h"
3738
#include "swift/AST/ReferenceCounting.h"
3839
#include "swift/AST/RequirementSignature.h"
3940
#include "swift/AST/StorageImpl.h"
@@ -725,7 +726,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
725726
HasAnyUnavailableDuringLoweringValues : 1
726727
);
727728

728-
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+8,
729+
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+8,
729730
/// If the module is compiled as static library.
730731
StaticLibrary : 1,
731732

@@ -791,7 +792,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
791792
AllowNonResilientAccess : 1,
792793

793794
/// Whether this module has been built with -package-cmo.
794-
SerializePackageEnabled : 1
795+
SerializePackageEnabled : 1,
796+
797+
/// Whether this module has enabled strict memory safety checking.
798+
StrictMemorySafety : 1
795799
);
796800

797801
SWIFT_INLINE_BITFIELD(PrecedenceGroupDecl, Decl, 1+2,
@@ -1782,14 +1786,8 @@ class ImportDecl final : public Decl,
17821786
/// An entry in the "inherited" list of a type or extension.
17831787
struct InheritedEntry : public TypeLoc {
17841788
private:
1785-
/// Whether there was an @unchecked attribute.
1786-
bool IsUnchecked : 1;
1787-
1788-
/// Whether there was an @retroactive attribute.
1789-
bool IsRetroactive : 1;
1790-
1791-
/// Whether there was an @preconcurrency attribute.
1792-
bool IsPreconcurrency : 1;
1789+
/// Options on a protocol conformance that are expressed as attributes.
1790+
unsigned RawOptions: 8;
17931791

17941792
/// Whether there was a ~ indicating suppression.
17951793
///
@@ -1799,17 +1797,38 @@ struct InheritedEntry : public TypeLoc {
17991797
public:
18001798
InheritedEntry(const TypeLoc &typeLoc);
18011799

1802-
InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive,
1803-
bool isPreconcurrency, bool isSuppressed = false)
1804-
: TypeLoc(typeLoc), IsUnchecked(isUnchecked),
1805-
IsRetroactive(isRetroactive), IsPreconcurrency(isPreconcurrency),
1800+
InheritedEntry(const TypeLoc &typeLoc, ProtocolConformanceOptions options,
1801+
bool isSuppressed = false)
1802+
: TypeLoc(typeLoc), RawOptions(options.toRaw()),
18061803
IsSuppressed(isSuppressed) {}
18071804

1808-
bool isUnchecked() const { return IsUnchecked; }
1809-
bool isRetroactive() const { return IsRetroactive; }
1810-
bool isPreconcurrency() const { return IsPreconcurrency; }
1805+
ProtocolConformanceOptions getOptions() const {
1806+
return ProtocolConformanceOptions(RawOptions);
1807+
}
1808+
1809+
bool isUnchecked() const {
1810+
return getOptions().contains(ProtocolConformanceFlags::Unchecked);
1811+
}
1812+
bool isRetroactive() const {
1813+
return getOptions().contains(ProtocolConformanceFlags::Retroactive);
1814+
}
1815+
bool isPreconcurrency() const {
1816+
return getOptions().contains(ProtocolConformanceFlags::Preconcurrency);
1817+
}
1818+
bool isUnsafe() const {
1819+
return getOptions().contains(ProtocolConformanceFlags::Unsafe);
1820+
}
1821+
1822+
bool isSafe() const {
1823+
return getOptions().contains(ProtocolConformanceFlags::Safe);
1824+
}
1825+
18111826
bool isSuppressed() const { return IsSuppressed; }
18121827

1828+
void setOption(ProtocolConformanceFlags flag) {
1829+
RawOptions = (getOptions() | flag).toRaw();
1830+
}
1831+
18131832
void setSuppressed() {
18141833
assert(!IsSuppressed && "setting suppressed again!?");
18151834
IsSuppressed = true;

include/swift/AST/DeclAttr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ SIMPLE_DECL_ATTR(_addressableSelf, AddressableSelf,
529529

530530
DECL_ATTR(safe, Safe,
531531
OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType |
532-
OnExtension | OnTypeAlias | UserInaccessible |
532+
OnExtension | OnTypeAlias | OnImport | UserInaccessible |
533533
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
534534
163)
535535

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,5 +589,9 @@ ERROR(experimental_not_supported_in_production,none,
589589
"experimental feature '%0' cannot be enabled in production compiler",
590590
(StringRef))
591591

592+
GROUPED_WARNING(Ounchecked_with_strict_safety,Unsafe,none,
593+
"'-Ounchecked' is not memory-safe and should not be combined with "
594+
"strict memory safety checking", ())
595+
592596
#define UNDEFINE_DIAGNOSTIC_MACROS
593597
#include "DefineDiagnosticMacros.h"

include/swift/AST/DiagnosticsSema.def

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5792,22 +5792,19 @@ ERROR(non_sendable_type,none,
57925792
ERROR(sendable_raw_storage,none,
57935793
"struct %0 with @_rawLayout does not conform to the 'Sendable' protocol", (DeclName))
57945794

5795+
ERROR(typeattr_not_inheritance_clause,none,
5796+
"'%0' attribute only applies in inheritance clauses", (StringRef))
5797+
ERROR(typeattr_not_existential,none,
5798+
"'%0' attribute cannot apply to non-protocol type %1", (StringRef, Type))
5799+
57955800
WARNING(unchecked_conformance_not_special,none,
57965801
"@unchecked conformance to %0 has no meaning", (Type))
57975802
WARNING(restate_unchecked_sendable,none,
57985803
"class %0 must restate inherited '@unchecked Sendable' conformance",
57995804
(DeclName))
5800-
ERROR(unchecked_not_inheritance_clause,none,
5801-
"'unchecked' attribute only applies in inheritance clauses", ())
5802-
ERROR(unchecked_not_existential,none,
5803-
"'unchecked' attribute cannot apply to non-protocol type %0", (Type))
58045805

58055806
WARNING(preconcurrency_conformance_not_used,none,
58065807
"@preconcurrency attribute on conformance to %0 has no effect", (Type))
5807-
ERROR(preconcurrency_not_inheritance_clause,none,
5808-
"'preconcurrency' attribute only applies in inheritance clauses", ())
5809-
ERROR(preconcurrency_not_existential,none,
5810-
"'preconcurrency' attribute cannot apply to non-protocol type %0", (Type))
58115808

58125809
ERROR(redundant_any_in_existential,none,
58135810
"redundant 'any' in type %0",
@@ -8084,22 +8081,33 @@ NOTE(note_reference_to_unsafe_decl,none,
80848081
NOTE(note_reference_to_unsafe_typed_decl,none,
80858082
"%select{reference|call}0 to %kind1 involves unsafe type %2",
80868083
(bool, const ValueDecl *, Type))
8084+
NOTE(note_reference_to_unsafe_through_typealias,none,
8085+
"reference to %kind0 whose underlying type involves unsafe type %1",
8086+
(const ValueDecl *, Type))
80878087
NOTE(note_reference_to_nonisolated_unsafe,none,
80888088
"reference to nonisolated(unsafe) %kind0 is unsafe in concurrently-executing code",
80898089
(const ValueDecl *))
80908090
NOTE(note_reference_unowned_unsafe,none,
80918091
"reference to unowned(unsafe) %kind0 is unsafe", (const ValueDecl *))
8092-
8093-
GROUPED_WARNING(override_safe_withunsafe,Unsafe,none,
8094-
"override of safe %0 with unsafe %0", (DescriptiveDeclKind))
8095-
GROUPED_WARNING(witness_unsafe,Unsafe,none,
8092+
NOTE(note_use_of_unsafe_conformance_is_unsafe,none,
8093+
"@unsafe conformance of %0 to %kind1 involves unsafe code",
8094+
(Type, const ValueDecl *))
8095+
GROUPED_WARNING(conformance_involves_unsafe,Unsafe,none,
8096+
"conformance of %0 to %kind1 involves unsafe code; use '@unsafe' to "
8097+
"indicate that the conformance is not memory-safe",
8098+
(Type, const ValueDecl *))
8099+
NOTE(note_witness_unsafe,none,
80968100
"unsafe %0 %1 cannot satisfy safe requirement",
80978101
(DescriptiveDeclKind, DeclName))
8098-
GROUPED_WARNING(type_witness_unsafe,Unsafe,none,
8102+
NOTE(note_type_witness_unsafe,none,
80998103
"unsafe type %0 cannot satisfy safe associated type %1",
81008104
(Type, DeclName))
8101-
GROUPED_WARNING(unchecked_conformance_is_unsafe,Unsafe,none,
8102-
"@unchecked conformance involves unsafe code", ())
8105+
8106+
GROUPED_WARNING(override_safe_withunsafe,Unsafe,none,
8107+
"override of safe %0 with unsafe %0", (DescriptiveDeclKind))
8108+
GROUPED_WARNING(use_of_unsafe_conformance_is_unsafe,Unsafe,none,
8109+
"@unsafe conformance of %0 to %kind1 involves unsafe code",
8110+
(Type, const ValueDecl *))
81038111
GROUPED_WARNING(reference_unowned_unsafe,Unsafe,none,
81048112
"reference to unowned(unsafe) %kind0 is unsafe", (const ValueDecl *))
81058113
GROUPED_WARNING(reference_to_nonisolated_unsafe,Unsafe,none,
@@ -8111,8 +8119,13 @@ GROUPED_WARNING(reference_to_unsafe_decl,Unsafe,none,
81118119
GROUPED_WARNING(reference_to_unsafe_typed_decl,Unsafe,none,
81128120
"%select{reference|call}0 to %kindbase1 involves unsafe type %2",
81138121
(bool, const ValueDecl *, Type))
8114-
NOTE(unsafe_decl_here,none,
8115-
"unsafe %kindbase0 declared here", (const ValueDecl *))
8122+
GROUPED_WARNING(reference_to_unsafe_through_typealias,Unsafe,none,
8123+
"reference to %kind0 whose underlying type involves unsafe type %1",
8124+
(const ValueDecl *, Type))
8125+
GROUPED_WARNING(preconcurrency_import_unsafe,Unsafe,none,
8126+
"@preconcurrency import is not memory-safe because it can silently "
8127+
"introduce data races; use '@safe(unchecked)' to assert that the "
8128+
"code is memory-safe", ())
81168129
NOTE(encapsulate_unsafe_in_enclosing_context,none,
81178130
"make %kindbase0 @safe(unchecked) to allow it to use unsafe constructs in its definition",
81188131
(const Decl *))

include/swift/AST/Module.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,15 @@ class ModuleDecl
830830
Bits.ModuleDecl.IsConcurrencyChecked = value;
831831
}
832832

833+
/// Whether this module has enable strict memory safety checking.
834+
bool strictMemorySafety() const {
835+
return Bits.ModuleDecl.StrictMemorySafety;
836+
}
837+
838+
void setStrictMemorySafety(bool value = true) {
839+
Bits.ModuleDecl.StrictMemorySafety = value;
840+
}
841+
833842
bool isObjCNameLookupCachePopulated() const {
834843
return Bits.ModuleDecl.ObjCNameLookupCachePopulated;
835844
}

include/swift/AST/NameLookup.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,16 +609,24 @@ struct InheritedNominalEntry : Located<NominalTypeDecl *> {
609609
/// The location of the "preconcurrency" attribute if present.
610610
SourceLoc preconcurrencyLoc;
611611

612+
/// The location of the "unsafe" attribute if present.
613+
SourceLoc unsafeLoc;
614+
615+
/// The range of the "safe(unchecked)" attribute if present.
616+
SourceRange safeRange;
617+
612618
/// Whether this inherited entry was suppressed via "~".
613619
bool isSuppressed;
614620

615621
InheritedNominalEntry() { }
616622

617623
InheritedNominalEntry(NominalTypeDecl *item, SourceLoc loc,
618624
SourceLoc uncheckedLoc, SourceLoc preconcurrencyLoc,
625+
SourceLoc unsafeLoc, SourceRange safeRange,
619626
bool isSuppressed)
620627
: Located(item, loc), uncheckedLoc(uncheckedLoc),
621-
preconcurrencyLoc(preconcurrencyLoc), isSuppressed(isSuppressed) {}
628+
preconcurrencyLoc(preconcurrencyLoc), unsafeLoc(unsafeLoc),
629+
safeRange(safeRange), isSuppressed(isSuppressed) {}
622630
};
623631

624632
/// Retrieve the set of nominal type declarations that are directly

include/swift/AST/ProtocolConformance.h

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "swift/AST/ConcreteDeclRef.h"
2020
#include "swift/AST/Decl.h"
21+
#include "swift/AST/ProtocolConformanceOptions.h"
2122
#include "swift/AST/Type.h"
2223
#include "swift/AST/Types.h"
2324
#include "swift/AST/TypeAlignments.h"
@@ -146,20 +147,21 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
146147
SWIFT_INLINE_BITFIELD_EMPTY(RootProtocolConformance, ProtocolConformance);
147148

148149
SWIFT_INLINE_BITFIELD_FULL(NormalProtocolConformance, RootProtocolConformance,
149-
1+1+1+1+bitmax(NumProtocolConformanceStateBits,8)+
150+
1+1+
151+
bitmax(NumProtocolConformanceOptions,8)+
152+
bitmax(NumProtocolConformanceStateBits,8)+
150153
bitmax(NumConformanceEntryKindBits,8),
151154
/// Indicates whether the conformance is invalid.
152155
IsInvalid : 1,
153-
/// The conformance was labeled with @unchecked.
154-
IsUnchecked : 1,
155-
/// The conformance was labeled with @preconcurrency.
156-
IsPreconcurrency : 1,
157156
/// We have allocated the AssociatedConformances array (but not necessarily
158157
/// populated any of its elements).
159158
HasComputedAssociatedConformances : 1,
160159

161160
: NumPadBits,
162161

162+
/// Options.
163+
Options : bitmax(NumProtocolConformanceOptions, 8),
164+
163165
/// The current state of the conformance.
164166
State : bitmax(NumProtocolConformanceStateBits, 8),
165167
/// The reason that this conformance exists.
@@ -567,8 +569,8 @@ class NormalProtocolConformance : public RootProtocolConformance,
567569
public:
568570
NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol,
569571
SourceLoc loc, DeclContext *dc,
570-
ProtocolConformanceState state, bool isUnchecked,
571-
bool isPreconcurrency,
572+
ProtocolConformanceState state,
573+
ProtocolConformanceOptions options,
572574
SourceLoc preconcurrencyLoc)
573575
: RootProtocolConformance(ProtocolConformanceKind::Normal,
574576
conformingType),
@@ -577,12 +579,12 @@ class NormalProtocolConformance : public RootProtocolConformance,
577579
Context(dc) {
578580
assert(!conformingType->hasArchetype() &&
579581
"ProtocolConformances should store interface types");
580-
assert((preconcurrencyLoc.isInvalid() || isPreconcurrency) &&
582+
assert((preconcurrencyLoc.isInvalid() ||
583+
options.contains(ProtocolConformanceFlags::Preconcurrency)) &&
581584
"Cannot have a @preconcurrency location without isPreconcurrency");
582585
setState(state);
583586
Bits.NormalProtocolConformance.IsInvalid = false;
584-
Bits.NormalProtocolConformance.IsUnchecked = isUnchecked;
585-
Bits.NormalProtocolConformance.IsPreconcurrency = isPreconcurrency;
587+
Bits.NormalProtocolConformance.Options = options.toRaw();
586588
Bits.NormalProtocolConformance.HasComputedAssociatedConformances = false;
587589
Bits.NormalProtocolConformance.SourceKind =
588590
unsigned(ConformanceEntryKind::Explicit);
@@ -626,27 +628,42 @@ class NormalProtocolConformance : public RootProtocolConformance,
626628
/// Mark this conformance as invalid.
627629
void setInvalid() { Bits.NormalProtocolConformance.IsInvalid = true; }
628630

631+
ProtocolConformanceOptions getOptions() const {
632+
return ProtocolConformanceOptions(Bits.NormalProtocolConformance.Options);
633+
}
634+
629635
/// Whether this is an "unchecked" conformance.
630636
bool isUnchecked() const {
631-
return Bits.NormalProtocolConformance.IsUnchecked;
637+
return getOptions().contains(ProtocolConformanceFlags::Unchecked);
632638
}
633639

634640
/// Mark the conformance as unchecked (equivalent to the @unchecked
635641
/// conformance attribute).
636642
void setUnchecked() {
637643
// OK to mutate because the flags are not part of the folding set node ID.
638-
Bits.NormalProtocolConformance.IsUnchecked = true;
644+
Bits.NormalProtocolConformance.Options =
645+
(getOptions() | ProtocolConformanceFlags::Unchecked).toRaw();
639646
}
640647

641648
/// Whether this is an preconcurrency conformance.
642649
bool isPreconcurrency() const {
643-
return Bits.NormalProtocolConformance.IsPreconcurrency;
650+
return getOptions().contains(ProtocolConformanceFlags::Preconcurrency);
644651
}
645652

646653
/// Retrieve the location of `@preconcurrency`, if there is one and it is
647654
/// known.
648655
SourceLoc getPreconcurrencyLoc() const { return PreconcurrencyLoc; }
649656

657+
/// Whether this is an "unsafe" conformance.
658+
bool isUnsafe() const {
659+
return getOptions().contains(ProtocolConformanceFlags::Unsafe);
660+
}
661+
662+
/// Whether this is an "safe(unchecked)" conformance.
663+
bool isSafe() const {
664+
return getOptions().contains(ProtocolConformanceFlags::Safe);
665+
}
666+
650667
/// Determine whether we've lazily computed the associated conformance array
651668
/// already.
652669
bool hasComputedAssociatedConformances() const {
@@ -684,8 +701,8 @@ class NormalProtocolConformance : public RootProtocolConformance,
684701
if (auto implying = implyingConformance) {
685702
ImplyingConformance = implying;
686703
PreconcurrencyLoc = implying->getPreconcurrencyLoc();
687-
Bits.NormalProtocolConformance.IsPreconcurrency =
688-
implying->isPreconcurrency();
704+
Bits.NormalProtocolConformance.Options =
705+
implyingConformance->getOptions().toRaw();
689706
}
690707
}
691708

0 commit comments

Comments
 (0)