Skip to content

[6.0] Runtime: Fix runtime type resolution when mangled names refer to protocol extensions with Self same type constraints. #74999

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

Merged
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
35 changes: 34 additions & 1 deletion stdlib/public/runtime/MetadataLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,38 @@ _findExtendedTypeContextDescriptor(const ContextDescriptor *maybeExtension,
Demangle::NodePointer &node = demangledNode ? *demangledNode : localNode;

auto mangledName = extension->getMangledExtendedContext();

// A extension of the form `extension Protocol where Self == ConcreteType`
// is formally a protocol extension, so the formal generic parameter list
// is `<Self>`, but because of the same type constraint, the extended context
// looks like a reference to that nominal type. We want to match the
// extension's formal generic environment rather than the nominal type's
// in this case, so we should skip out on this case.
//
// We can detect this by looking at whether the generic context of the
// extension has a first generic parameter, which would be the Self parameter,
// with a same type constraint matching the extended type.
for (auto &reqt : extension->getGenericRequirements()) {
if (reqt.getKind() != GenericRequirementKind::SameType) {
continue;
}
// 'x' is the mangling of the first generic parameter
if (!reqt.getParam().equals("x")) {
continue;
}
// Is the generic parameter same-type-constrained to the same type
// we're extending? Then this is a `Self == ExtendedType` constraint.
// This is impossible for normal generic nominal type extensions because
// that would mean that you had:
// struct Foo<T> {...}
// extension Foo where T == Foo<T> {...}
// which would mean that the extended type is the infinite expansion
// Foo<Foo<Foo<Foo<...>>>>, which we don't allow.
if (reqt.getMangledTypeName().data() == mangledName.data()) {
return nullptr;
}
}

node = demangler.demangleType(mangledName,
ResolveAsSymbolicReference(demangler));
if (!node)
Expand Down Expand Up @@ -1252,7 +1284,8 @@ _gatherGenericParameters(const ContextDescriptor *context,
(void)_gatherGenericParameterCounts(context,
genericParamCounts, demangler);
unsigned numTotalGenericParams =
genericParamCounts.empty() ? 0 : genericParamCounts.back();
genericParamCounts.empty() ? context->getNumGenericParams()
: genericParamCounts.back();

// Check whether we have the right number of generic arguments.
if (genericArgs.size() == getLocalGenericParams(context).size()) {
Expand Down
27 changes: 27 additions & 0 deletions test/Runtime/protocol_self_same_type_extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// RUN: %target-run-simple-swift | %FileCheck %s
// REQUIRES: executable_test
// UNSUPPORTED: use_os_stdlib
// UNSUPPORTED: back_deployment_runtime

// rdar://130168101: Make sure that we correctly resolve types in
// the generic context of a protocol extension with a `Self` same
// type constraint.


protocol P { }

struct P2: P { }

extension P where Self == P2 {
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
dynamic func p2() -> some P {
return self
}
}

// CHECK: P2()
if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) {
print(P2().p2())
} else {
print(P2())
}