mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
744 lines
26 KiB
C++
744 lines
26 KiB
C++
//===--- ConcreteContraction.cpp - Preprocessing concrete conformances ----===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2022 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// The concrete contraction pass runs after requirement desugaring and before
|
|
// rewrite rules are built from desugared requirements when constructing a
|
|
// rewrite system for a user-written generic signature.
|
|
//
|
|
// This is an imperfect hack to deal with two issues:
|
|
//
|
|
// a) When a generic parameter is subject to both a conformance requirement and
|
|
// a concrete type requirement (or a superclass requirement where the
|
|
// superclass also conforms to the protocol), the conformance requirement
|
|
// becomes redundant during property map construction.
|
|
//
|
|
// However, unlike other kinds of requirements, dropping a conformance
|
|
// requirement can change canonical types in the rewrite system, and in
|
|
// particular, it can change the canonical types of other minimal
|
|
// requirements, if the protocol in the conformance requirement has
|
|
// associated types.
|
|
//
|
|
// Consider this example:
|
|
//
|
|
// protocol P {
|
|
// associatedtype T
|
|
// }
|
|
//
|
|
// protocol Q {}
|
|
//
|
|
// struct S<T> : P {}
|
|
//
|
|
// struct Holder<A : P, B : P> where A.T : Q {}
|
|
//
|
|
// extension Holder where A == S<B.T> {}
|
|
//
|
|
// The signature of the extension is built from these requirements:
|
|
//
|
|
// - A : P
|
|
// - B : P
|
|
// - A.T : Q
|
|
// - A == S<B.T>
|
|
//
|
|
// In this rewrite system, the canonical type of 'B.T' is 'A.T', so the
|
|
// requirements canonicalize as follows:
|
|
//
|
|
// - A : P
|
|
// - B : P
|
|
// - A.T : Q
|
|
// - A == S<A.T>
|
|
//
|
|
// Also, the first requirement 'A : P' is redundant in this rewrite system,
|
|
// because 'A == S<B.T>' and S conforms to P.
|
|
//
|
|
// However, simply dropping 'A : P' is not enough. If the user instead
|
|
// directly wrote a signature with the original requirements omitting
|
|
// 'A : P', we would have:
|
|
//
|
|
// - B : P
|
|
// - A == S<B.T>
|
|
// - B.T : Q
|
|
//
|
|
// Indeed, computing canonical types from the first rewrite system produces
|
|
// different results, because 'B.T' canonicalizes to 'A.T' and not 'B.T'.
|
|
//
|
|
// b) The GenericSignatureBuilder permitted references to "fully concrete"
|
|
// member types of a dependent type that were not associated types from
|
|
// conformance requirements.
|
|
//
|
|
// That is, the following was permitted:
|
|
//
|
|
// class C {
|
|
// typealias A = Int
|
|
// }
|
|
//
|
|
// <T, U where T : C, U == T.A>
|
|
//
|
|
// The requirement 'U == T.A' was resolved to 'U == Int', despite 'T' not
|
|
// declaring any protocol conformance requirements with an associated type
|
|
// named 'A' (or any conformance requirements at all).
|
|
//
|
|
// The GenericSignatureBuilder dealt with a) using a "rebuilding" pass that
|
|
// build a new generic signature after dropping redundant conformance
|
|
// requirements, feeding the original requirements back in. The behavior b)
|
|
// was a consequence of how requirement resolution was implemented, by calling
|
|
// into name lookup.
|
|
//
|
|
// The Requirement Machine's approach to both a) and b) doesn't handle as many
|
|
// cases, but is much simpler and hopefully more robust. Before building the
|
|
// rewrite system, we pre-process the requirements and identify generic
|
|
// parameters subject to a superclass or concrete type requirement. Then, we
|
|
// substitute this generic parameter for the superclass or concrete type,
|
|
// respectively, in all other requirements.
|
|
//
|
|
// In the above example, it produces the following list of requirements:
|
|
//
|
|
// - S<B.T> : P
|
|
// - B : P
|
|
// - S<B.T>.T : Q
|
|
// - A == S<B.T>
|
|
//
|
|
// The requirements are fed back into desugarRequirements(), and we get:
|
|
//
|
|
// - B : P
|
|
// - B.T : Q
|
|
// - A == S<B.T>
|
|
//
|
|
// This also handles b), where the original requirements are:
|
|
//
|
|
// - T : C
|
|
// - U == T.A
|
|
//
|
|
// and after concrete contraction, we get
|
|
//
|
|
// - T : C
|
|
// - U == Int
|
|
//
|
|
// Since this is all a heuristic that is applied before the rewrite system is
|
|
// built, it doesn't handle cases where a nested type of a generic parameter is
|
|
// subject to both a concrete type and conformance requirement, nor does it
|
|
// handle more complex cases where the redundant conformance is only discovered
|
|
// via an intermediate same-type requirement, such as the following:
|
|
//
|
|
// <T, U, V where T == S<V>, T == U, U : P>
|
|
//
|
|
// If concrete contraction fails, the minimized generic signature will fail
|
|
// verification if it still contains incorrectly-canonicalized types.
|
|
//
|
|
// We might need a more general solution eventually, but for now this is good
|
|
// enough to handle the cases where this arises in practice.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/AST/ConformanceLookup.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/Requirement.h"
|
|
#include "swift/AST/Type.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "NameLookup.h"
|
|
#include "RequirementLowering.h"
|
|
|
|
using namespace swift;
|
|
using namespace rewriting;
|
|
|
|
/// Strip associated types from types used as keys to erase differences between
|
|
/// resolved types coming from the parent generic signature and unresolved types
|
|
/// coming from user-written requirements.
|
|
static Type stripBoundDependentMemberTypes(Type t) {
|
|
if (auto *depMemTy = t->getAs<DependentMemberType>()) {
|
|
return DependentMemberType::get(
|
|
stripBoundDependentMemberTypes(depMemTy->getBase()),
|
|
depMemTy->getName());
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
/// Returns true if \p lhs appears as the base of a member type in \p rhs.
|
|
static bool typeOccursIn(Type lhs, Type rhs) {
|
|
return rhs.findIf([lhs](Type t) -> bool {
|
|
if (auto *memberType = t->getAs<DependentMemberType>())
|
|
return memberType->getBase()->isEqual(lhs);
|
|
return false;
|
|
});
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// Utility class to store some shared state.
|
|
class ConcreteContraction {
|
|
bool Debug;
|
|
|
|
llvm::SmallDenseMap<CanType, llvm::SmallDenseSet<Type, 1>> ConcreteTypes;
|
|
llvm::SmallDenseMap<CanType, llvm::SmallDenseSet<Type, 1>> Superclasses;
|
|
llvm::SmallDenseMap<CanType, llvm::SmallVector<ProtocolDecl *, 1>> Conformances;
|
|
|
|
enum Position {
|
|
/// Base type of some other type appearing in a requirement.
|
|
BaseType,
|
|
|
|
/// Subject type of a conformance requirement.
|
|
ConformanceRequirement,
|
|
|
|
/// Subject type of a superclass requirement.
|
|
SuperclassRequirement,
|
|
|
|
/// Subject type of a same-type requirement.
|
|
SameTypeRequirement,
|
|
|
|
/// Some other position.
|
|
Other
|
|
};
|
|
|
|
std::optional<Type> substTypeParameterRec(Type type, Position position) const;
|
|
Type substTypeParameter(Type type, Position position) const;
|
|
Type substType(Type type) const;
|
|
Requirement substRequirement(const Requirement &req) const;
|
|
|
|
bool preserveSameTypeRequirement(const Requirement &req) const;
|
|
|
|
bool hasResolvedMemberTypeOfInterestingParameter(Type t) const;
|
|
|
|
public:
|
|
ConcreteContraction(bool debug) : Debug(debug) {}
|
|
|
|
bool performConcreteContraction(
|
|
ArrayRef<StructuralRequirement> requirements,
|
|
SmallVectorImpl<StructuralRequirement> &result,
|
|
SmallVectorImpl<RequirementError> &errors);
|
|
};
|
|
|
|
} // end namespace
|
|
|
|
/// A re-implementation of Type::subst() that also handles unresolved
|
|
/// DependentMemberTypes by performing name lookup into the base type.
|
|
///
|
|
/// When substituting a superclass requirement, we have to handle the
|
|
/// case where the superclass might not conform to the protocol in
|
|
/// question. For example, you can have a generic signature like this
|
|
///
|
|
/// <T where T : Sequence, T : SomeClass, T.Element == Int>
|
|
///
|
|
/// If SomeClass does not conform to Sequence, the type T is understood
|
|
/// to be some subclass of SomeClass which does conform to Sequence;
|
|
/// this is perfectly valid, and we cannot substitute the 'T.Element'
|
|
/// requirement. In this case, this method returns None.
|
|
std::optional<Type>
|
|
ConcreteContraction::substTypeParameterRec(Type type, Position position) const {
|
|
|
|
// If we have a superclass (T : C) or same-type requirement (T == C),
|
|
// don't substitute T, since then we end up with 'C == C' or 'C : C',
|
|
// losing the requirement.
|
|
if (position == Position::BaseType ||
|
|
position == Position::ConformanceRequirement) {
|
|
auto key = stripBoundDependentMemberTypes(type)->getCanonicalType();
|
|
|
|
Type concreteType;
|
|
{
|
|
auto found = ConcreteTypes.find(key);
|
|
if (found != ConcreteTypes.end() && found->second.size() == 1)
|
|
concreteType = *found->second.begin();
|
|
}
|
|
|
|
Type superclass;
|
|
{
|
|
auto found = Superclasses.find(key);
|
|
if (found != Superclasses.end() && found->second.size() == 1)
|
|
superclass = *found->second.begin();
|
|
}
|
|
|
|
// If we have both, prefer the concrete type requirement since it is more
|
|
// specific.
|
|
if (!concreteType && superclass)
|
|
concreteType = superclass;
|
|
|
|
if (concreteType) {
|
|
return concreteType;
|
|
}
|
|
}
|
|
|
|
if (auto *memberType = type->getAs<DependentMemberType>()) {
|
|
auto baseType = memberType->getBase();
|
|
auto substBaseType = substTypeParameterRec(baseType, Position::BaseType);
|
|
if (!substBaseType)
|
|
return std::nullopt;
|
|
|
|
// A resolved DependentMemberType stores an associated type declaration.
|
|
//
|
|
// Handle this by looking up the corresponding type witness in the base
|
|
// type's conformance to the associated type's protocol.
|
|
if (auto *assocType = memberType->getAssocType()) {
|
|
auto *proto = assocType->getProtocol();
|
|
|
|
// The 'Sendable' protocol does not declare any associated types, so the
|
|
// 'allowMissing' value here is actually irrelevant.
|
|
auto conformance = lookupConformance(*substBaseType, proto,
|
|
/*allowMissing=*/false);
|
|
|
|
if (proto->isSpecificProtocol(KnownProtocolKind::Sendable) &&
|
|
conformance.hasUnavailableConformance()) {
|
|
conformance = ProtocolConformanceRef::forInvalid();
|
|
}
|
|
|
|
// The base type doesn't conform, in which case the requirement remains
|
|
// unsubstituted.
|
|
if (!conformance) {
|
|
if (Debug) {
|
|
llvm::dbgs() << "@@@ " << substBaseType << " does not conform to "
|
|
<< proto->getName() << "\n";
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
return conformance.getTypeWitness(assocType);
|
|
}
|
|
|
|
// An unresolved DependentMemberType stores an identifier. Handle this
|
|
// by performing a name lookup into the base type.
|
|
SmallVector<TypeDecl *> concreteDecls;
|
|
lookupConcreteNestedType(*substBaseType, memberType->getName(), concreteDecls);
|
|
|
|
auto *typeDecl = findBestConcreteNestedType(concreteDecls);
|
|
if (typeDecl == nullptr) {
|
|
// The base type doesn't contain a member type with this name, in which
|
|
// case the requirement remains unsubstituted.
|
|
if (Debug) {
|
|
llvm::dbgs() << "@@@ Lookup of " << memberType->getName() << " failed on "
|
|
<< *substBaseType << "\n";
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Substitute the base type into the member type.
|
|
auto *dc = typeDecl->getDeclContext();
|
|
auto subMap = (*substBaseType)->getContextSubstitutionMap(dc);
|
|
return typeDecl->getDeclaredInterfaceType().subst(subMap);
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
/// Replace the generic parameter at the root of \p type, which must be a
|
|
/// type parameter, with the superclass or concrete type requirement that
|
|
/// the generic parameter is subject to.
|
|
///
|
|
/// Note that if the generic parameter has a superclass conformance, we
|
|
/// only substitute if it's the root of a member type; the generic parameter
|
|
/// itself does not become concrete when it's superclass-constrained, unless
|
|
/// it is the subject of a conformance requirement.
|
|
Type ConcreteContraction::substTypeParameter(
|
|
Type type, Position position) const {
|
|
ASSERT(type->isTypeParameter());
|
|
|
|
auto result = substTypeParameterRec(type, position);
|
|
if (!result)
|
|
return type;
|
|
|
|
return *result;
|
|
}
|
|
|
|
/// Substitute all type parameters occurring in structural positions of \p type.
|
|
Type ConcreteContraction::substType(Type type) const {
|
|
return type.transformRec([&](Type type) -> std::optional<Type> {
|
|
if (!type->isTypeParameter())
|
|
return std::nullopt;
|
|
|
|
return substTypeParameter(type, Position::Other);
|
|
});
|
|
}
|
|
|
|
/// Substitute all type parameters occurring in the given requirement.
|
|
Requirement
|
|
ConcreteContraction::substRequirement(const Requirement &req) const {
|
|
auto firstType = req.getFirstType();
|
|
|
|
switch (req.getKind()) {
|
|
case RequirementKind::SameShape: {
|
|
auto substFirstType = substType(firstType);
|
|
auto substSecondType = substType(req.getSecondType());
|
|
|
|
return Requirement(req.getKind(), substFirstType, substSecondType);
|
|
}
|
|
|
|
case RequirementKind::Superclass:
|
|
case RequirementKind::SameType: {
|
|
auto position = (req.getKind() == RequirementKind::Superclass
|
|
? Position::SuperclassRequirement
|
|
: Position::SameTypeRequirement);
|
|
auto substFirstType = substTypeParameter(firstType, position);
|
|
|
|
auto secondType = req.getSecondType();
|
|
auto substSecondType = substType(secondType);
|
|
|
|
return Requirement(req.getKind(),
|
|
substFirstType,
|
|
substSecondType);
|
|
}
|
|
|
|
case RequirementKind::Conformance: {
|
|
auto substFirstType = substTypeParameter(
|
|
firstType, Position::ConformanceRequirement);
|
|
|
|
auto *proto = req.getProtocolDecl();
|
|
|
|
// For conformance to 'Sendable', allow synthesis of a missing conformance
|
|
// if the generic parameter is concrete, that is, if we're looking at a
|
|
// signature of the form 'T == Foo, T : Sendable'.
|
|
//
|
|
// Otherwise, we have a superclass requirement, like 'T : C, T : Sendable';
|
|
// don't synthesize the conformance in this case since dropping
|
|
// 'T : Sendable' would be incorrect; we want to ensure that we only admit
|
|
// subclasses of 'C' which are 'Sendable'.
|
|
bool allowMissing = false;
|
|
auto key = stripBoundDependentMemberTypes(firstType)->getCanonicalType();
|
|
if (ConcreteTypes.count(key) > 0)
|
|
allowMissing = true;
|
|
|
|
if (!substFirstType->isTypeParameter()) {
|
|
auto conformance = lookupConformance(substFirstType, proto,
|
|
allowMissing);
|
|
|
|
if (!allowMissing &&
|
|
proto->isSpecificProtocol(KnownProtocolKind::Sendable) &&
|
|
conformance.hasUnavailableConformance()) {
|
|
conformance = ProtocolConformanceRef::forInvalid();
|
|
}
|
|
|
|
if (!conformance) {
|
|
// Handle the case of <T where T : P, T : C> where C is a class and
|
|
// C does not conform to P and only substitute the parent type of T
|
|
// by pretending we have a same-type requirement here.
|
|
substFirstType = substTypeParameter(
|
|
firstType, Position::SameTypeRequirement);
|
|
}
|
|
}
|
|
|
|
// Otherwise, replace the generic parameter in the conformance
|
|
// requirement with the concrete type. It will desugar to nothing
|
|
// (if the conformance is conditional) or to zero or more
|
|
// conditional requirements needed to satisfy the conformance.
|
|
return Requirement(req.getKind(),
|
|
substFirstType,
|
|
req.getSecondType());
|
|
}
|
|
|
|
case RequirementKind::Layout: {
|
|
auto substFirstType = substTypeParameter(firstType, Position::Other);
|
|
if (!substFirstType->isTypeParameter() &&
|
|
!substFirstType->satisfiesClassConstraint() &&
|
|
req.getLayoutConstraint()->isClass()) {
|
|
// If the concrete type doesn't satisfy the layout constraint, produce
|
|
// a better diagnostic and only substitute the parent type by pretending
|
|
// we have a same-type requirement here.
|
|
substFirstType = substTypeParameter(
|
|
firstType, Position::SameTypeRequirement);
|
|
}
|
|
|
|
return Requirement(req.getKind(),
|
|
substFirstType,
|
|
req.getLayoutConstraint());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ConcreteContraction::
|
|
hasResolvedMemberTypeOfInterestingParameter(Type type) const {
|
|
return type.findIf([&](Type t) -> bool {
|
|
if (auto *memberTy = t->getAs<DependentMemberType>()) {
|
|
if (memberTy->getAssocType() == nullptr)
|
|
return false;
|
|
|
|
auto key = stripBoundDependentMemberTypes(memberTy->getBase())
|
|
->getCanonicalType();
|
|
Type concreteType;
|
|
{
|
|
auto found = ConcreteTypes.find(key);
|
|
if (found != ConcreteTypes.end() && found->second.size() == 1)
|
|
return true;
|
|
}
|
|
|
|
Type superclass;
|
|
{
|
|
auto found = Superclasses.find(key);
|
|
if (found != Superclasses.end() && found->second.size() == 1)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
});
|
|
}
|
|
|
|
/// Another silly GenericSignatureBuilder compatibility hack.
|
|
///
|
|
/// Consider this code:
|
|
///
|
|
/// class C<T> {
|
|
/// typealias A = T
|
|
/// }
|
|
///
|
|
/// protocol P {
|
|
/// associatedtype A
|
|
/// }
|
|
///
|
|
/// func f<X, T>(_: X, _: T) where X : P, X : C<T>, X.A == T {}
|
|
///
|
|
/// The GenericSignatureBuilder would introduce an equivalence between
|
|
/// typealias A in class C and associatedtype A in protocol P, so the
|
|
/// requirement 'X.A == T' would effectively constrain _both_.
|
|
///
|
|
/// Simulate this by keeping both the original and substituted same-type
|
|
/// requirement in a narrow case.
|
|
bool ConcreteContraction::preserveSameTypeRequirement(
|
|
const Requirement &req) const {
|
|
if (req.getKind() != RequirementKind::SameType)
|
|
return false;
|
|
|
|
// One of the parent types of this type parameter should be subject
|
|
// to a superclass requirement.
|
|
auto type = stripBoundDependentMemberTypes(req.getFirstType())
|
|
->getCanonicalType();
|
|
while (true) {
|
|
if (Superclasses.find(type) != Superclasses.end())
|
|
break;
|
|
|
|
if (auto memberType = dyn_cast<DependentMemberType>(type)) {
|
|
type = memberType.getBase();
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (hasResolvedMemberTypeOfInterestingParameter(req.getFirstType()) ||
|
|
hasResolvedMemberTypeOfInterestingParameter(req.getSecondType()))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Substitute all occurrences of generic parameters subject to superclass
|
|
/// or concrete type requirements with their corresponding superclass or
|
|
/// concrete type.
|
|
///
|
|
/// If this returns false, \p result should be ignored and the requirements
|
|
/// remain unchanged. If this returns true, \p result should replace the
|
|
/// original \p requirements.
|
|
bool ConcreteContraction::performConcreteContraction(
|
|
ArrayRef<StructuralRequirement> requirements,
|
|
SmallVectorImpl<StructuralRequirement> &result,
|
|
SmallVectorImpl<RequirementError> &errors) {
|
|
|
|
// Phase 1 - collect concrete type and superclass requirements where the
|
|
// subject type is a generic parameter.
|
|
for (auto req : requirements) {
|
|
auto subjectType = req.req.getFirstType();
|
|
ASSERT(subjectType->isTypeParameter() &&
|
|
"Forgot to call desugarRequirement()");
|
|
|
|
auto kind = req.req.getKind();
|
|
switch (kind) {
|
|
case RequirementKind::SameShape:
|
|
ASSERT(req.req.getSecondType()->isTypeParameter());
|
|
continue;
|
|
|
|
case RequirementKind::SameType: {
|
|
auto constraintType = req.req.getSecondType();
|
|
|
|
// Same-type requirements between type parameters are not interesting
|
|
// to this pass.
|
|
if (constraintType->isTypeParameter())
|
|
break;
|
|
|
|
subjectType = stripBoundDependentMemberTypes(subjectType);
|
|
if (typeOccursIn(subjectType,
|
|
stripBoundDependentMemberTypes(constraintType))) {
|
|
if (Debug) {
|
|
llvm::dbgs() << "@ Subject type of same-type requirement "
|
|
<< subjectType << " == " << constraintType << " "
|
|
<< "occurs in the constraint type, skipping\n";
|
|
}
|
|
break;
|
|
}
|
|
ConcreteTypes[subjectType->getCanonicalType()].insert(constraintType);
|
|
break;
|
|
}
|
|
case RequirementKind::Superclass: {
|
|
auto constraintType = req.req.getSecondType();
|
|
ASSERT(!constraintType->isTypeParameter() &&
|
|
"Forgot to call desugarRequirement()");
|
|
|
|
subjectType = stripBoundDependentMemberTypes(subjectType);
|
|
if (typeOccursIn(subjectType,
|
|
stripBoundDependentMemberTypes(constraintType))) {
|
|
if (Debug) {
|
|
llvm::dbgs() << "@ Subject type of superclass requirement "
|
|
<< subjectType << " : " << constraintType << " "
|
|
<< "occurs in the constraint type, skipping\n";
|
|
}
|
|
break;
|
|
}
|
|
Superclasses[subjectType->getCanonicalType()].insert(constraintType);
|
|
break;
|
|
}
|
|
case RequirementKind::Conformance: {
|
|
auto *protoDecl = req.req.getProtocolDecl();
|
|
subjectType = stripBoundDependentMemberTypes(subjectType);
|
|
Conformances[subjectType->getCanonicalType()].push_back(protoDecl);
|
|
|
|
break;
|
|
}
|
|
case RequirementKind::Layout:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Block concrete contraction if a generic parameter conforms to a protocol P
|
|
// which has a superclass bound C which again conforms to P. This is a really
|
|
// silly edge case, but we go to great pains to produce the same minimized
|
|
// signature as the GenericSignatureBuilder in this case, <T : P>, and not the
|
|
// more logical <T : C>.
|
|
for (const auto &pair : Conformances) {
|
|
auto subjectType = pair.first;
|
|
auto found = Superclasses.find(subjectType);
|
|
if (found == Superclasses.end() || found->second.size() != 1)
|
|
continue;
|
|
|
|
auto superclassTy = *found->second.begin();
|
|
|
|
for (auto *proto : pair.second) {
|
|
if (lookupConformance(superclassTy, proto)) {
|
|
auto genericSig = proto->getGenericSignature();
|
|
// FIXME: If we end up here while building the requirement
|
|
// signature of `proto`, we will hit a request cycle.
|
|
if (auto otherSuperclassTy = genericSig->getSuperclassBound(
|
|
proto->getSelfInterfaceType())) {
|
|
if (Debug) {
|
|
llvm::dbgs() << "@ Subject type of superclass requirement "
|
|
<< subjectType << " : " << superclassTy
|
|
<< " conforms to "<< proto->getName()
|
|
<< " which has a superclass bound "
|
|
<< otherSuperclassTy << "\n";
|
|
}
|
|
|
|
if (superclassTy->isEqual(otherSuperclassTy)) {
|
|
Superclasses.erase(subjectType);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there's nothing to do just return.
|
|
if (ConcreteTypes.empty() && Superclasses.empty())
|
|
return false;
|
|
|
|
if (Debug) {
|
|
llvm::dbgs() << "@ Concrete types: @\n";
|
|
for (auto pair : ConcreteTypes) {
|
|
llvm::dbgs() << pair.first;
|
|
if (pair.second.size() == 1) {
|
|
llvm::dbgs() << " == " << *pair.second.begin() << "\n";
|
|
} else {
|
|
llvm::dbgs() << " has duplicate concrete type requirements\n";
|
|
}
|
|
}
|
|
|
|
llvm::dbgs() << "@ Superclasses: @\n";
|
|
for (auto pair : Superclasses) {
|
|
llvm::dbgs() << pair.first;
|
|
if (pair.second.size() == 1) {
|
|
llvm::dbgs() << " : " << *pair.second.begin() << "\n";
|
|
} else {
|
|
llvm::dbgs() << " has duplicate superclass requirements\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Phase 2: Replace each concretely-conforming generic parameter with its
|
|
// concrete type.
|
|
for (auto req : requirements) {
|
|
if (Debug) {
|
|
llvm::dbgs() << "@ Original requirement: ";
|
|
req.req.dump(llvm::dbgs());
|
|
llvm::dbgs() << "\n";
|
|
}
|
|
|
|
// Substitute the requirement.
|
|
auto substReq = substRequirement(req.req);
|
|
|
|
if (Debug) {
|
|
llvm::dbgs() << "@ Substituted requirement: ";
|
|
substReq.dump(llvm::dbgs());
|
|
llvm::dbgs() << "\n";
|
|
}
|
|
|
|
// Otherwise, desugar the requirement again, since we might now have a
|
|
// requirement where the left hand side is not a type parameter.
|
|
SmallVector<Requirement, 4> reqs;
|
|
SmallVector<InverseRequirement, 4> ignoreInverses;
|
|
desugarRequirement(substReq, req.loc, reqs, ignoreInverses, errors);
|
|
|
|
for (auto desugaredReq : reqs) {
|
|
if (Debug) {
|
|
llvm::dbgs() << "@@ Desugared requirement: ";
|
|
desugaredReq.dump(llvm::dbgs());
|
|
llvm::dbgs() << "\n";
|
|
}
|
|
result.push_back({desugaredReq, req.loc});
|
|
}
|
|
|
|
if (preserveSameTypeRequirement(req.req) &&
|
|
(!req.req.getFirstType()->isEqual(substReq.getFirstType()) ||
|
|
!req.req.getSecondType()->isEqual(substReq.getSecondType()))) {
|
|
if (Debug) {
|
|
llvm::dbgs() << "@ Preserving original requirement: ";
|
|
req.req.dump(llvm::dbgs());
|
|
llvm::dbgs() << "\n";
|
|
}
|
|
|
|
// Make the duplicated requirement 'inferred' so that we don't diagnose
|
|
// it as redundant.
|
|
result.push_back({req.req, SourceLoc()});
|
|
}
|
|
}
|
|
|
|
if (Debug) {
|
|
llvm::dbgs() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";
|
|
llvm::dbgs() << "@ Concrete contraction succeeded @\n";
|
|
llvm::dbgs() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Substitute all occurrences of generic parameters subject to superclass
|
|
/// or concrete type requirements with their corresponding superclass or
|
|
/// concrete type.
|
|
///
|
|
/// If this returns false, \p result should be ignored and the requirements
|
|
/// remain unchanged. If this returns true, \p result should replace the
|
|
/// original \p requirements.
|
|
bool swift::rewriting::performConcreteContraction(
|
|
ArrayRef<StructuralRequirement> requirements,
|
|
SmallVectorImpl<StructuralRequirement> &result,
|
|
SmallVectorImpl<RequirementError> &errors,
|
|
bool debug) {
|
|
ConcreteContraction concreteContraction(debug);
|
|
return concreteContraction.performConcreteContraction(
|
|
requirements, result, errors);
|
|
}
|