Skip to content

Refactor canonical type ordering #38837

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 4 commits into from
Aug 12, 2021
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
5 changes: 5 additions & 0 deletions include/swift/AST/GenericSignature.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,11 @@ inline bool CanGenericSignature::isActuallyCanonicalOrNull() const {
getPointer()->isCanonical();
}

int compareAssociatedTypes(AssociatedTypeDecl *assocType1,
AssociatedTypeDecl *assocType2);

int compareDependentTypes(Type type1, Type type2);

} // end namespace swift

namespace llvm {
Expand Down
3 changes: 0 additions & 3 deletions include/swift/AST/GenericSignatureBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1677,9 +1677,6 @@ inline bool isErrorResult(GenericSignatureBuilder::ConstraintResult result) {
llvm_unreachable("unhandled result");
}

/// Canonical ordering for dependent types.
int compareDependentTypes(Type type1, Type type2);

template<typename T>
Type GenericSignatureBuilder::Constraint<T>::getSubjectDependentType(
TypeArrayView<GenericTypeParamType> genericParams) const {
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/Requirement.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ class Requirement
/// superclass requirement, 'T : C' cannot be satisfied.
bool canBeSatisfied() const;

/// Linear order on requirements in a generic signature.
int compare(const Requirement &other) const;

SWIFT_DEBUG_DUMP;
void dump(raw_ostream &out) const;
void print(raw_ostream &os, const PrintOptions &opts) const;
Expand Down
102 changes: 102 additions & 0 deletions lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1430,3 +1430,105 @@ bool Requirement::canBeSatisfied() const {

llvm_unreachable("Bad requirement kind");
}

/// Determine the canonical ordering of requirements.
static unsigned getRequirementKindOrder(RequirementKind kind) {
switch (kind) {
case RequirementKind::Conformance: return 2;
case RequirementKind::Superclass: return 0;
case RequirementKind::SameType: return 3;
case RequirementKind::Layout: return 1;
}
llvm_unreachable("unhandled kind");
}

/// Linear order on requirements in a generic signature.
int Requirement::compare(const Requirement &other) const {
int compareLHS =
compareDependentTypes(getFirstType(), other.getFirstType());

if (compareLHS != 0)
return compareLHS;

int compareKind = (getRequirementKindOrder(getKind()) -
getRequirementKindOrder(other.getKind()));

if (compareKind != 0)
return compareKind;

// We should only have multiple conformance requirements.
assert(getKind() == RequirementKind::Conformance);

int compareProtos =
TypeDecl::compare(getProtocolDecl(), other.getProtocolDecl());

assert(compareProtos != 0 && "Duplicate conformance requirement");
return compareProtos;
}

/// Compare two associated types.
int swift::compareAssociatedTypes(AssociatedTypeDecl *assocType1,
AssociatedTypeDecl *assocType2) {
// - by name.
if (int result = assocType1->getName().str().compare(
assocType2->getName().str()))
return result;

// Prefer an associated type with no overrides (i.e., an anchor) to one
// that has overrides.
bool hasOverridden1 = !assocType1->getOverriddenDecls().empty();
bool hasOverridden2 = !assocType2->getOverriddenDecls().empty();
if (hasOverridden1 != hasOverridden2)
return hasOverridden1 ? +1 : -1;

// - by protocol, so t_n_m.`P.T` < t_n_m.`Q.T` (given P < Q)
auto proto1 = assocType1->getProtocol();
auto proto2 = assocType2->getProtocol();
if (int compareProtocols = TypeDecl::compare(proto1, proto2))
return compareProtocols;

// Error case: if we have two associated types with the same name in the
// same protocol, just tie-break based on address.
if (assocType1 != assocType2)
return assocType1 < assocType2 ? -1 : +1;

return 0;
}

/// Canonical ordering for type parameters.
int swift::compareDependentTypes(Type type1, Type type2) {
// Fast-path check for equality.
if (type1->isEqual(type2)) return 0;

// Ordering is as follows:
// - Generic params
auto gp1 = type1->getAs<GenericTypeParamType>();
auto gp2 = type2->getAs<GenericTypeParamType>();
if (gp1 && gp2)
return GenericParamKey(gp1) < GenericParamKey(gp2) ? -1 : +1;

// A generic parameter is always ordered before a nested type.
if (static_cast<bool>(gp1) != static_cast<bool>(gp2))
return gp1 ? -1 : +1;

// - Dependent members
auto depMemTy1 = type1->castTo<DependentMemberType>();
auto depMemTy2 = type2->castTo<DependentMemberType>();

// - by base, so t_0_n.`P.T` < t_1_m.`P.T`
if (int compareBases =
compareDependentTypes(depMemTy1->getBase(), depMemTy2->getBase()))
return compareBases;

// - by name, so t_n_m.`P.T` < t_n_m.`P.U`
if (int compareNames = depMemTy1->getName().str().compare(
depMemTy2->getName().str()))
return compareNames;

auto *assocType1 = depMemTy1->getAssocType();
auto *assocType2 = depMemTy2->getAssocType();
if (int result = compareAssociatedTypes(assocType1, assocType2))
return result;

return 0;
}
105 changes: 2 additions & 103 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1979,35 +1979,6 @@ bool EquivalenceClass::isConformanceSatisfiedBySuperclass(
return false;
}

/// Compare two associated types.
static int compareAssociatedTypes(AssociatedTypeDecl *assocType1,
AssociatedTypeDecl *assocType2) {
// - by name.
if (int result = assocType1->getName().str().compare(
assocType2->getName().str()))
return result;

// Prefer an associated type with no overrides (i.e., an anchor) to one
// that has overrides.
bool hasOverridden1 = !assocType1->getOverriddenDecls().empty();
bool hasOverridden2 = !assocType2->getOverriddenDecls().empty();
if (hasOverridden1 != hasOverridden2)
return hasOverridden1 ? +1 : -1;

// - by protocol, so t_n_m.`P.T` < t_n_m.`Q.T` (given P < Q)
auto proto1 = assocType1->getProtocol();
auto proto2 = assocType2->getProtocol();
if (int compareProtocols = TypeDecl::compare(proto1, proto2))
return compareProtocols;

// Error case: if we have two associated types with the same name in the
// same protocol, just tie-break based on address.
if (assocType1 != assocType2)
return assocType1 < assocType2 ? -1 : +1;

return 0;
}

static void lookupConcreteNestedType(NominalTypeDecl *decl,
Identifier name,
SmallVectorImpl<TypeDecl *> &concreteDecls) {
Expand Down Expand Up @@ -2511,44 +2482,6 @@ auto PotentialArchetype::getRepresentative() const -> PotentialArchetype * {
return result;
}

/// Canonical ordering for dependent types.
int swift::compareDependentTypes(Type type1, Type type2) {
// Fast-path check for equality.
if (type1->isEqual(type2)) return 0;

// Ordering is as follows:
// - Generic params
auto gp1 = type1->getAs<GenericTypeParamType>();
auto gp2 = type2->getAs<GenericTypeParamType>();
if (gp1 && gp2)
return GenericParamKey(gp1) < GenericParamKey(gp2) ? -1 : +1;

// A generic parameter is always ordered before a nested type.
if (static_cast<bool>(gp1) != static_cast<bool>(gp2))
return gp1 ? -1 : +1;

// - Dependent members
auto depMemTy1 = type1->castTo<DependentMemberType>();
auto depMemTy2 = type2->castTo<DependentMemberType>();

// - by base, so t_0_n.`P.T` < t_1_m.`P.T`
if (int compareBases =
compareDependentTypes(depMemTy1->getBase(), depMemTy2->getBase()))
return compareBases;

// - by name, so t_n_m.`P.T` < t_n_m.`P.U`
if (int compareNames = depMemTy1->getName().str().compare(
depMemTy2->getName().str()))
return compareNames;

auto *assocType1 = depMemTy1->getAssocType();
auto *assocType2 = depMemTy2->getAssocType();
if (int result = compareAssociatedTypes(assocType1, assocType2))
return result;

return 0;
}

/// Compare two dependent paths to determine which is better.
static int compareDependentPaths(ArrayRef<AssociatedTypeDecl *> path1,
ArrayRef<AssociatedTypeDecl *> path2) {
Expand Down Expand Up @@ -7915,43 +7848,9 @@ static Optional<Requirement> createRequirement(RequirementKind kind,
}
}

/// Determine the canonical ordering of requirements.
static unsigned getRequirementKindOrder(RequirementKind kind) {
switch (kind) {
case RequirementKind::Conformance: return 2;
case RequirementKind::Superclass: return 0;
case RequirementKind::SameType: return 3;
case RequirementKind::Layout: return 1;
}
llvm_unreachable("unhandled kind");
}

static int compareRequirements(const Requirement *lhsPtr,
const Requirement *rhsPtr) {
auto &lhs = *lhsPtr;
auto &rhs = *rhsPtr;

int compareLHS =
compareDependentTypes(lhs.getFirstType(), rhs.getFirstType());

if (compareLHS != 0)
return compareLHS;

int compareKind = (getRequirementKindOrder(lhs.getKind()) -
getRequirementKindOrder(rhs.getKind()));

if (compareKind != 0)
return compareKind;

// We should only have multiple conformance requirements.
assert(lhs.getKind() == RequirementKind::Conformance);

int compareProtos =
TypeDecl::compare(lhs.getProtocolDecl(), rhs.getProtocolDecl());

assert(compareProtos != 0 && "Duplicate conformance requirement");

return compareProtos;
return lhsPtr->compare(*rhsPtr);
}

void GenericSignatureBuilder::enumerateRequirements(
Expand Down Expand Up @@ -8180,7 +8079,7 @@ static void checkGenericSignature(CanGenericSignature canSig,
"Concrete subject type should not have any other requirements");
}

assert(compareRequirements(&prevReqt, &reqt) < 0 &&
assert(prevReqt.compare(reqt) < 0 &&
"Out-of-order requirements");
}
}
Expand Down
29 changes: 0 additions & 29 deletions lib/AST/RequirementMachine/GenericSignatureQueries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -575,35 +575,6 @@ RequirementMachine::getConformanceAccessPath(Type type,
}
}

/// Compare two associated types.
static int compareAssociatedTypes(AssociatedTypeDecl *assocType1,
AssociatedTypeDecl *assocType2) {
// - by name.
if (int result = assocType1->getName().str().compare(
assocType2->getName().str()))
return result;

// Prefer an associated type with no overrides (i.e., an anchor) to one
// that has overrides.
bool hasOverridden1 = !assocType1->getOverriddenDecls().empty();
bool hasOverridden2 = !assocType2->getOverriddenDecls().empty();
if (hasOverridden1 != hasOverridden2)
return hasOverridden1 ? +1 : -1;

// - by protocol, so t_n_m.`P.T` < t_n_m.`Q.T` (given P < Q)
auto proto1 = assocType1->getProtocol();
auto proto2 = assocType2->getProtocol();
if (int compareProtocols = TypeDecl::compare(proto1, proto2))
return compareProtocols;

// Error case: if we have two associated types with the same name in the
// same protocol, just tie-break based on address.
if (assocType1 != assocType2)
return assocType1 < assocType2 ? -1 : +1;

return 0;
}

static void lookupConcreteNestedType(NominalTypeDecl *decl,
Identifier name,
SmallVectorImpl<TypeDecl *> &concreteDecls) {
Expand Down
Loading