Skip to content

Sema: Properly handle inference for abstract fixed type witnesses #27425

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

Closed
Closed
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/GenericSignature.h
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,9 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final
bool isConcreteType(Type type);

/// Return the concrete type that the given dependent type is constrained to,
/// or the null Type if it is not the subject of a concrete same-type
/// constraint.
Type getConcreteType(Type type);
/// the null Type if it is not the subject of a concrete same-type
/// constraint, or None if the equivalence class failed to resolve.
Optional<Type> maybeGetConcreteType(Type type);

/// Return the layout constraint that the given dependent type is constrained
/// to, or the null LayoutConstraint if it is not the subject of layout
Expand Down
9 changes: 3 additions & 6 deletions lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,21 +467,18 @@ bool GenericSignatureImpl::conformsToProtocol(Type type, ProtocolDecl *proto) {

/// Determine whether the given dependent type is equal to a concrete type.
bool GenericSignatureImpl::isConcreteType(Type type) {
return bool(getConcreteType(type));
return bool(maybeGetConcreteType(type).getValueOr(Type()));
}

/// Return the concrete type that the given dependent type is constrained to,
/// or the null Type if it is not the subject of a concrete same-type
/// constraint.
Type GenericSignatureImpl::getConcreteType(Type type) {
Optional<Type> GenericSignatureImpl::maybeGetConcreteType(Type type) {
if (!type->isTypeParameter()) return Type();

auto &builder = *getGenericSignatureBuilder();
auto equivClass =
builder.resolveEquivalenceClass(
type,
ArchetypeResolutionKind::CompleteWellFormed);
if (!equivClass) return Type();
if (!equivClass) return None;

return equivClass->concreteType;
}
Expand Down
12 changes: 12 additions & 0 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2165,6 +2165,18 @@ void EquivalenceClass::dump(llvm::raw_ostream &out,
}, [&] {
out << ", ";
});
if (!concreteTypeConstraints.empty()) {
out << "\nConcrete-type constraints:";
interleave(concreteTypeConstraints, [&](const Constraint<Type> &c) {
out << "\n " << c.getSubjectDependentType({ })
<< " == " << c.value;

if (c.source->isDerivedRequirement())
out << " [derived]";
}, [&] {
out << ", ";
});
}
if (concreteType)
out << "\nConcrete type: " << concreteType.getString();
if (superclass)
Expand Down
3 changes: 2 additions & 1 deletion lib/AST/SubstitutionMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,8 @@ Type SubstitutionMap::lookupSubstitution(CanSubstitutableType type) const {

// The generic parameter may have been made concrete by the generic signature,
// substitute into the concrete type.
if (auto concreteType = genericSig->getConcreteType(genericParam)){
if (auto concreteType = genericSig
->maybeGetConcreteType(genericParam).getValueOr(Type())) {
// Set the replacement type to an error, to block infinite recursion.
replacementType = ErrorType::get(concreteType);

Expand Down
9 changes: 5 additions & 4 deletions lib/SIL/TypeLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,9 @@ namespace {
if (auto genericSig = getGenericSignature()) {
if (genericSig->requiresClass(type)) {
return asImpl().handleReference(type);
} else if (genericSig->isConcreteType(type)) {
return asImpl().visit(genericSig->getConcreteType(type)
->getCanonicalType());
} else if (auto concreteTy = genericSig
->maybeGetConcreteType(type).getValueOr(Type())) {
return asImpl().visit(concreteTy->getCanonicalType());
} else {
return asImpl().handleAddressOnly(type,
RecursiveProperties::forOpaque());
Expand All @@ -281,7 +281,8 @@ namespace {
auto signature = getGenericSignature();
assert(signature && "dependent type without generic signature?!");

if (auto concreteType = signature->getConcreteType(type))
if (auto concreteType = signature
->maybeGetConcreteType(type).getValueOr(Type()))
return concreteType->getCanonicalType();

assert(signature->requiresClass(type));
Expand Down
77 changes: 53 additions & 24 deletions lib/Sema/TypeCheckProtocolInference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -794,8 +794,22 @@ Type AssociatedTypeInference::computeFixedTypeWitness(
auto genericSig = conformedProto->getGenericSignature();
if (!genericSig) return Type();

Type concreteType = genericSig->getConcreteType(dependentType);
if (!concreteType) continue;
Type concreteType;
if (auto optType = genericSig->maybeGetConcreteType(dependentType)) {
if (optType.getValue()) {
concreteType = optType.getValue();

// If this associated type has a same-type constraint
// with Self, the fixed type is the adoptee.
} else if (genericSig->areSameTypeParameterInContext(
dependentType, proto->getSelfInterfaceType())) {
concreteType = adoptee;
} else {
continue;
}
} else {
continue;
}

if (!resultType) {
resultType = concreteType;
Expand Down Expand Up @@ -936,9 +950,16 @@ Type AssociatedTypeInference::substCurrentTypeWitnesses(Type type) {
llvm::DenseSet<AssociatedTypeDecl *> recursionCheck;
foldDependentMemberTypes = [&](Type type) -> Type {
if (auto depMemTy = type->getAs<DependentMemberType>()) {
auto baseTy = depMemTy->getBase().transform(foldDependentMemberTypes);
if (baseTy.isNull() || baseTy->hasTypeParameter())
return nullptr;
Type baseTy;
if (depMemTy->getBase()->is<GenericTypeParamType>()) {
// The base type is Self.
baseTy = depMemTy->getBase();
} else {
baseTy = depMemTy->getBase().transform(foldDependentMemberTypes);

if (baseTy.isNull() || baseTy->hasTypeParameter())
return nullptr;
}

auto assocType = depMemTy->getAssocType();
if (!assocType)
Expand All @@ -949,21 +970,26 @@ Type AssociatedTypeInference::substCurrentTypeWitnesses(Type type) {

SWIFT_DEFER { recursionCheck.erase(assocType); };

// Try to substitute into the base type.
Type result = depMemTy->substBaseType(dc->getParentModule(), baseTy);
if (!result->hasError())
return result;

// If that failed, check whether it's because of the conformance we're
// evaluating.
auto localConformance
= TypeChecker::conformsToProtocol(
baseTy, assocType->getProtocol(), dc,
ConformanceCheckFlags::SkipConditionalRequirements);
if (localConformance.isInvalid() || localConformance.isAbstract() ||
(localConformance.getConcrete()->getRootConformance() !=
conformance)) {
return nullptr;
// If the base type is Self, we are folding a fixed type witness, which
// is to say, a witness through same-type constraints on protocols;
// substituting into the base and conformance lookup are irrelevant.
if (!baseTy->is<GenericTypeParamType>()) {
// Try to substitute into the base type.
Type result = depMemTy->substBaseType(dc->getParentModule(), baseTy);
if (!result->hasError())
return result;

// If that failed, check whether it's because of the conformance we're
// evaluating.
auto localConformance
= TypeChecker::conformsToProtocol(
baseTy, assocType->getProtocol(), dc,
ConformanceCheckFlags::SkipConditionalRequirements);
if (localConformance.isInvalid() || localConformance.isAbstract() ||
(localConformance.getConcrete()->getRootConformance() !=
conformance)) {
return nullptr;
}
}

// Find the tentative type witness for this associated type.
Expand All @@ -974,10 +1000,13 @@ Type AssociatedTypeInference::substCurrentTypeWitnesses(Type type) {
return known->first.transform(foldDependentMemberTypes);
}

// The presence of a generic type parameter indicates that we
// cannot use this type binding.
if (type->is<GenericTypeParamType>()) {
return nullptr;
if (const auto genParam = type->getAs<GenericTypeParamType>()) {
bool isProtocolSelf = true;
if (const auto gpDecl = genParam->getDecl())
if (adoptee->getAnyNominal() == gpDecl->getDeclContext())
isProtocolSelf = false;
if (isProtocolSelf)
return adoptee;
}

return type;
Expand Down
40 changes: 39 additions & 1 deletion test/Generics/protocol_where_clause.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-typecheck-verify-swift -swift-version 4
// RUN: %target-typecheck-verify-swift -swift-version 4 -print-ast %s | %FileCheck %s

func needsSameType<T>(_: T.Type, _: T.Type) {}

Expand Down Expand Up @@ -77,3 +77,41 @@ protocol P2 {
associatedtype AT
}
protocol P3: P2 where AT == Y<X> {}


// SR-10831:
struct G<T> {}

protocol SR10831_P1 {
associatedtype A
associatedtype B
associatedtype C
}

protocol SR10831_P2: SR10831_P1 where A == G<G<C?>> {}
protocol SR10831_P3: SR10831_P2 where B == Int, C == G<B> {}

struct SR10831: SR10831_P3 { // OK
// CHECK: typealias A = G<G<G<Int>?>>
// CHECK: typealias B = Int
// CHECK: typealias C = G<Int>
}


// SR-11671:
protocol SR11671_P1 {
associatedtype A
associatedtype B
}
protocol SR11671_P2: SR11671_P1 where A == Self {}
protocol SR11671_P3: SR11671_P2 where B == G<Self?> {}

struct SR11671_S1: SR11671_P3 { // OK
// CHECK: typealias A = SR11671_S1
// CHECK: typealias B = G<SR11671_S1?>
}

struct SR11671_S2<T, U>: SR11671_P3 { // OK
// CHECK: typealias A = SR11671_S2<T, U>
// CHECK: typealias B = G<SR11671_S2<T, U>?>
}