Skip to content

Commit

Permalink
AST: Move ClangImporter's finishSignatureConformances() to a method o…
Browse files Browse the repository at this point in the history
…n NormalProtocolConformance

Also generalize the algorithm slightly to handle the full generality of
non-imported conformances.
  • Loading branch information
slavapestov committed Jul 6, 2019
1 parent e1ce7f5 commit df86891
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 54 deletions.
4 changes: 4 additions & 0 deletions include/swift/AST/ProtocolConformance.h
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,10 @@ class NormalProtocolConformance : public RootProtocolConformance,
/// with the conformance requirements in the requirement signature (in order).
std::function<void(ProtocolConformanceRef)> populateSignatureConformances();

/// Populate the signature conformances without checking if they satisfy
/// requirements. Can only be used with parsed or imported conformances.
void finishSignatureConformances();

/// Determine whether the witness for the given type requirement
/// is the default definition.
bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const {
Expand Down
98 changes: 97 additions & 1 deletion lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,8 @@ void NormalProtocolConformance::setSignatureConformances(
!conformances[idx].getConcrete()->getType()->hasArchetype() &&
"Should have interface types here");
assert(idx < conformances.size());
assert(conformances[idx].getRequirement() ==
assert(conformances[idx].isInvalid() ||
conformances[idx].getRequirement() ==
req.getSecondType()->castTo<ProtocolType>()->getDecl());
++idx;
}
Expand Down Expand Up @@ -942,6 +943,98 @@ NormalProtocolConformance::getAssociatedConformance(Type assocType,
"requested conformance was not a direct requirement of the protocol");
}


/// A stripped-down version of Type::subst that only works on the protocol
/// Self type wrapped in zero or more DependentMemberTypes.
static Type
recursivelySubstituteBaseType(ModuleDecl *module,
NormalProtocolConformance *conformance,
DependentMemberType *depMemTy) {
Type origBase = depMemTy->getBase();

// Recursive case.
if (auto *depBase = origBase->getAs<DependentMemberType>()) {
Type substBase = recursivelySubstituteBaseType(
module, conformance, depBase);
auto result = depMemTy->substBaseType(module, substBase);
if (!result)
return ErrorType::get(substBase);
return result;
}

// Base case. The associated type's protocol should be either the
// conformance protocol or an inherited protocol.
auto *reqProto = depMemTy->getAssocType()->getProtocol();
assert(origBase->isEqual(reqProto->getSelfInterfaceType()));

ProtocolConformance *reqConformance = conformance;

// If we have an inherited protocol just look up the conformance.
if (reqProto != conformance->getProtocol()) {
reqConformance = module->lookupConformance(
conformance->getType(), reqProto)->getConcrete();
}

return reqConformance->getTypeWitness(depMemTy->getAssocType(),
/*resolver=*/nullptr);
}

/// Collect conformances for the requirement signature.
void NormalProtocolConformance::finishSignatureConformances() {
if (!SignatureConformances.empty())
return;

auto *proto = getProtocol();
auto reqSig = proto->getRequirementSignature();
if (reqSig.empty())
return;

SmallVector<ProtocolConformanceRef, 4> reqConformances;
for (const auto &req : reqSig) {
if (req.getKind() != RequirementKind::Conformance)
continue;

ModuleDecl *module = getDeclContext()->getParentModule();

Type substTy;
auto origTy = req.getFirstType();
if (origTy->isEqual(proto->getSelfInterfaceType())) {
substTy = getType();
} else {
auto *depMemTy = origTy->castTo<DependentMemberType>();
substTy = recursivelySubstituteBaseType(module, this, depMemTy);
}
auto reqProto = req.getSecondType()->castTo<ProtocolType>()->getDecl();

// Looking up a conformance for a contextual type and mapping the
// conformance context produces a more accurate result than looking
// up a conformance from an interface type.
//
// This can happen if the conformance has an associated conformance
// depending on an associated type that is made concrete in a
// refining protocol.
//
// That is, the conformance of an interface type G<T> : P really
// depends on the generic signature of the current context, because
// performing the lookup in a "more" constrained extension than the
// one where the conformance was defined must produce concrete
// conformances.
//
// FIXME: Eliminate this, perhaps by adding a variant of
// lookupConformance() taking a generic signature.
if (substTy->hasTypeParameter())
substTy = getDeclContext()->mapTypeIntoContext(substTy);

auto reqConformance = module->lookupConformance(substTy, reqProto);
if (!reqConformance)
reqConformance = ProtocolConformanceRef::forInvalid();

reqConformance = reqConformance->mapConformanceOutOfContext();
reqConformances.push_back(*reqConformance);
}
setSignatureConformances(reqConformances);
}

Witness RootProtocolConformance::getWitness(ValueDecl *requirement,
LazyResolver *resolver) const {
ROOT_CONFORMANCE_SUBCLASS_DISPATCH(getWitness, (requirement, resolver))
Expand Down Expand Up @@ -1068,6 +1161,9 @@ SpecializedProtocolConformance::getTypeWitnessAndDecl(
AssociatedTypeDecl *assocType,
LazyResolver *resolver,
SubstOptions options) const {
assert(getProtocol() == cast<ProtocolDecl>(assocType->getDeclContext()) &&
"associated type in wrong protocol");

// If we've already created this type witness, return it.
auto known = TypeWitnesses.find(assocType);
if (known != TypeWitnesses.end()) {
Expand Down
54 changes: 1 addition & 53 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7723,58 +7723,6 @@ static void finishTypeWitnesses(
}
}

/// A stripped-down version of Type::subst that only works on non-generic
/// associated types.
///
/// This is used to finish a conformance for a concrete imported type that may
/// rely on default associated types defined in protocol extensions...without
/// having to do all the work of gathering conformances from scratch.
static Type
recursivelySubstituteBaseType(const NormalProtocolConformance *conformance,
DependentMemberType *depMemTy) {
Type origBase = depMemTy->getBase();
if (auto *depBase = origBase->getAs<DependentMemberType>()) {
Type substBase = recursivelySubstituteBaseType(conformance, depBase);
ModuleDecl *module = conformance->getDeclContext()->getParentModule();
return depMemTy->substBaseType(module, substBase);
}

const ProtocolDecl *proto = conformance->getProtocol();
assert(origBase->isEqual(proto->getSelfInterfaceType()));
(void)proto;
return conformance->getTypeWitness(depMemTy->getAssocType(),
/*resolver=*/nullptr);
}

/// Collect conformances for the requirement signature.
static void finishSignatureConformances(
NormalProtocolConformance *conformance) {
auto *proto = conformance->getProtocol();

SmallVector<ProtocolConformanceRef, 4> reqConformances;
for (const auto &req : proto->getRequirementSignature()) {
if (req.getKind() != RequirementKind::Conformance)
continue;

Type substTy;
auto origTy = req.getFirstType();
if (origTy->isEqual(proto->getSelfInterfaceType())) {
substTy = conformance->getType();
} else {
auto *depMemTy = origTy->castTo<DependentMemberType>();
substTy = recursivelySubstituteBaseType(conformance, depMemTy);
}
auto reqProto = req.getSecondType()->castTo<ProtocolType>()->getDecl();

ModuleDecl *M = conformance->getDeclContext()->getParentModule();
auto reqConformance = M->lookupConformance(substTy, reqProto);
assert(reqConformance && reqConformance->isConcrete() &&
"required conformance not found");
reqConformances.push_back(*reqConformance);
}
conformance->setSignatureConformances(reqConformances);
}

/// Create witnesses for requirements not already met.
static void finishMissingOptionalWitnesses(
NormalProtocolConformance *conformance) {
Expand Down Expand Up @@ -7820,7 +7768,7 @@ void ClangImporter::Implementation::finishNormalConformance(
conformance);

finishTypeWitnesses(conformance);
finishSignatureConformances(conformance);
conformance->finishSignatureConformances();

// Imported conformances to @objc protocols also require additional
// initialization to complete the requirement to witness mapping.
Expand Down

0 comments on commit df86891

Please sign in to comment.