Skip to content

[Distributed] Survive generics with Sendable requirement in executeDistributedTarget #41854

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion stdlib/public/runtime/MetadataLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2120,7 +2120,11 @@ swift_distributed_getWitnessTables(GenericEnvironmentDescriptor *genericEnv,
},
[&substFn](const Metadata *type, unsigned index) {
return substFn.getWitnessTable(type, index);
});
},
/*skipUnknownKinds=*/true);
// The reason we skip unknown kinds is to avoid reporting missing
// conformances to Sendable, which cannot be checked at runtime, where we're
// calling this from 'executeDistributedTarget'.

if (error) {
return {/*ptr=*/nullptr, -1};
Expand Down
6 changes: 5 additions & 1 deletion stdlib/public/runtime/Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,13 +462,17 @@ class TypeInfo {
/// \param extraArguments The extra arguments determined while checking
/// generic requirements (e.g., those that need to be
/// passed to an instantiation function) will be added to this vector.
/// \param skipUnknownKinds If true, unknown requirement kinds will not be
/// reported as errors. This should used with care, and e.g. only to ignore
/// @_marker protocols at runtime, which cannot be checked at runtime.
Comment on lines +465 to +467
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, should we consider a runtime representation that indicates the requirement came from a marker protocol? Having any representation at runtime sounds like it goes against the purpose of a marker protocol, but I guess that was unavoidable in this case?

///
/// \returns the error if an error occurred, None otherwise.
llvm::Optional<TypeLookupError> _checkGenericRequirements(
llvm::ArrayRef<GenericRequirementDescriptor> requirements,
llvm::SmallVectorImpl<const void *> &extraArguments,
SubstGenericParameterFn substGenericParam,
SubstDependentWitnessTableFn substWitnessTable);
SubstDependentWitnessTableFn substWitnessTable,
bool skipUnknownKinds = false);

/// A helper function which avoids performing a store if the destination
/// address already contains the source value. This is useful when
Expand Down
11 changes: 8 additions & 3 deletions stdlib/public/runtime/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1234,11 +1234,16 @@ llvm::Optional<TypeLookupError> swift::_checkGenericRequirements(
llvm::ArrayRef<GenericRequirementDescriptor> requirements,
llvm::SmallVectorImpl<const void *> &extraArguments,
SubstGenericParameterFn substGenericParam,
SubstDependentWitnessTableFn substWitnessTable) {
SubstDependentWitnessTableFn substWitnessTable, bool skipUnknownKinds) {
for (const auto &req : requirements) {
// Make sure we understand the requirement we're dealing with.
if (!req.hasKnownKind())
return TypeLookupError("unknown kind");
if (!req.hasKnownKind()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the kind here ends up being some weird 24 for the Sendable requirement...
Since it has an unknown kind we can't really get the mangled name or protocol etc to check it in detail if it is the exact sendable.

But the same issue would happen with any other @_marker protocol I suppose...

if (skipUnknownKinds) {
continue;
} else {
return TypeLookupError("unknown kind");
}
}

// Resolve the subject generic parameter.
auto result = swift_getTypeByMangledName(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import FakeDistributedActorSystems

typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem

protocol SomeProtocol {}
extension String: SomeProtocol {}

distributed actor Greeter {
distributed func noParams() {}
distributed func noParamsThrows() throws {}
Expand All @@ -28,9 +31,10 @@ distributed actor Greeter {
distributed func oneLabel(value: String, _ value2: String, _ value3: String) {}
distributed func parameterSingle(first: String) {}
distributed func parameterPair(first: String, second: Int) {}
// FIXME(distributed): rdar://90293494 fails to get
// distributed func generic<A: Codable & Sendable>(first: A) {}
// distributed func genericNoLabel<A: Codable & Sendable>(_ first: A) {}

distributed func generic<A: Codable & Sendable>(first: A) {}
distributed func genericThree<A: Codable & Sendable & SomeProtocol>(first: A) {}
distributed func genericThreeTwo<A: Codable & Sendable, B: Codable & SomeProtocol>(first: A, second: B) {}
}
extension Greeter {
distributed func parameterTriple(first: String, second: Int, third: Double) {}
Expand Down Expand Up @@ -62,19 +66,25 @@ func test() async throws {
_ = try await greeter.parameterPair(first: "X", second: 2)
// CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.parameterPair(first:second:)

_ = try await greeter.parameterTriple(first: "X", second: 2, third: 3.0)
// CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.parameterTriple(first:second:third:)
_ = try await greeter.generic(first: "X")
// CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.generic(first:)

_ = try await greeter.genericThree(first: "X")
// CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.genericThree(first:)

// FIXME: rdar://90293494 seems to fail getting the substitutions?
// _ = try await greeter.generic(first: "X")
// // TODO: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.parameterTriple(first:second:third:)
_ = try await greeter.genericThreeTwo(first: "X", second: "SecondValue")
// CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.genericThreeTwo(first:second:)

print("done")
// CHECK: done
}

@main struct Main {
static func main() async {
try! await test()
do {
try await test()
} catch {
print("ERROR: \(error)")
}
}
}