Skip to content

Commit ca0e984

Browse files
authored
Merge pull request #81703 from DougGregor/more-migratable-features
Make `InferIsolatedConformances` and `StrictMemorySafety` migratable features
2 parents 3305e89 + 32267f4 commit ca0e984

19 files changed

+219
-15
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8617,6 +8617,9 @@ GROUPED_ERROR(isolated_conformance_with_sendable_simple,IsolatedConformances,
86178617
GROUPED_ERROR(isolated_conformance_wrong_domain,IsolatedConformances,none,
86188618
"%0 conformance of %1 to %2 cannot be used in %3 context",
86198619
(ActorIsolation, Type, DeclName, ActorIsolation))
8620+
GROUPED_WARNING(isolated_conformance_will_become_nonisolated,IsolatedConformances,none,
8621+
"conformance of %0 to %1 should be marked 'nonisolated' to retain its behavior with upcoming feature 'InferIsolatedConformances'",
8622+
(const ValueDecl *, const ValueDecl *))
86208623
86218624
//===----------------------------------------------------------------------===//
86228625
// MARK: @_inheritActorContext

include/swift/Basic/Features.def

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@
155155
#endif
156156
#endif
157157

158+
#ifndef MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE
159+
#define MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Name) \
160+
OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, #Name)
161+
#endif
162+
158163
#ifndef UPCOMING_FEATURE
159164
#define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
160165
LANGUAGE_FEATURE(FeatureName, SENumber, #FeatureName)
@@ -283,14 +288,14 @@ UPCOMING_FEATURE(GlobalActorIsolatedTypesUsability, 0434, 6)
283288
MIGRATABLE_UPCOMING_FEATURE(ExistentialAny, 335, 7)
284289
UPCOMING_FEATURE(InternalImportsByDefault, 409, 7)
285290
UPCOMING_FEATURE(MemberImportVisibility, 444, 7)
286-
UPCOMING_FEATURE(InferIsolatedConformances, 470, 7)
291+
MIGRATABLE_UPCOMING_FEATURE(InferIsolatedConformances, 470, 7)
287292
MIGRATABLE_UPCOMING_FEATURE(NonisolatedNonsendingByDefault, 461, 7)
288293

289294
// Optional language features / modes
290295

291296
/// Diagnose uses of language constructs and APIs that can violate memory
292297
/// safety.
293-
OPTIONAL_LANGUAGE_FEATURE(StrictMemorySafety, 458, "Strict memory safety")
298+
MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE(StrictMemorySafety, 458, "Strict memory safety")
294299

295300
// Experimental features
296301

@@ -521,6 +526,7 @@ EXPERIMENTAL_FEATURE(ModuleSelector, false)
521526
#undef UPCOMING_FEATURE
522527
#undef MIGRATABLE_UPCOMING_FEATURE
523528
#undef MIGRATABLE_EXPERIMENTAL_FEATURE
529+
#undef MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE
524530
#undef BASELINE_LANGUAGE_FEATURE
525531
#undef OPTIONAL_LANGUAGE_FEATURE
526532
#undef CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,9 @@ namespace swift {
872872
FeatureState getFeatureState(Feature feature) const;
873873

874874
/// Returns whether the given feature is enabled.
875-
bool hasFeature(Feature feature) const;
875+
///
876+
/// If allowMigration is set, also returns true when the feature has been enabled for migration.
877+
bool hasFeature(Feature feature, bool allowMigration = false) const;
876878

877879
/// Returns whether a feature with the given name is enabled. Returns
878880
/// `false` if a feature by this name is not known.

include/swift/Option/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,10 @@ def strict_memory_safety : Flag<["-"], "strict-memory-safety">,
10181018
Flags<[FrontendOption, ModuleInterfaceOptionIgnorable,
10191019
SwiftAPIDigesterOption, SwiftSynthesizeInterfaceOption]>,
10201020
HelpText<"Enable strict memory safety checking">;
1021+
def strict_memory_safety_migrate : Flag<["-"], "strict-memory-safety:migrate">,
1022+
Flags<[FrontendOption, ModuleInterfaceOptionIgnorable,
1023+
SwiftAPIDigesterOption, SwiftSynthesizeInterfaceOption]>,
1024+
HelpText<"Enable migration to strict memory safety checking">;
10211025

10221026
def Rpass_EQ : Joined<["-"], "Rpass=">,
10231027
Flags<[FrontendOption]>,

lib/Basic/Feature.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ bool Feature::isMigratable() const {
7373
switch (kind) {
7474
#define MIGRATABLE_UPCOMING_FEATURE(FeatureName, SENumber, Version)
7575
#define MIGRATABLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd)
76+
#define MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Name)
7677
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
7778
case Feature::FeatureName:
7879
#include "swift/Basic/Features.def"
@@ -82,6 +83,8 @@ bool Feature::isMigratable() const {
8283
case Feature::FeatureName:
8384
#define MIGRATABLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
8485
case Feature::FeatureName:
86+
#define MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Name) \
87+
case Feature::FeatureName:
8588
#include "swift/Basic/Features.def"
8689
return true;
8790
}

lib/Basic/LangOptions.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,13 +335,17 @@ LangOptions::FeatureState LangOptions::getFeatureState(Feature feature) const {
335335
return state;
336336
}
337337

338-
bool LangOptions::hasFeature(Feature feature) const {
339-
if (featureStates.getState(feature).isEnabled())
338+
bool LangOptions::hasFeature(Feature feature, bool allowMigration) const {
339+
auto state = featureStates.getState(feature);
340+
if (state.isEnabled())
340341
return true;
341342

342343
if (auto version = feature.getLanguageVersion())
343344
return isSwiftVersionAtLeast(*version);
344345

346+
if (allowMigration && state.isEnabledForMigration())
347+
return true;
348+
345349
return false;
346350
}
347351

lib/Basic/SupportedFeatures.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <array>
1414
#include <vector>
1515

16+
#include "swift/AST/DiagnosticGroups.h"
1617
#include "swift/Basic/Feature.h"
1718
#include "swift/Frontend/Frontend.h"
1819

@@ -22,11 +23,58 @@ using namespace swift;
2223

2324
namespace swift {
2425
namespace features {
26+
27+
/// The subset of diagnostic groups (called categories by the diagnostic machinery) whose diagnostics should be
28+
/// considered to be part of the migration for this feature.
29+
///
30+
/// When making a feature migratable, ensure that all of the warnings that are used to drive the migration are
31+
/// part of a diagnostic group, and put that diagnostic group into the list for that feature here.
32+
static std::vector<DiagGroupID> migratableCategories(Feature feature) {
33+
switch (feature) {
34+
case Feature::InnerKind::ExistentialAny:
35+
return { DiagGroupID::ExistentialAny };
36+
case Feature::InnerKind::InferIsolatedConformances:
37+
return { DiagGroupID::IsolatedConformances };
38+
case Feature::InnerKind::NonisolatedNonsendingByDefault:
39+
return { DiagGroupID::NonisolatedNonsendingByDefault };
40+
case Feature::InnerKind::StrictMemorySafety:
41+
return { DiagGroupID::StrictMemorySafety };
42+
43+
// Provide unreachable cases for all of the non-migratable features.
44+
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) case Feature::FeatureName:
45+
#define MIGRATABLE_UPCOMING_FEATURE(FeatureName, SENumber, Version)
46+
#define MIGRATABLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd)
47+
#define MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Name)
48+
#include "swift/Basic/Features.def"
49+
llvm_unreachable("Not a migratable feature");
50+
}
51+
}
52+
53+
/// For optional language features, return the flag name used by the compiler to enable the feature. For all others,
54+
/// returns an empty optional.
55+
static std::optional<std::string_view> optionalFlagName(Feature feature) {
56+
switch (feature) {
57+
case Feature::StrictMemorySafety:
58+
return "-strict-memory-safety";
59+
60+
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) case Feature::FeatureName:
61+
#define OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Description)
62+
#include "swift/Basic/Features.def"
63+
return std::nullopt;
64+
}
65+
}
66+
2567
/// Print information about what features upcoming/experimental are
2668
/// supported by the compiler.
2769
/// The information includes whether a feature is adoptable and for
2870
/// upcoming features - what is the first mode it's introduced.
2971
void printSupportedFeatures(llvm::raw_ostream &out) {
72+
std::array optional{
73+
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description)
74+
#define OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Description) Feature::FeatureName,
75+
#include "swift/Basic/Features.def"
76+
};
77+
3078
std::array upcoming{
3179
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description)
3280
#define UPCOMING_FEATURE(FeatureName, SENumber, Version) Feature::FeatureName,
@@ -50,14 +98,32 @@ void printSupportedFeatures(llvm::raw_ostream &out) {
5098
out << "{ \"name\": \"" << feature.getName() << "\"";
5199
if (feature.isMigratable()) {
52100
out << ", \"migratable\": true";
101+
102+
auto categories = migratableCategories(feature);
103+
out << ", \"categories\": [";
104+
llvm::interleave(categories, [&out](DiagGroupID diagGroupID) {
105+
out << "\"" << getDiagGroupInfoByID(diagGroupID).name << "\"";
106+
}, [&out] {
107+
out << ", ";
108+
});
109+
out << "]";
53110
}
54111
if (auto version = feature.getLanguageVersion()) {
55112
out << ", \"enabled_in\": \"" << *version << "\"";
56113
}
114+
115+
if (auto flagName = optionalFlagName(feature)) {
116+
out << ", \"flag_name\": \"" << *flagName << "\"";
117+
}
118+
57119
out << " }";
58120
};
59121

60122
out << " \"features\": {\n";
123+
out << " \"optional\": [\n";
124+
llvm::interleave(optional, printFeature, [&out] { out << ",\n"; });
125+
out << "\n ],\n";
126+
61127
out << " \"upcoming\": [\n";
62128
llvm::interleave(upcoming, printFeature, [&out] { out << ",\n"; });
63129
out << "\n ],\n";

lib/Driver/ToolChains.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
284284
options::OPT_disable_experimental_feature,
285285
options::OPT_enable_upcoming_feature,
286286
options::OPT_disable_upcoming_feature});
287-
inputArgs.AddLastArg(arguments, options::OPT_strict_memory_safety);
287+
inputArgs.AddLastArg(arguments, options::OPT_strict_memory_safety,
288+
options::OPT_strict_memory_safety_migrate);
288289
inputArgs.AddLastArg(arguments, options::OPT_warn_implicit_overrides);
289290
inputArgs.AddLastArg(arguments, options::OPT_typo_correction_limit);
290291
inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,8 @@ static bool ParseEnabledFeatureArgs(LangOptions &Opts, ArgList &Args,
996996

997997
if (Args.hasArg(OPT_strict_memory_safety))
998998
Opts.enableFeature(Feature::StrictMemorySafety);
999+
else if (Args.hasArg(OPT_strict_memory_safety_migrate))
1000+
Opts.enableFeature(Feature::StrictMemorySafety, /*forMigration=*/true);
9991001

10001002
return HadError;
10011003
}

lib/Sema/DerivedConformance/DerivedConformanceDistributedActor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ deriveBodyDistributed_doInvokeOnReturn(AbstractFunctionDecl *afd, void *arg) {
171171
new (C) DeclRefExpr(ConcreteDeclRef(returnTypeParam),
172172
dloc, implicit))}));
173173

174-
if (C.LangOpts.hasFeature(Feature::StrictMemorySafety))
174+
if (C.LangOpts.hasFeature(Feature::StrictMemorySafety, /*allowMigration=*/true))
175175
resultLoadCall = new (C) UnsafeExpr(sloc, resultLoadCall, Type(), true);
176176

177177
auto resultPattern = NamedPattern::createImplicit(C, resultVar);

lib/Sema/DerivedConformance/DerivedConformanceRawRepresentable.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ deriveBodyRawRepresentable_raw(AbstractFunctionDecl *toRawDecl, void *) {
106106
auto *argList = ArgumentList::forImplicitCallTo(functionRef->getName(),
107107
{selfRef, typeExpr}, C);
108108
Expr *call = CallExpr::createImplicit(C, functionRef, argList);
109-
if (C.LangOpts.hasFeature(Feature::StrictMemorySafety))
109+
if (C.LangOpts.hasFeature(Feature::StrictMemorySafety, /*allowMigration=*/true))
110110
call = UnsafeExpr::createImplicit(C, SourceLoc(), call);
111111
auto *returnStmt = ReturnStmt::createImplicit(C, call);
112112
auto body = BraceStmt::create(C, SourceLoc(), ASTNode(returnStmt),

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2257,7 +2257,7 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
22572257
diagnoseOverrideForAvailability(override, base);
22582258
}
22592259

2260-
if (ctx.LangOpts.hasFeature(Feature::StrictMemorySafety)) {
2260+
if (ctx.LangOpts.hasFeature(Feature::StrictMemorySafety, /*allowMigration=*/true)) {
22612261
// If the override is unsafe but the base declaration is not, then the
22622262
// inheritance itself is unsafe.
22632263
auto subs = SubstitutionMap::getOverrideSubstitutions(base, override);

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2427,7 +2427,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
24272427

24282428
// If strict memory safety checking is enabled, check the storage
24292429
// of the nominal type.
2430-
if (Ctx.LangOpts.hasFeature(Feature::StrictMemorySafety) &&
2430+
if (Ctx.LangOpts.hasFeature(
2431+
Feature::StrictMemorySafety, /*allowMigration=*/true) &&
24312432
!isa<ProtocolDecl>(nominal)) {
24322433
checkUnsafeStorage(nominal);
24332434
}

lib/Sema/TypeCheckEffects.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4554,7 +4554,8 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
45544554
if (classification.hasUnsafe()) {
45554555
// If there is no such effect, complain.
45564556
if (S->getUnsafeLoc().isInvalid() &&
4557-
Ctx.LangOpts.hasFeature(Feature::StrictMemorySafety)) {
4557+
Ctx.LangOpts.hasFeature(Feature::StrictMemorySafety,
4558+
/*allowMigration=*/true)) {
45584559
auto insertionLoc = S->getPattern()->getStartLoc();
45594560
Ctx.Diags.diagnose(S->getForLoc(), diag::for_unsafe_without_unsafe)
45604561
.fixItInsert(insertionLoc, "unsafe ");
@@ -4801,7 +4802,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
48014802

48024803
void diagnoseUncoveredUnsafeSite(
48034804
const Expr *anchor, ArrayRef<UnsafeUse> unsafeUses) {
4804-
if (!Ctx.LangOpts.hasFeature(Feature::StrictMemorySafety))
4805+
if (!Ctx.LangOpts.hasFeature(Feature::StrictMemorySafety, /*allowMigration=*/true))
48054806
return;
48064807

48074808
const auto &[loc, insertText] = getFixItForUncoveredSite(anchor, "unsafe");

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2646,7 +2646,8 @@ checkIndividualConformance(NormalProtocolConformance *conformance) {
26462646
// If we're enforcing strict memory safety and this conformance hasn't
26472647
// opted out, look for safe/unsafe witness mismatches.
26482648
if (conformance->getExplicitSafety() == ExplicitSafety::Unspecified &&
2649-
Context.LangOpts.hasFeature(Feature::StrictMemorySafety)) {
2649+
Context.LangOpts.hasFeature(Feature::StrictMemorySafety,
2650+
/*allowMigration=*/true)) {
26502651
// Collect all of the unsafe uses for this conformance.
26512652
SmallVector<UnsafeUse, 2> unsafeUses;
26522653
for (auto requirement: Proto->getMembers()) {
@@ -6675,6 +6676,49 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
66756676
}
66766677
}
66776678

6679+
// If we are migrating to InferIsolatedConformances, and the
6680+
// nominal type is global-actor-isolated, look for conformances
6681+
// that are nonisolated but were not explicitly marked as such.
6682+
// These conformances will need to be marked 'nonisolated' to
6683+
// retain their current behavior.
6684+
if (Context.LangOpts
6685+
.getFeatureState(Feature::InferIsolatedConformances)
6686+
.isEnabledForMigration() &&
6687+
getActorIsolation(const_cast<NominalTypeDecl *>(nominal))
6688+
.isGlobalActor()) {
6689+
for (auto conformance : conformances) {
6690+
auto normal = dyn_cast<NormalProtocolConformance>(conformance);
6691+
if (!normal)
6692+
continue;
6693+
6694+
// Explicit nonisolated and @preconcurrency suppress this.
6695+
auto options = normal->getOptions();
6696+
if (options.contains(ProtocolConformanceFlags::Nonisolated) ||
6697+
options.contains(ProtocolConformanceFlags::Preconcurrency))
6698+
continue;
6699+
6700+
// Only consider conformances that were explicitly written in the source.
6701+
if (normal->getSourceKind() != ConformanceEntryKind::Explicit)
6702+
continue;
6703+
6704+
// Only consider conformances to non-marker, nonisolated protocols.
6705+
auto proto = normal->getProtocol();
6706+
if (proto->isMarkerProtocol() || getActorIsolation(proto).isActorIsolated())
6707+
continue;
6708+
6709+
// Only nonisolated conformances can be affected.
6710+
if (!conformance->getIsolation().isNonisolated())
6711+
continue;
6712+
6713+
auto nameLoc = normal->getProtocolNameLoc();
6714+
if (nameLoc.isValid()) {
6715+
Context.Diags.diagnose(
6716+
nameLoc, diag::isolated_conformance_will_become_nonisolated, nominal, proto)
6717+
.fixItInsert(nameLoc, "nonisolated ");
6718+
}
6719+
}
6720+
}
6721+
66786722
if (Context.TypeCheckerOpts.DebugGenericSignatures &&
66796723
!conformances.empty()) {
66806724
// Now that they're filled out, print out information about the conformances
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %target-swift-frontend -typecheck -verify -target %target-swift-5.1-abi-triple -swift-version 6 -enable-upcoming-feature InferIsolatedConformances:migrate %s
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: swift_feature_InferIsolatedConformances
5+
6+
protocol P { }
7+
protocol Q: P { }
8+
9+
struct S: P { }
10+
11+
struct S2: Q { }
12+
13+
@MainActor
14+
struct MA1: P { }
15+
// expected-warning@-1{{conformance of 'MA1' to 'P' should be marked 'nonisolated' to retain its behavior with upcoming feature 'InferIsolatedConformances'}}{{13-13=nonisolated }}
16+
17+
@MainActor
18+
struct MA2: Q { }
19+
// expected-warning@-1{{conformance of 'MA2' to 'Q' should be marked 'nonisolated' to retain its behavior with upcoming feature 'InferIsolatedConformances'}}{{13-13=nonisolated }}
20+
21+
@MainActor
22+
struct MA3 { }
23+
24+
extension MA3: P, Q { }
25+
// expected-warning@-1{{conformance of 'MA3' to 'P' should be marked 'nonisolated' to retain its behavior with upcoming feature 'InferIsolatedConformances'}}{{16-16=nonisolated }}
26+
// expected-warning@-2{{conformance of 'MA3' to 'Q' should be marked 'nonisolated' to retain its behavior with upcoming feature 'InferIsolatedConformances'}}{{19-19=nonisolated }}
27+
28+
@MainActor
29+
struct MA4: @MainActor P { }
30+
31+
@MainActor
32+
struct MA5: nonisolated P { }
33+
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
// RUN: %target-swift-frontend -print-supported-features | %FileCheck %s
22

33
// CHECK: "features": {
4+
// CHECK-NEXT: "optional": [
5+
// CHECK: { "name": "StrictMemorySafety", "migratable": true, "categories": ["StrictMemorySafety"], "flag_name": "-strict-memory-safety" }
6+
// CHECK-NEXT: ],
47
// CHECK-NEXT: "upcoming": [
5-
// CHECK: { "name": "{{.*}}"{{, "migratable": true}}, "enabled_in": "{{.*}}" }
8+
// CHECK: { "name": "InferIsolatedConformances", "migratable": true, "categories": ["IsolatedConformances"], "enabled_in": "7" },
69
// CHECK: ],
7-
// CHECK-NEXT: "experimental": [
10+
// CHECK: "experimental": [
811
// CHECK: { "name": "{{.*}}" }
912
// CHECK: ]
1013
// CHECK: }

0 commit comments

Comments
 (0)