Skip to content

Commit 4b31382

Browse files
committed
[swift-settings] Only allow for a setting to be passed exactly once to #SwiftSettings.
This responds to some feedback on the forums. Most importantly this allows for us to use variadic generics in the the type system to document whether we allow for "appending" behavior or not. Previously, for some options we would take the last behavior (and theoretically) for others would have silently had appending behavior. This just makes the behavior simple and more explicit.
1 parent cd38b78 commit 4b31382

File tree

4 files changed

+70
-3
lines changed

4 files changed

+70
-3
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8399,5 +8399,10 @@ ERROR(swift_settings_invalid_setting, none,
83998399
ERROR(swift_settings_must_be_top_level, none,
84008400
"#SwiftSettings can only be invoked as a top level declaration", ())
84018401

8402+
ERROR(swift_settings_duplicate_setting, none,
8403+
"duplicate setting passed to #SwiftSettings", ())
8404+
NOTE(swift_settings_duplicate_setting_original_loc, none,
8405+
"setting originally passed here", ())
8406+
84028407
#define UNDEFINE_DIAGNOSTIC_MACROS
84038408
#include "DefineDiagnosticMacros.h"

lib/AST/Module.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#include "llvm/ADT/SmallPtrSet.h"
6262
#include "llvm/ADT/StringExtras.h"
6363
#include "llvm/ADT/TinyPtrVector.h"
64+
#include "llvm/Support/CommandLine.h"
6465
#include "llvm/Support/MD5.h"
6566
#include "llvm/Support/MemoryBuffer.h"
6667
#include "llvm/Support/Path.h"
@@ -4191,20 +4192,41 @@ version::Version ModuleDecl::getLanguageVersionBuiltWith() const {
41914192
// MARK: SwiftSettings
41924193
//===----------------------------------------------------------------------===//
41934194

4195+
static llvm::cl::opt<bool> AllowForDuplicateSwiftSettings(
4196+
"swift-settings-allow-duplicates",
4197+
llvm::cl::desc("Option that allows for duplicate SwiftSettings. Just for "
4198+
"compiler testing"),
4199+
llvm::cl::Hidden);
4200+
41944201
namespace {
41954202

41964203
enum class SwiftSettingKind {
41974204
Unknown = 0,
41984205
DefaultIsolation,
4206+
4207+
LastKind = DefaultIsolation,
41994208
};
42004209

42014210
struct SwiftSettingsWalker : ASTWalker {
42024211
SourceFile &sf;
42034212
ASTContext &ctx;
42044213
SourceFileLangOptions result;
42054214

4215+
SmallVector<Expr *, 1> swiftSettingIndexToOriginalExprMap;
4216+
42064217
SwiftSettingsWalker(SourceFile &sf, ASTContext &ctx)
4207-
: sf(sf), ctx(ctx), result() {}
4218+
: sf(sf), ctx(ctx), result() {
4219+
// NOTE: We do not store a value for Unknown.
4220+
for (unsigned i : range(unsigned(SwiftSettingKind::LastKind))) {
4221+
(void)i;
4222+
swiftSettingIndexToOriginalExprMap.push_back(nullptr);
4223+
}
4224+
}
4225+
4226+
Expr *&getOriginalSwiftSetting(SwiftSettingKind kind) {
4227+
assert(kind != SwiftSettingKind::Unknown);
4228+
return swiftSettingIndexToOriginalExprMap[unsigned(kind) - 1];
4229+
}
42084230

42094231
/// Given a specific CallExpr, pattern matches the CallExpr's first argument
42104232
/// to validate it is MainActor.self. Returns CanType() if the CallExpr has
@@ -4263,13 +4285,30 @@ struct SwiftSettingsWalker : ASTWalker {
42634285
continue;
42644286

42654287
case SwiftSettingKind::DefaultIsolation:
4288+
auto *&expr =
4289+
getOriginalSwiftSetting(SwiftSettingKind::DefaultIsolation);
4290+
4291+
// If we already have an expr, emit an error and continue.
4292+
if (!AllowForDuplicateSwiftSettings && expr) {
4293+
ctx.Diags.diagnose(arg.getStartLoc(),
4294+
diag::swift_settings_duplicate_setting);
4295+
ctx.Diags.diagnose(
4296+
expr->getLoc(),
4297+
diag::swift_settings_duplicate_setting_original_loc);
4298+
foundValidArg = true;
4299+
continue;
4300+
}
4301+
4302+
// Otherwise, set things up appropriately.
42664303
if (auto actor = patternMatchDefaultIsolationMainActor(callExpr)) {
4304+
expr = callExpr;
42674305
result.defaultIsolation = actor;
42684306
foundValidArg = true;
42694307
continue;
42704308
}
42714309

42724310
if (isa<NilLiteralExpr>(callExpr->getArgs()->getExpr(0))) {
4311+
expr = callExpr;
42734312
result.defaultIsolation = {Type()};
42744313
foundValidArg = true;
42754314
continue;

test/Sema/swiftsettings.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -enable-experimental-feature SwiftSettings -enable-experimental-feature Macros -c -swift-version 6 -disable-availability-checking -verify %s
1+
// RUN: %target-swift-frontend -enable-experimental-feature SwiftSettings -enable-experimental-feature Macros -c -swift-version 6 -disable-availability-checking -verify -Xllvm -swift-settings-allow-duplicates %s
22

33
// REQUIRES: asserts
44
// REQUIRES: concurrency
@@ -15,7 +15,6 @@ actor MyActor {}
1515
#SwiftSettings(2) // expected-error {{Unrecognized setting passed to #SwiftSettings}}
1616
// expected-error @-1 {{cannot convert value of type 'Int' to expected argument type 'SwiftSetting'}}
1717

18-
// We should for now just take the last one that is specified.
1918
#SwiftSettings(.defaultIsolation(MainActor.self),
2019
.defaultIsolation(nil))
2120

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-swift-frontend -enable-experimental-feature SwiftSettings -enable-experimental-feature Macros -c -swift-version 6 -disable-availability-checking -verify %s
2+
3+
// REQUIRES: asserts
4+
// REQUIRES: concurrency
5+
// REQUIRES: swift_feature_Macros
6+
// REQUIRES: swift_feature_SwiftSettings
7+
8+
// This test specifically tests the behavior of #SwiftSettings when we find
9+
// multiple instances of the same setting.
10+
11+
actor MyActor {}
12+
13+
#SwiftSettings(.defaultIsolation(MainActor.self), // expected-note 6 {{setting originally passed here}}
14+
.defaultIsolation(nil)) // expected-error {{duplicate setting passed to #SwiftSettings}}
15+
#SwiftSettings(.defaultIsolation(nil)) // expected-error {{duplicate setting passed to #SwiftSettings}}
16+
#SwiftSettings(.defaultIsolation(MyActor.self)) // expected-error {{duplicate setting passed to #SwiftSettings}}
17+
#SwiftSettings(.defaultIsolation(1)) // expected-error {{duplicate setting passed to #SwiftSettings}}
18+
// expected-error @-1 {{cannot convert value of type 'Int' to expected argument type 'any Actor.Type'}}
19+
#SwiftSettings(2) // expected-error {{Unrecognized setting passed to #SwiftSettings}}
20+
// expected-error @-1 {{cannot convert value of type 'Int' to expected argument type 'SwiftSetting'}}
21+
22+
#SwiftSettings(.defaultIsolation(MainActor.self), // expected-error {{duplicate setting passed to #SwiftSettings}}
23+
.defaultIsolation(nil)) // expected-error {{duplicate setting passed to #SwiftSettings}}
24+

0 commit comments

Comments
 (0)