Skip to content

Commit 8fde809

Browse files
committed
Remove ad-hoc ambiguity detection code; use conflicting()
1 parent faf2870 commit 8fde809

File tree

6 files changed

+74
-77
lines changed

6 files changed

+74
-77
lines changed

include/swift/AST/Decl.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ struct OverloadSignature {
279279
/// Whether this is an async function.
280280
unsigned IsAsyncFunction : 1;
281281

282+
/// Whether this is an distributed function.
283+
unsigned IsDistributed : 1;
284+
282285
/// Whether this is a enum element.
283286
unsigned IsEnumElement : 1;
284287

@@ -304,8 +307,8 @@ struct OverloadSignature {
304307
OverloadSignature()
305308
: UnaryOperator(UnaryOperatorKind::None), IsInstanceMember(false),
306309
IsVariable(false), IsFunction(false), IsAsyncFunction(false),
307-
InProtocolExtension(false), InExtensionOfGenericType(false),
308-
HasOpaqueReturnType(false) { }
310+
IsDistributed(false), InProtocolExtension(false),
311+
InExtensionOfGenericType(false), HasOpaqueReturnType(false) { }
309312
};
310313

311314
/// Determine whether two overload signatures conflict.

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5351,8 +5351,8 @@ ERROR(conflicting_default_argument_isolation,none,
53515351
ERROR(distributed_actor_needs_explicit_distributed_import,none,
53525352
"'Distributed' module not imported, required for 'distributed actor'",
53535353
())
5354-
ERROR(distributed_func_cannot_overload_on_async_only,none,
5355-
"ambiguous distributed func declaration %0, cannot overload distributed methods on effect only", (DeclName))
5354+
NOTE(distributed_func_cannot_overload_on_async_only,none,
5355+
"%0 previously declared here, cannot overload distributed methods on effect only", (const ValueDecl *))
53565356
NOTE(distributed_func_other_ambiguous_overload_here,none,
53575357
"ambiguous distributed func %0 declared here", (DeclName))
53585358
ERROR(actor_isolated_inout_state,none,

lib/AST/Decl.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3204,9 +3204,37 @@ bool swift::conflicting(const OverloadSignature& sig1,
32043204
if (sig1.IsInstanceMember != sig2.IsInstanceMember)
32053205
return false;
32063206

3207-
// If one is an async function and the other is not, they can't conflict.
3208-
if (sig1.IsAsyncFunction != sig2.IsAsyncFunction)
3209-
return false;
3207+
// For distributed decls, check there's no async/no-async overloads,
3208+
// since those are more fragile in distribution than we'd want distributed calls to be.
3209+
//
3210+
// A remote call is always 'async throws', and we can always record
3211+
// an async throws "accessor" (see AccessibleFunction.cpp) as such.
3212+
// This means, if we allowed async/no-async overloads of functions,
3213+
// we'd have to store the precise "it was not throwing" information,
3214+
// but we'll _never_ make use of such because all remote calls are
3215+
// necessarily going to async to the actor in the recipient process,
3216+
// and for the remote caller, they are always as-if-async.
3217+
//
3218+
// By banning such overloads, which may be useful in local APIs,
3219+
// but too fragile in distributed APIs, we allow a remote 'v2' version
3220+
// of an implementation to add or remove `async` to their implementation
3221+
// without breaking calls which were made on previous 'v1' versions of
3222+
// the same interface; Callers are never broken this way, and rollouts
3223+
// are simpler.
3224+
//
3225+
// The restriction on overloads is not a problem for distributed calls,
3226+
// as we don't have a vast swab of APIs which must compatibly get async
3227+
// versions, as that is what the async overloading aimed to address.
3228+
//
3229+
// Note also, that overloading on throws is already illegal anyway.
3230+
if (sig1.IsDistributed || sig2.IsDistributed) {
3231+
if (sig1.IsAsyncFunction != sig2.IsAsyncFunction)
3232+
return true;
3233+
} else {
3234+
// Otherwise one is an async function and the other is not, they don't conflict.
3235+
if (sig1.IsAsyncFunction != sig2.IsAsyncFunction)
3236+
return false;
3237+
}
32103238

32113239
// If one is a macro and the other is not, they can't conflict.
32123240
if (sig1.IsMacro != sig2.IsMacro)
@@ -3459,6 +3487,8 @@ OverloadSignature ValueDecl::getOverloadSignature() const {
34593487
signature.IsFunction = true;
34603488
if (func->hasAsync())
34613489
signature.IsAsyncFunction = true;
3490+
if (func->isDistributed())
3491+
signature.IsDistributed = true;
34623492
}
34633493

34643494
if (auto *extension = dyn_cast<ExtensionDecl>(getDeclContext()))

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,16 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current,
830830
wouldBeSwift5Redeclaration = false;
831831
}
832832

833+
// Distributed declarations cannot be overloaded on async-ness only,
834+
// because it'd cause problems with the always async distributed thunks.
835+
// Provide an extra diagnostic if this is the case we're facing.
836+
bool diagnoseDistributedAsyncOverload = false;
837+
if (auto func = dyn_cast<AbstractFunctionDecl>(other)) {
838+
diagnoseDistributedAsyncOverload = func->isDistributed();
839+
} else if (auto var = dyn_cast<VarDecl>(other)) {
840+
diagnoseDistributedAsyncOverload = var->isDistributed();
841+
}
842+
833843
// If this isn't a redeclaration in the current version of Swift, but
834844
// would be in Swift 5 mode, emit a warning instead of an error.
835845
if (wouldBeSwift5Redeclaration) {
@@ -936,7 +946,13 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current,
936946
} else {
937947
ctx.Diags.diagnoseWithNotes(
938948
current->diagnose(diag::invalid_redecl, current), [&]() {
939-
other->diagnose(diag::invalid_redecl_prev, other);
949+
950+
// Add a specialized note about the 'other' overload
951+
if (diagnoseDistributedAsyncOverload) {
952+
other->diagnose(diag::distributed_func_cannot_overload_on_async_only, other);
953+
} else {
954+
other->diagnose(diag::invalid_redecl_prev, other);
955+
}
940956
});
941957

942958
current->setInvalid();

lib/Sema/TypeCheckDistributed.cpp

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@ void TypeChecker::checkDistributedActor(SourceFile *SF, NominalTypeDecl *nominal
707707
// on as differenciators in remote calls - the call will always be "async"
708708
// since it will go through a thunk, and then be asynchronously transferred
709709
// to the called process.
710-
llvm::SmallDenseSet<DeclName, 2> diagnosedAmbiguity;
710+
// llvm::SmallDenseSet<DeclName, 2> diagnosedAmbiguity;
711711

712712
for (auto member : nominal->getMembers()) {
713713
// --- Ensure 'distributed func' all thunks
@@ -734,63 +734,6 @@ void TypeChecker::checkDistributedActor(SourceFile *SF, NominalTypeDecl *nominal
734734
nominal->getName());
735735
return;
736736
}
737-
738-
// Check there's no async/no-async overloads, since those are more
739-
// fragile in distribution than we'd want distributed calls to be.
740-
// A remote call is always 'async throws', and we can always record
741-
// an async throws "accessor" (see AccessibleFunction.cpp) as such.
742-
// This means, if we allowed async/no-async overloads of functions,
743-
// we'd have to store the precise "it was not throwing" information,
744-
// but we'll _never_ make use of such because all remote calls are
745-
// necessarily going to async to the actor in the recipient process,
746-
// and for the remote caller, they are always as-if-async.
747-
//
748-
// By banning such overloads, which may be useful in local APIs,
749-
// but too fragile in distributed APIs, we allow a remote 'v2' version
750-
// of an implementation to add or remove `async` to their implementation
751-
// without breaking calls which were made on previous 'v1' versions of
752-
// the same interface; Callers are never broken this way, and rollouts
753-
// are simpler.
754-
//
755-
// The restriction on overloads is not a problem for distributed calls,
756-
// as we don't have a vast swab of APIs which must compatibly get async
757-
// versions, as that is what the async overloading aimed to address.
758-
//
759-
// Note also, that overloading on throws is already illegal anyway.
760-
if (!diagnosedAmbiguity.contains(func->getName())) {
761-
auto candidates = nominal->lookupDirect(func->getName());
762-
if (candidates.size() > 1) {
763-
auto firstDecl = dyn_cast<AbstractFunctionDecl>(candidates.back());
764-
for (auto candidate: candidates) {
765-
if (auto candidateFunc = dyn_cast<AbstractFunctionDecl>(candidate)) {
766-
assert(candidateFunc->getParameters()->size() ==
767-
firstDecl->getParameters()->size());
768-
bool allSame = true;
769-
for (size_t i = 0; i < candidateFunc->getParameters()->size(); ++i) {
770-
auto lhs = firstDecl->getParameters()->get(i);
771-
auto rhs = candidateFunc->getParameters()->get(i);
772-
if (!lhs->getInterfaceType()->isEqual(rhs->getInterfaceType())) {
773-
allSame = false;
774-
break;
775-
}
776-
}
777-
778-
if (candidate != firstDecl && // can't be ambiguous with itself
779-
allSame && // diagnose if ambiguous
780-
!diagnosedAmbiguity.contains(func->getName())) {
781-
candidate->diagnose(
782-
diag::distributed_func_cannot_overload_on_async_only,
783-
candidate->getName());
784-
diagnosedAmbiguity.insert(func->getName());
785-
} else if (diagnosedAmbiguity.contains(func->getName())) {
786-
candidate->diagnose(
787-
diag::distributed_func_other_ambiguous_overload_here,
788-
candidate->getName());
789-
}
790-
}
791-
}
792-
}
793-
}
794737
}
795738

796739
if (auto thunk = func->getDistributedThunk()) {

test/Distributed/distributed_func_overloads.swift

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,39 @@ distributed actor Overloader {
1616
func overloaded() {}
1717
func overloaded() async {}
1818

19-
distributed func overloadedDistA() {} // expected-error{{ambiguous distributed func declaration 'overloadedDistA()', cannot overload distributed methods on effect only}}
20-
distributed func overloadedDistA() async {} // expected-note{{ambiguous distributed func 'overloadedDistA()' declared here}}
19+
distributed func overloadedDistA() {}
20+
// expected-note@-1{{'overloadedDistA()' previously declared here, cannot overload distributed methods on effect only}}
21+
distributed func overloadedDistA() async {}
22+
// expected-error@-1{{invalid redeclaration of 'overloadedDistA()'}}
2123

22-
distributed func overloadedDistT() throws {} // expected-error{{ambiguous distributed func declaration 'overloadedDistT()', cannot overload distributed methods on effect only}}
23-
distributed func overloadedDistT() async throws {} // expected-note{{ambiguous distributed func 'overloadedDistT()' declared here}}
24+
distributed func overloadedDistT() throws {}
25+
// expected-note@-1{{'overloadedDistT()' previously declared here, cannot overload distributed methods on effect only}}
26+
distributed func overloadedDistT() async throws {}
27+
// expected-error@-1{{invalid redeclaration of 'overloadedDistT()'}}
28+
29+
distributed func overloadedDistST(string: String) throws {}
30+
// expected-note@-1{{'overloadedDistST(string:)' previously declared here, cannot overload distributed methods on effect only}}
31+
distributed func overloadedDistST(string: String) async throws {}
32+
// expected-error@-1{{invalid redeclaration of 'overloadedDistST(string:)'}}
2433

2534
// Throws overloads are not legal anyway, but let's check for them here too:
2635
distributed func overloadedDistThrows() {}
2736
// expected-note@-1{{'overloadedDistThrows()' previously declared here}}
28-
// expected-error@-2{{ambiguous distributed func declaration 'overloadedDistThrows()', cannot overload distributed methods on effect only}}
2937
distributed func overloadedDistThrows() throws {}
3038
// expected-error@-1{{invalid redeclaration of 'overloadedDistThrows()'}}
31-
// expected-note@-2{{ambiguous distributed func 'overloadedDistThrows()' declared here}}
3239

3340
distributed func overloadedDistAsync() async {}
3441
// expected-note@-1{{'overloadedDistAsync()' previously declared here}}
35-
// expected-error@-2{{ambiguous distributed func declaration 'overloadedDistAsync()', cannot overload distributed methods on effect only}}
3642
distributed func overloadedDistAsync() async throws {}
3743
// expected-error@-1{{invalid redeclaration of 'overloadedDistAsync()'}}
38-
// expected-note@-2{{ambiguous distributed func 'overloadedDistAsync()' declared here}}
3944

4045
// overloads differing by parameter type are allowed,
4146
// since the mangled identifier includes full type information:
42-
distributed func overloadedDistParams(param: String) async {}
43-
distributed func overloadedDistParams(param: Int) async {}
47+
distributed func overloadedDistParams(param: String) async {} // ok
48+
distributed func overloadedDistParams(param: Int) async {} // ok
4449

4550
distributed func overloadedDistParams() async {} // also ok
4651

47-
distributed func overloadedDistParams<A: Sendable & Codable>(param: A) async {}
52+
distributed func overloadedDistParams<A: Sendable & Codable>(param: A) async {} // ok
4853
}
4954

0 commit comments

Comments
 (0)