Skip to content

Commit 89f3da6

Browse files
committed
[Sema] Start recording default associated conformances.
When forming the default witness table for a resilient protocol, look for default associated conformances as well. These are identified by associated conformance requirements whose root associated type has a default type. For such requirements, we look for a conformance and record it as a default associated conformance. Emit a warning in cases where we don't find such a conformance, because the associated type and conformance *may* have been added with the intent of being resilient, and we can't know. This warning might be a terrible idea, but it is only enabled under -enable-resilience (which itself is hidden) and fires in one only place in the standard library (which seems legitimate), so we'll try it for now.
1 parent b71bef1 commit 89f3da6

File tree

5 files changed

+164
-13
lines changed

5 files changed

+164
-13
lines changed

include/swift/AST/Decl.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3979,6 +3979,23 @@ class ProtocolDecl final : public NominalTypeDecl {
39793979
/// Record the default witness for a requirement.
39803980
void setDefaultWitness(ValueDecl *requirement, Witness witness);
39813981

3982+
/// Returns the default associated conformance witness for an associated
3983+
/// type, or \c None if there is no default.
3984+
///
3985+
/// \param reqIndex The index of the associated conformance within the
3986+
/// requirement signature.
3987+
Optional<ProtocolConformanceRef> getDefaultAssociatedConformanceWitness(
3988+
unsigned reqIndex) const;
3989+
3990+
/// Set the default associated conformance witness for the given
3991+
/// associated conformance.
3992+
///
3993+
/// \param reqIndex The index of the associated conformance within the
3994+
/// requirement signature.
3995+
void setDefaultAssociatedConformanceWitness(
3996+
unsigned reqIndex,
3997+
ProtocolConformanceRef conformance);
3998+
39823999
/// Retrieve the name to use for this protocol when interoperating
39834000
/// with the Objective-C runtime.
39844001
StringRef getObjCRuntimeName(llvm::SmallVectorImpl<char> &buffer) const;

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,6 +1686,11 @@ ERROR(type_witness_objc_generic_parameter,none,
16861686
NOTE(witness_fix_access,none,
16871687
"mark the %0 as '%select{%error|fileprivate|internal|public|%error}1' to "
16881688
"satisfy the requirement", (DescriptiveDeclKind, AccessLevel))
1689+
WARNING(assoc_type_default_conformance_failed,none,
1690+
"default type %0 for associated type %1 does not satisfy constraint "
1691+
"%2: %3", (Type, DeclName, Type, Type))
1692+
NOTE(assoc_type_default_here,none,
1693+
"associated type %0 has default type %1 written here", (DeclName, Type))
16891694

16901695
ERROR(protocol_access,none,
16911696
"%select{protocol must be declared %select{"

lib/AST/ASTContext.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,11 @@ FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL)
285285
llvm::DenseMap<std::pair<const ProtocolDecl *, AssociatedTypeDecl *>, Type>
286286
DefaultTypeWitnesses;
287287

288+
/// Default associated conformance witnesses for protocols.
289+
llvm::DenseMap<std::pair<const ProtocolDecl *, unsigned>,
290+
ProtocolConformanceRef>
291+
DefaultAssociatedConformanceWitnesses;
292+
288293
/// \brief Structure that captures data that is segregated into different
289294
/// arenas.
290295
struct Arena {
@@ -1709,6 +1714,27 @@ void ProtocolDecl::setDefaultTypeWitness(AssociatedTypeDecl *assocType,
17091714
(void)pair;
17101715
}
17111716

1717+
Optional<ProtocolConformanceRef>
1718+
ProtocolDecl::getDefaultAssociatedConformanceWitness(unsigned reqIndex) const {
1719+
auto &ctx = getASTContext();
1720+
auto found =
1721+
ctx.getImpl().DefaultAssociatedConformanceWitnesses.find({this, reqIndex});
1722+
if (found == ctx.getImpl().DefaultAssociatedConformanceWitnesses.end())
1723+
return None;
1724+
1725+
return found->second;
1726+
}
1727+
1728+
void ProtocolDecl::setDefaultAssociatedConformanceWitness(
1729+
unsigned reqIndex,
1730+
ProtocolConformanceRef conformance) {
1731+
auto &ctx = getASTContext();
1732+
auto pair = ctx.getImpl().DefaultAssociatedConformanceWitnesses.insert(
1733+
std::make_pair(std::make_pair(this, reqIndex), conformance));
1734+
assert(pair.second && "Already have a default associated conformance");
1735+
(void)pair;
1736+
}
1737+
17121738
bool ASTContext::canImportModule(std::pair<Identifier, SourceLoc> ModulePath) {
17131739
// If this module has already been successfully imported, it is importable.
17141740
if (getLoadedModule(ModulePath) != nullptr)

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 98 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5252,6 +5252,30 @@ void DefaultWitnessChecker::recordWitness(
52525252
void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) {
52535253
DefaultWitnessChecker checker(*this, proto);
52545254

5255+
// Find the default for the given associated type.
5256+
auto findAssociatedTypeDefault =
5257+
[&](AssociatedTypeDecl *assocType,
5258+
AssociatedTypeDecl **defaultedAssocTypeOut = nullptr) -> Type {
5259+
auto defaultedAssocType =
5260+
AssociatedTypeInference::findDefaultedAssociatedType(*this, assocType);
5261+
if (!defaultedAssocType)
5262+
return nullptr;;
5263+
5264+
Type defaultType = defaultedAssocType->getDefaultDefinitionLoc().getType();
5265+
if (!defaultType)
5266+
return nullptr;
5267+
5268+
// Map out of its protocol context...
5269+
defaultType = defaultType->mapTypeOutOfContext();
5270+
if (defaultType->hasError())
5271+
return nullptr;
5272+
5273+
if (defaultedAssocTypeOut)
5274+
*defaultedAssocTypeOut = defaultedAssocType;
5275+
5276+
return defaultType;
5277+
};
5278+
52555279
for (auto *requirement : proto->getMembers()) {
52565280
if (requirement->isInvalid())
52575281
continue;
@@ -5262,19 +5286,8 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) {
52625286

52635287
if (auto assocType = dyn_cast<AssociatedTypeDecl>(valueDecl)) {
52645288
if (assocType->getOverriddenDecls().empty()) {
5265-
if (auto defaultedAssocType =
5266-
AssociatedTypeInference::findDefaultedAssociatedType(
5267-
*this, assocType)) {
5268-
Type defaultType =
5269-
defaultedAssocType->getDefaultDefinitionLoc().getType();
5270-
5271-
// Map out of its protocol context...
5272-
defaultType = defaultType->mapTypeOutOfContext();
5273-
5274-
if (!defaultType->hasError()) {
5275-
proto->setDefaultTypeWitness(assocType, defaultType);
5276-
}
5277-
}
5289+
if (Type defaultType = findAssociatedTypeDefault(assocType))
5290+
proto->setDefaultTypeWitness(assocType, defaultType);
52785291
}
52795292

52805293
continue;
@@ -5288,6 +5301,78 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) {
52885301

52895302
checker.resolveWitnessViaLookup(valueDecl);
52905303
}
5304+
5305+
// Find defaults for any associated conformances rooted on defaulted
5306+
// associated types.
5307+
auto requirementSignature = proto->getRequirementSignature();
5308+
for (unsigned reqIndex : indices(requirementSignature)) {
5309+
const auto &req = requirementSignature[reqIndex];
5310+
if (req.getKind() != RequirementKind::Conformance)
5311+
continue;
5312+
if (req.getFirstType()->isEqual(proto->getProtocolSelfType()))
5313+
continue;
5314+
5315+
// Find the innermost dependent member type (e.g., Self.AssocType), so
5316+
// we can look at the associated type.
5317+
auto depMemTy = req.getFirstType()->getAs<DependentMemberType>();
5318+
if (!depMemTy)
5319+
continue;
5320+
5321+
while (auto innerDepMemTy =
5322+
depMemTy->getBase()->getAs<DependentMemberType>())
5323+
depMemTy = innerDepMemTy;
5324+
5325+
if (!depMemTy->getBase()->isEqual(proto->getProtocolSelfType()))
5326+
continue;
5327+
5328+
auto assocType = depMemTy->getAssocType();
5329+
if (!assocType)
5330+
continue;
5331+
5332+
// Find the associated type nearest our own protocol, which might have
5333+
// a default not available in the associated type referenced by the
5334+
// (canonicalized) requirement.
5335+
if (assocType->getProtocol() != proto) {
5336+
SmallVector<ValueDecl *, 2> found;
5337+
proto->getModuleContext()->lookupQualified(
5338+
proto, assocType->getFullName(),
5339+
NL_QualifiedDefault|NL_ProtocolMembers|NL_OnlyTypes,
5340+
found);
5341+
if (found.size() == 1 && isa<AssociatedTypeDecl>(found[0]))
5342+
assocType = cast<AssociatedTypeDecl>(found[0]);
5343+
}
5344+
5345+
// Dig out the default associated type definition.
5346+
AssociatedTypeDecl *defaultedAssocType = nullptr;
5347+
Type defaultAssocType = findAssociatedTypeDefault(assocType,
5348+
&defaultedAssocType);
5349+
if (!defaultAssocType)
5350+
continue;
5351+
5352+
Type defaultAssocTypeInContext =
5353+
proto->mapTypeIntoContext(defaultAssocType);
5354+
auto requirementProto =
5355+
req.getSecondType()->castTo<ProtocolType>()->getDecl();
5356+
auto conformance = conformsToProtocol(defaultAssocTypeInContext,
5357+
requirementProto, proto,
5358+
ConformanceCheckFlags::Used);
5359+
if (!conformance) {
5360+
// Diagnose the lack of a conformance. This is potentially an ABI
5361+
// incompatibility.
5362+
diagnose(proto, diag::assoc_type_default_conformance_failed,
5363+
defaultAssocType, assocType->getFullName(), req.getFirstType(),
5364+
req.getSecondType());
5365+
diagnose(defaultedAssocType, diag::assoc_type_default_here,
5366+
assocType->getFullName(), defaultAssocType)
5367+
.highlight(
5368+
defaultedAssocType->getDefaultDefinitionLoc().getSourceRange());
5369+
5370+
continue;
5371+
}
5372+
5373+
// Record the default associated conformance.
5374+
proto->setDefaultAssociatedConformanceWitness(reqIndex, *conformance);
5375+
}
52915376
}
52925377

52935378
void TypeChecker::recordKnownWitness(NormalProtocolConformance *conformance,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %target-typecheck-verify-swift -enable-resilience
2+
3+
public struct Wrapper<T: P>: P { }
4+
extension Wrapper: Q where T: Q { }
5+
6+
public protocol PBase {
7+
associatedtype AssocType
8+
}
9+
10+
public protocol P: PBase {
11+
override associatedtype AssocType: P = Wrapper<Self>
12+
// expected-note@-1{{associated type 'AssocType' has default type 'Wrapper<Self>' written here}}
13+
}
14+
15+
public protocol Q: P where Self.AssocType: Q { }
16+
17+
public protocol R: Q where Self.AssocType: R { }
18+
// expected-warning@-1{{default type 'Wrapper<Self>' for associated type 'AssocType' does not satisfy constraint 'Self.AssocType': 'R'}}

0 commit comments

Comments
 (0)