Skip to content

Fix ExistentialSpecializer: inherited conformance #28430

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
merged 1 commit into from
Nov 22, 2019
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
6 changes: 3 additions & 3 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -866,9 +866,9 @@ class ASTContext final {
CanGenericSignature getSingleGenericParameterSignature() const;

/// Retrieve a generic signature with a single type parameter conforming
/// to the given existential type.
CanGenericSignature getExistentialSignature(CanType existential,
ModuleDecl *mod);
/// to the given opened archetype.
CanGenericSignature getOpenedArchetypeSignature(CanType existential,
ModuleDecl *mod);

GenericSignature getOverrideGenericSignature(const ValueDecl *base,
const ValueDecl *derived);
Expand Down
15 changes: 11 additions & 4 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3689,8 +3689,8 @@ GenericEnvironment *OpenedArchetypeType::getGenericEnvironment() const {
auto thisType = Type(const_cast<OpenedArchetypeType*>(this));
auto &ctx = thisType->getASTContext();
// Create a generic environment to represent the opened type.
auto signature = ctx.getExistentialSignature(Opened->getCanonicalType(),
nullptr);
auto signature =
ctx.getOpenedArchetypeSignature(Opened->getCanonicalType(), nullptr);
auto *builder = signature->getGenericSignatureBuilder();
auto *env = GenericEnvironment::getIncomplete(signature, builder);
env->addMapping(signature->getGenericParams()[0], thisType);
Expand Down Expand Up @@ -4323,8 +4323,15 @@ CanGenericSignature ASTContext::getSingleGenericParameterSignature() const {
return canonicalSig;
}

CanGenericSignature ASTContext::getExistentialSignature(CanType existential,
ModuleDecl *mod) {
// Return the signature for an opened existential. The opened archetype may have
// a different set of conformances from the corresponding existential. The
// opened archetype conformances are dictated by the ABI for generic arguments,
// while the existential value conformances are dictated by their layout (see
// Type::getExistentialLayout()). In particular, the opened archetype signature
// does not have requirements for conformances inherited from superclass
// constraints while existential values do.
CanGenericSignature ASTContext::getOpenedArchetypeSignature(CanType existential,
ModuleDecl *mod) {
auto found = getImpl().ExistentialSignatures.find(existential);
if (found != getImpl().ExistentialSignatures.end())
return found->second;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#define DEBUG_TYPE "sil-existential-transform"
#include "ExistentialTransform.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/SIL/OptimizationRemark.h"
Expand Down Expand Up @@ -114,11 +115,29 @@ void ExistentialSpecializerCloner::cloneAndPopulateFunction() {
}
}

// Gather the conformances needed for an existential value based on an opened
// archetype. This adds any conformances inherited from superclass constraints.
static ArrayRef<ProtocolConformanceRef>
collectExistentialConformances(ModuleDecl *M, CanType openedType,
CanType existentialType) {
assert(!openedType.isAnyExistentialType());

auto layout = existentialType.getExistentialLayout();
auto protocols = layout.getProtocols();

SmallVector<ProtocolConformanceRef, 4> conformances;
for (auto proto : protocols) {
auto conformance = M->lookupConformance(openedType, proto->getDecl());
assert(conformance);
conformances.push_back(conformance);
}
return M->getASTContext().AllocateCopy(conformances);
}

// Create the entry basic block with the function arguments.
void ExistentialSpecializerCloner::cloneArguments(
SmallVectorImpl<SILValue> &entryArgs) {
auto &M = OrigF->getModule();
auto &Ctx = M.getASTContext();

// Create the new entry block.
SILFunction &NewF = getBuilder().getFunction();
Expand Down Expand Up @@ -164,14 +183,10 @@ void ExistentialSpecializerCloner::cloneArguments(
NewArg->setOwnershipKind(ValueOwnershipKind(
NewF, GenericSILType, ArgDesc.Arg->getArgumentConvention()));
// Determine the Conformances.
SmallVector<ProtocolConformanceRef, 1> NewConformances;
auto ContextTy = NewF.mapTypeIntoContext(GenericParam);
auto OpenedArchetype = ContextTy->castTo<ArchetypeType>();
for (auto proto : OpenedArchetype->getConformsTo()) {
NewConformances.push_back(ProtocolConformanceRef(proto));
}
ArrayRef<ProtocolConformanceRef> Conformances =
Ctx.AllocateCopy(NewConformances);
SILType ExistentialType = ArgDesc.Arg->getType().getObjectType();
CanType OpenedType = NewArg->getType().getASTType();
auto Conformances = collectExistentialConformances(
M.getSwiftModule(), OpenedType, ExistentialType.getASTType());
auto ExistentialRepr =
ArgDesc.Arg->getType().getPreferredExistentialRepresentation();
auto &EAD = ExistentialArgDescriptor[ArgDesc.Index];
Expand Down
31 changes: 27 additions & 4 deletions lib/SILOptimizer/Utils/Existential.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,34 @@ void ConcreteExistentialInfo::initializeSubstitutionMap(

// Construct a single-generic-parameter substitution map directly to the
// ConcreteType with this existential's full list of conformances.
//
// NOTE: getOpenedArchetypeSignature() generates the signature for passing an
// opened existential as a generic parameter. No opened archetypes are
// actually involved here--the API is only used as a convenient way to create
// a substitution map. Since opened archetypes have different conformances
// than their corresponding existential, ExistentialConformances needs to be
// filtered when using it with this (phony) generic signature.
CanGenericSignature ExistentialSig =
M->getASTContext().getExistentialSignature(ExistentialType,
M->getSwiftModule());
ExistentialSubs = SubstitutionMap::get(ExistentialSig, {ConcreteType},
ExistentialConformances);
M->getASTContext().getOpenedArchetypeSignature(ExistentialType,
M->getSwiftModule());
ExistentialSubs = SubstitutionMap::get(
ExistentialSig, [&](SubstitutableType *type) { return ConcreteType; },
[&](CanType /*depType*/, Type /*replaceType*/,
ProtocolDecl *proto) -> ProtocolConformanceRef {
// Directly providing ExistentialConformances to the SubstitionMap will
// fail because of the mismatch between opened archetype conformance and
// existential value conformance. Instead, provide a conformance lookup
// function that pulls only the necessary conformances out of
// ExistentialConformances. This assumes that existential conformances
// are a superset of opened archetype conformances.
auto iter =
llvm::find_if(ExistentialConformances,
[&](const ProtocolConformanceRef &conformance) {
return conformance.getRequirement() == proto;
});
assert(iter != ExistentialConformances.end() && "missing conformance");
return *iter;
});
assert(isValid());
}

Expand Down
58 changes: 58 additions & 0 deletions test/SILOptimizer/existential_transform_extras.sil
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,61 @@ sil_witness_table hidden Klass1: P module dealloc {
sil_witness_table hidden Klass2: P module dealloc {
method #P.foo!1: <Self where Self : P> (Self) -> () -> Int32 : nil
}

// -----------------------------------------------------------------------------
// Test composite conformances with superclass constraints where one of
// the protocol constraints is satisfied by the superclass constraint.
//
// <rdar://problem/57025861> "Assertion failed: (conformances.size()
// == numConformanceRequirements)" in ExistentialSpecializer on
protocol Plotable {}

class PlotLayer : Plotable {
init()
}

protocol InView {}

class PlotLayerInView : PlotLayer & InView {
override init()
}

class PlotView {
@_hasStorage @_hasInitialValue var layers: Container<PlotLayer & Plotable & InView> { get set }
public func resolveLayers()
init()
}

struct Container<T> {
@_hasStorage @_hasInitialValue var val: T { get set }
}

// Check that the init_existential instruction was created with all
// three requirements (the generic parameter only has two
// requirements). Relies on assertions during specialization and on
// the SILVerifier to catch other inconsistencies.
//
// CHECK-LABEL: sil shared @$s40testExistentialSpecializeCompositeHelperTf4en_n : $@convention(thin) <τ_0_0 where τ_0_0 : PlotLayer, τ_0_0 : InView> (@owned τ_0_0, @inout Container<PlotLayer & InView & Plotable>) -> () {
// CHECK: bb0(%0 : $τ_0_0, %1 : $*Container<PlotLayer & InView & Plotable>):
// CHECK: init_existential_ref %0 : $τ_0_0 : $τ_0_0, $PlotLayer & InView & Plotable
// CHECK-LABEL: } // end sil function '$s40testExistentialSpecializeCompositeHelperTf4en_n'
sil shared @testExistentialSpecializeCompositeHelper : $@convention(method) (@owned PlotLayer & Plotable & InView, @inout Container<PlotLayer & Plotable & InView>) -> () {
bb0(%0 : $PlotLayer & Plotable & InView, %1 : $*Container<PlotLayer & Plotable & InView>):
%adr = struct_element_addr %1 : $*Container<PlotLayer & Plotable & InView>, #Container.val
store %0 to %adr : $*PlotLayer & Plotable & InView
%v = tuple ()
return %v : $()
}

sil @testExistentialSpecializeComposite : $@convention(method) (@guaranteed PlotView) -> () {
bb0(%0 : $PlotView):
%ref = alloc_ref $PlotLayerInView
strong_retain %ref : $PlotLayerInView
%exis = init_existential_ref %ref : $PlotLayerInView : $PlotLayerInView, $PlotLayer & Plotable & InView
%array = ref_element_addr %0 : $PlotView, #PlotView.layers
%f = function_ref @testExistentialSpecializeCompositeHelper : $@convention(method) (@owned PlotLayer & Plotable & InView, @inout Container<PlotLayer & Plotable & InView>) -> ()
%call = apply %f(%exis, %array) : $@convention(method) (@owned PlotLayer & Plotable & InView, @inout Container<PlotLayer & Plotable & InView>) -> ()
strong_release %ref : $PlotLayerInView
%v = tuple ()
return %v : $()
}