Skip to content

Commit b60cbd7

Browse files
committed
Remove ad-hoc ambiguity detection code; use conflicting()
1 parent 0914e58 commit b60cbd7

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
@@ -273,6 +273,9 @@ struct OverloadSignature {
273273
/// Whether this is an async function.
274274
unsigned IsAsyncFunction : 1;
275275

276+
/// Whether this is an distributed function.
277+
unsigned IsDistributed : 1;
278+
276279
/// Whether this is a enum element.
277280
unsigned IsEnumElement : 1;
278281

@@ -298,8 +301,8 @@ struct OverloadSignature {
298301
OverloadSignature()
299302
: UnaryOperator(UnaryOperatorKind::None), IsInstanceMember(false),
300303
IsVariable(false), IsFunction(false), IsAsyncFunction(false),
301-
InProtocolExtension(false), InExtensionOfGenericType(false),
302-
HasOpaqueReturnType(false) { }
304+
IsDistributed(false), InProtocolExtension(false),
305+
InExtensionOfGenericType(false), HasOpaqueReturnType(false) { }
303306
};
304307

305308
/// 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
@@ -5213,8 +5213,8 @@ ERROR(conflicting_default_argument_isolation,none,
52135213
ERROR(distributed_actor_needs_explicit_distributed_import,none,
52145214
"'Distributed' module not imported, required for 'distributed actor'",
52155215
())
5216-
ERROR(distributed_func_cannot_overload_on_async_only,none,
5217-
"ambiguous distributed func declaration %0, cannot overload distributed methods on effect only", (DeclName))
5216+
NOTE(distributed_func_cannot_overload_on_async_only,none,
5217+
"%0 previously declared here, cannot overload distributed methods on effect only", (const ValueDecl *))
52185218
NOTE(distributed_func_other_ambiguous_overload_here,none,
52195219
"ambiguous distributed func %0 declared here", (DeclName))
52205220
ERROR(actor_isolated_inout_state,none,

lib/AST/Decl.cpp

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

3075-
// If one is an async function and the other is not, they can't conflict.
3076-
if (sig1.IsAsyncFunction != sig2.IsAsyncFunction)
3077-
return false;
3075+
// For distributed decls, check there's no async/no-async overloads,
3076+
// since those are more fragile in distribution than we'd want distributed calls to be.
3077+
//
3078+
// A remote call is always 'async throws', and we can always record
3079+
// an async throws "accessor" (see AccessibleFunction.cpp) as such.
3080+
// This means, if we allowed async/no-async overloads of functions,
3081+
// we'd have to store the precise "it was not throwing" information,
3082+
// but we'll _never_ make use of such because all remote calls are
3083+
// necessarily going to async to the actor in the recipient process,
3084+
// and for the remote caller, they are always as-if-async.
3085+
//
3086+
// By banning such overloads, which may be useful in local APIs,
3087+
// but too fragile in distributed APIs, we allow a remote 'v2' version
3088+
// of an implementation to add or remove `async` to their implementation
3089+
// without breaking calls which were made on previous 'v1' versions of
3090+
// the same interface; Callers are never broken this way, and rollouts
3091+
// are simpler.
3092+
//
3093+
// The restriction on overloads is not a problem for distributed calls,
3094+
// as we don't have a vast swab of APIs which must compatibly get async
3095+
// versions, as that is what the async overloading aimed to address.
3096+
//
3097+
// Note also, that overloading on throws is already illegal anyway.
3098+
if (sig1.IsDistributed || sig2.IsDistributed) {
3099+
if (sig1.IsAsyncFunction != sig2.IsAsyncFunction)
3100+
return true;
3101+
} else {
3102+
// Otherwise one is an async function and the other is not, they don't conflict.
3103+
if (sig1.IsAsyncFunction != sig2.IsAsyncFunction)
3104+
return false;
3105+
}
30783106

30793107
// If one is a macro and the other is not, they can't conflict.
30803108
if (sig1.IsMacro != sig2.IsMacro)
@@ -3327,6 +3355,8 @@ OverloadSignature ValueDecl::getOverloadSignature() const {
33273355
signature.IsFunction = true;
33283356
if (func->hasAsync())
33293357
signature.IsAsyncFunction = true;
3358+
if (func->isDistributed())
3359+
signature.IsDistributed = true;
33303360
}
33313361

33323362
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
@@ -861,6 +861,16 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current,
861861
wouldBeSwift5Redeclaration = false;
862862
}
863863

864+
// Distributed declarations cannot be overloaded on async-ness only,
865+
// because it'd cause problems with the always async distributed thunks.
866+
// Provide an extra diagnostic if this is the case we're facing.
867+
bool diagnoseDistributedAsyncOverload = false;
868+
if (auto func = dyn_cast<AbstractFunctionDecl>(other)) {
869+
diagnoseDistributedAsyncOverload = func->isDistributed();
870+
} else if (auto var = dyn_cast<VarDecl>(other)) {
871+
diagnoseDistributedAsyncOverload = var->isDistributed();
872+
}
873+
864874
// If this isn't a redeclaration in the current version of Swift, but
865875
// would be in Swift 5 mode, emit a warning instead of an error.
866876
if (wouldBeSwift5Redeclaration) {
@@ -967,7 +977,13 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current,
967977
} else {
968978
ctx.Diags.diagnoseWithNotes(
969979
current->diagnose(diag::invalid_redecl, current), [&]() {
970-
other->diagnose(diag::invalid_redecl_prev, other);
980+
981+
// Add a specialized note about the 'other' overload
982+
if (diagnoseDistributedAsyncOverload) {
983+
other->diagnose(diag::distributed_func_cannot_overload_on_async_only, other);
984+
} else {
985+
other->diagnose(diag::invalid_redecl_prev, other);
986+
}
971987
});
972988

973989
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)