Skip to content

Commit d7c0356

Browse files
Merge pull request #4821 from swiftwasm/release/5.7
[pull] swiftwasm-release/5.7 from release/5.7
2 parents ab4b5ca + 0aacdfc commit d7c0356

File tree

8 files changed

+270
-13
lines changed

8 files changed

+270
-13
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4590,6 +4590,10 @@ ERROR(distributed_actor_isolated_non_self_reference,none,
45904590
ERROR(distributed_actor_needs_explicit_distributed_import,none,
45914591
"'Distributed' module not imported, required for 'distributed actor'",
45924592
())
4593+
ERROR(distributed_func_cannot_overload_on_async_only,none,
4594+
"ambiguous distributed func declaration %0, cannot overload distributed methods on effect only", (DeclName))
4595+
NOTE(distributed_func_other_ambiguous_overload_here,none,
4596+
"ambiguous distributed func %0 declared here", (DeclName))
45934597
ERROR(actor_isolated_inout_state,none,
45944598
"actor-isolated %0 %1 cannot be passed 'inout' to"
45954599
"%select{| implicitly}2 'async' function call",

lib/IRGen/GenDecl.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3437,9 +3437,9 @@ IRGenModule::getAddrOfLLVMVariable(LinkEntity entity,
34373437
? overrideDeclType
34383438
: entity.getDefaultDeclarationType(*this);
34393439

3440-
auto &entry = GlobalVars[entity];
3441-
if (entry) {
3442-
auto existing = cast<llvm::GlobalValue>(entry);
3440+
auto existingGlobal = GlobalVars[entity];
3441+
if (existingGlobal) {
3442+
auto existing = cast<llvm::GlobalValue>(existingGlobal);
34433443

34443444
// If we're looking to define something, we may need to replace a
34453445
// forward declaration.
@@ -3459,12 +3459,12 @@ IRGenModule::getAddrOfLLVMVariable(LinkEntity entity,
34593459

34603460
// Fall out to the case below, clearing the name so that
34613461
// createVariable doesn't detect a collision.
3462-
entry->setName("");
3462+
existingGlobal->setName("");
34633463

34643464
// Otherwise, we have a previous declaration or definition which
34653465
// we need to ensure has the right type.
34663466
} else {
3467-
return getElementBitCast(entry, defaultType);
3467+
return getElementBitCast(existingGlobal, defaultType);
34683468
}
34693469
}
34703470

@@ -3508,11 +3508,17 @@ IRGenModule::getAddrOfLLVMVariable(LinkEntity entity,
35083508
lazyInitializer->Create(var);
35093509
}
35103510

3511+
if (lazyInitializer) {
3512+
// Protect against self-references that might've been created during
3513+
// the lazy emission.
3514+
existingGlobal = GlobalVars[entity];
3515+
}
3516+
35113517
// If we have an existing entry, destroy it, replacing it with the
3512-
// new variable.
3513-
if (entry) {
3514-
auto existing = cast<llvm::GlobalValue>(entry);
3515-
auto castVar = llvm::ConstantExpr::getBitCast(var, entry->getType());
3518+
// new variable. We only really have to do
3519+
if (existingGlobal) {
3520+
auto existing = cast<llvm::GlobalValue>(existingGlobal);
3521+
auto castVar = llvm::ConstantExpr::getBitCast(var, existing->getType());
35163522
existing->replaceAllUsesWith(castVar);
35173523
existing->eraseFromParent();
35183524
}
@@ -3535,7 +3541,7 @@ IRGenModule::getAddrOfLLVMVariable(LinkEntity entity,
35353541
}
35363542

35373543
// Cache and return.
3538-
entry = var;
3544+
GlobalVars[entity] = var;
35393545
return var;
35403546
}
35413547

lib/Sema/CSSimplify.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3856,6 +3856,13 @@ ConstraintSystem::matchTypesBindTypeVar(
38563856
// let's ignore this mismatch and mark affected type variable as a hole
38573857
// because something else has to be fixed already for this to happen.
38583858
if (type->is<DependentMemberType>() && !type->hasTypeVariable()) {
3859+
// Since the binding couldn't be performed, the type variable is a
3860+
// hole regardless whether it would be bound later to some other
3861+
// type or not. If this is not reflected in constraint system
3862+
// it would let the solver to form a _valid_ solution as if the
3863+
// constraint between the type variable and the unresolved dependent
3864+
// member type never existed.
3865+
increaseScore(SK_Hole);
38593866
recordPotentialHole(typeVar);
38603867
return getTypeMatchSuccess();
38613868
}

lib/Sema/TypeCheckDistributed.cpp

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -677,10 +677,21 @@ void TypeChecker::checkDistributedActor(SourceFile *SF, NominalTypeDecl *nominal
677677
// If applicable, this will create the default 'init(transport:)' initializer
678678
(void)nominal->getDefaultInitializer();
679679

680+
// We check decls for ambiguity more strictly than normal nominal types,
681+
// because we want to record distributed accessors the same if function they
682+
// point at (in a remote process) is async or not, as that has no effect on
683+
// a caller from a different process, so we want to make the remoteCall target
684+
// identifiers, less fragile against such refactorings.
685+
//
686+
// To achieve this, we ban overloads on just "effects" of functions,
687+
// which are useful in local settings, but really should not be relied
688+
// on as differenciators in remote calls - the call will always be "async"
689+
// since it will go through a thunk, and then be asynchronously transferred
690+
// to the called process.
691+
llvm::SmallDenseSet<DeclName, 2> diagnosedAmbiguity;
680692

681693
for (auto member : nominal->getMembers()) {
682-
// A distributed computed property needs to have a thunk for
683-
// its getter accessor.
694+
// --- Ensure 'distributed func' all thunks
684695
if (auto *var = dyn_cast<VarDecl>(member)) {
685696
if (!var->isDistributed())
686697
continue;
@@ -691,7 +702,7 @@ void TypeChecker::checkDistributedActor(SourceFile *SF, NominalTypeDecl *nominal
691702
continue;
692703
}
693704

694-
// --- Ensure all thunks
705+
// --- Ensure 'distributed func' all thunks
695706
if (auto func = dyn_cast<AbstractFunctionDecl>(member)) {
696707
if (!func->isDistributed())
697708
continue;
@@ -704,6 +715,48 @@ void TypeChecker::checkDistributedActor(SourceFile *SF, NominalTypeDecl *nominal
704715
nominal->getName());
705716
return;
706717
}
718+
719+
// Check there's no async/no-async overloads, since those are more
720+
// fragile in distribution than we'd want distributed calls to be.
721+
// A remote call is always 'async throws', and we can always record
722+
// an async throws "accessor" (see AccessibleFunction.cpp) as such.
723+
// This means, if we allowed async/no-async overloads of functions,
724+
// we'd have to store the precise "it was not throwing" information,
725+
// but we'll _never_ make use of such because all remote calls are
726+
// necessarily going to async to the actor in the recipient process,
727+
// and for the remote caller, they are always as-if-async.
728+
//
729+
// By banning such overloads, which may be useful in local APIs,
730+
// but too fragile in distributed APIs, we allow a remote 'v2' version
731+
// of an implementation to add or remove `async` to their implementation
732+
// without breaking calls which were made on previous 'v1' versions of
733+
// the same interface; Callers are never broken this way, and rollouts
734+
// are simpler.
735+
//
736+
// The restriction on overloads is not a problem for distributed calls,
737+
// as we don't have a vast swab of APIs which must compatibly get async
738+
// versions, as that is what the async overloading aimed to address.
739+
//
740+
// Note also, that overloading on throws is already illegal anyway.
741+
if (!diagnosedAmbiguity.contains(func->getName())) {
742+
auto candidates = nominal->lookupDirect(func->getName());
743+
if (candidates.size() > 1) {
744+
auto firstDecl = dyn_cast<AbstractFunctionDecl>(candidates.back());
745+
for (auto decl : candidates) {
746+
if (decl == firstDecl) {
747+
decl->diagnose(
748+
diag::distributed_func_cannot_overload_on_async_only,
749+
decl->getName());
750+
} else {
751+
decl->diagnose(
752+
diag::distributed_func_other_ambiguous_overload_here,
753+
decl->getName());
754+
}
755+
}
756+
757+
diagnosedAmbiguity.insert(func->getName());
758+
}
759+
}
707760
}
708761

709762
if (auto thunk = func->getDistributedThunk()) {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/../Inputs/FakeDistributedActorSystems.swift
3+
// RUN: %target-build-swift -module-name main -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeDistributedActorSystems.swift -o %t/a.out
4+
// RUN: %target-run %t/a.out | %FileCheck %s --color --dump-input=always
5+
6+
// REQUIRES: executable_test
7+
// REQUIRES: concurrency
8+
// REQUIRES: distributed
9+
10+
// rdar://76038845
11+
// UNSUPPORTED: use_os_stdlib
12+
// UNSUPPORTED: back_deployment_runtime
13+
14+
// FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574
15+
// UNSUPPORTED: OS=windows-msvc
16+
17+
import Distributed
18+
import FakeDistributedActorSystems
19+
20+
typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem
21+
22+
distributed actor Greeter {
23+
distributed func usedToHaveAsyncBytNotAnymore()
24+
/*previously had async, and remote called invoked such async method */ -> String {
25+
return #function
26+
}
27+
}
28+
29+
distributed actor Helloer {
30+
distributed func usedToBeSyncButNowIsAsync()
31+
/* The remote caller thinks this method was not async, but actually it is */
32+
async -> String {
33+
return #function
34+
}
35+
}
36+
37+
func test_usedToBeAsync_but_remoteImplIsNotAnymore() async throws {
38+
let system = DefaultDistributedActorSystem()
39+
40+
let local = Greeter(actorSystem: system)
41+
let remote = try Greeter.resolve(id: local.id, using: system)
42+
43+
var invocation = FakeInvocationEncoder()
44+
// let reply = try await remote.usedToHaveAsyncBytNotAnymore()
45+
let reply: String = try await system.remoteCall(
46+
on: remote,
47+
// Important: notice the mangling has a YaK here, since we mangled
48+
// based on the distributed thunk, which is 'async throws' (YaK).
49+
//
50+
// This test is about ensuring, that even if a remote recipient process,
51+
// changed their implementation from async to not async, we're still able
52+
// to invoke them. From the perspective of the remote caller it truly does
53+
// not matter how the recipient implements this call: is it async or not,
54+
// and we should not tie ability to invoke a method to it's asyncness.
55+
//
56+
// Note also that a remote call is always async and throws as well,
57+
// so mangling based on the 'async throws' thunk does not introduce
58+
// unexpected effects in remote calls.
59+
//
60+
// Design limitation by choice: this means we cannot
61+
target: RemoteCallTarget("$s4main7GreeterC28usedToHaveAsyncBytNotAnymoreSSyYaKFTE"),
62+
invocation: &invocation,
63+
throwing: Never.self,
64+
returning: String.self
65+
)
66+
67+
// CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.usedToHaveAsyncBytNotAnymore(), invocation:FakeInvocationEncoder(genericSubs: [], arguments: [], returnType: nil, errorType: nil), throwing:Swift.Never, returning:Swift.String
68+
// CHECK: << onReturn: usedToHaveAsyncBytNotAnymore()
69+
70+
print("reply: \(reply)") // CHECK: reply: usedToHaveAsyncBytNotAnymore()
71+
}
72+
73+
func test_usedToBeSync_but_remoteImplIsAsyncNow() async throws {
74+
let system = DefaultDistributedActorSystem()
75+
76+
let local = Helloer(actorSystem: system)
77+
let remote = try Helloer.resolve(id: local.id, using: system)
78+
79+
var invocation = FakeInvocationEncoder()
80+
// let reply: String = try await remote.usedToBeSyncButNowIsAsync()
81+
let reply: String = try await system.remoteCall(
82+
on: remote,
83+
target: RemoteCallTarget("$s4main7HelloerC25usedToBeSyncButNowIsAsyncSSyYaKFTE"),
84+
invocation: &invocation,
85+
throwing: Never.self,
86+
returning: String.self
87+
)
88+
89+
// CHECK: >> remoteCall: on:main.Helloer, target:main.Helloer.usedToBeSyncButNowIsAsync(), invocation:FakeInvocationEncoder(genericSubs: [], arguments: [], returnType: nil, errorType: nil), throwing:Swift.Never, returning:Swift.String
90+
// CHECK: << onReturn: usedToBeSyncButNowIsAsync()
91+
92+
print("reply: \(reply)") // CHECK: reply: usedToBeSyncButNowIsAsync()
93+
}
94+
95+
@main struct Main {
96+
static func main() async {
97+
try! await test_usedToBeAsync_but_remoteImplIsNotAnymore()
98+
try! await test_usedToBeSync_but_remoteImplIsAsyncNow()
99+
}
100+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/Inputs/FakeDistributedActorSystems.swift
3+
// RUN: %target-swift-frontend -typecheck -verify -disable-availability-checking -I %t 2>&1 %s
4+
// REQUIRES: concurrency
5+
// REQUIRES: distributed
6+
7+
import Distributed
8+
import FakeDistributedActorSystems
9+
10+
typealias DefaultDistributedActorSystem = FakeActorSystem
11+
12+
// ==== ------------------------------------------------------------------------
13+
14+
distributed actor Overloader {
15+
16+
func overloaded() {}
17+
func overloaded() async {}
18+
19+
distributed func overloadedDistA() {} // expected-note{{ambiguous distributed func 'overloadedDistA()' declared here}}
20+
distributed func overloadedDistA() async {} // expected-error{{ambiguous distributed func declaration 'overloadedDistA()', cannot overload distributed methods on effect only}}
21+
22+
distributed func overloadedDistT() throws {} // expected-note{{ambiguous distributed func 'overloadedDistT()' declared here}}
23+
distributed func overloadedDistT() async throws {} // expected-error{{ambiguous distributed func declaration 'overloadedDistT()', cannot overload distributed methods on effect only}}
24+
25+
// Throws overloads are not legal anyway, but let's check for them here too:
26+
distributed func overloadedDistThrows() {}
27+
// expected-note@-1{{ambiguous distributed func 'overloadedDistThrows()' declared here}}
28+
// expected-note@-2{{'overloadedDistThrows()' previously declared here}}
29+
distributed func overloadedDistThrows() throws {}
30+
// expected-error@-1{{ambiguous distributed func declaration 'overloadedDistThrows()', cannot overload distributed methods on effect only}}
31+
// expected-error@-2{{invalid redeclaration of 'overloadedDistThrows()'}}
32+
33+
distributed func overloadedDistAsync() async {}
34+
// expected-note@-1{{ambiguous distributed func 'overloadedDistAsync()' declared here}}
35+
// expected-note@-2{{'overloadedDistAsync()' previously declared here}}
36+
distributed func overloadedDistAsync() async throws {}
37+
// expected-error@-1{{ambiguous distributed func declaration 'overloadedDistAsync()', cannot overload distributed methods on effect only}}
38+
// expected-error@-2{{invalid redeclaration of 'overloadedDistAsync()'}}
39+
}
40+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-run-simple-swiftgyb
2+
3+
%for N in range(0, 100):
4+
5+
protocol P${N}<A, B> {
6+
associatedtype A
7+
associatedtype B
8+
}
9+
10+
%end
11+
12+
var array : [Any.Type] = [
13+
%for N in range(0, 100):
14+
(any P${N}<Int, Float>).self,
15+
%end
16+
%for N in range(0, 100):
17+
(any P${N}<Float, String>).self,
18+
%end
19+
]
20+
21+
print(array)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol P {}
4+
5+
protocol Key {
6+
associatedtype A: P
7+
// expected-note@-1 {{unable to infer associated type 'A' for protocol 'Key'}}
8+
static var value: A { get }
9+
}
10+
11+
struct Values {
12+
subscript<K: Key>(type: K.Type) -> K.A {
13+
fatalError()
14+
}
15+
}
16+
17+
enum MyKey: Key { // expected-error {{type 'MyKey' does not conform to protocol 'Key'}}
18+
static let value = 1
19+
// expected-note@-1 {{candidate would match and infer 'A' = 'Int' if 'Int' conformed to 'P'}}
20+
}
21+
22+
extension Values {
23+
var myValue: Int {
24+
get { self[MyKey.self] }
25+
}
26+
}

0 commit comments

Comments
 (0)