AST: Split off ProtocolConformanceRef.cpp from ProtocolConformance.cpp

This commit is contained in:
Slava Pestov
2022-08-18 22:27:11 -04:00
parent 3992b18e0b
commit e08e525063
3 changed files with 307 additions and 282 deletions

View File

@@ -74,6 +74,7 @@ add_swift_host_library(swiftAST STATIC
PlatformKind.cpp PlatformKind.cpp
PrettyStackTrace.cpp PrettyStackTrace.cpp
ProtocolConformance.cpp ProtocolConformance.cpp
ProtocolConformanceRef.cpp
RawComment.cpp RawComment.cpp
RequirementEnvironment.cpp RequirementEnvironment.cpp
RequirementMachine/ConcreteContraction.cpp RequirementMachine/ConcreteContraction.cpp

View File

@@ -10,7 +10,7 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// //
// This file implements the protocol conformance data structures. // This file implements the ProtocolConformance class hierarchy.
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@@ -25,13 +25,9 @@
#include "swift/AST/LazyResolver.h" #include "swift/AST/LazyResolver.h"
#include "swift/AST/Module.h" #include "swift/AST/Module.h"
#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/TypeWalker.h"
#include "swift/AST/Types.h" #include "swift/AST/Types.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Statistic.h" #include "swift/Basic/Statistic.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/Statistic.h" #include "llvm/ADT/Statistic.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/SaveAndRestore.h"
@@ -79,128 +75,6 @@ void Witness::dump(llvm::raw_ostream &out) const {
} }
} }
ProtocolConformanceRef::ProtocolConformanceRef(ProtocolDecl *protocol,
ProtocolConformance *conf) {
assert(protocol != nullptr &&
"cannot construct ProtocolConformanceRef with null protocol");
if (conf) {
assert(protocol == conf->getProtocol() && "protocol conformance mismatch");
Union = conf;
} else {
Union = protocol;
}
}
ProtocolDecl *ProtocolConformanceRef::getRequirement() const {
assert(!isInvalid());
if (isConcrete()) {
return getConcrete()->getProtocol();
} else {
return getAbstract();
}
}
ProtocolConformanceRef
ProtocolConformanceRef::subst(Type origType,
SubstitutionMap subMap,
SubstOptions options) const {
return subst(origType,
QuerySubstitutionMap{subMap},
LookUpConformanceInSubstitutionMap(subMap),
options);
}
ProtocolConformanceRef
ProtocolConformanceRef::subst(Type origType,
TypeSubstitutionFn subs,
LookupConformanceFn conformances,
SubstOptions options) const {
if (isInvalid())
return *this;
// If we have a concrete conformance, we need to substitute the
// conformance to apply to the new type.
if (isConcrete())
return ProtocolConformanceRef(getConcrete()->subst(subs, conformances,
options));
// If the type is an opaque archetype, the conformance will remain abstract,
// unless we're specifically substituting opaque types.
if (auto origArchetype = origType->getAs<ArchetypeType>()) {
if (!options.contains(SubstFlags::SubstituteOpaqueArchetypes)
&& isa<OpaqueTypeArchetypeType>(origArchetype)) {
return *this;
}
}
// Otherwise, compute the substituted type.
auto substType = origType.subst(subs, conformances, options);
auto *proto = getRequirement();
// If the type is an existential, it must be self-conforming.
if (substType->isExistentialType()) {
auto optConformance =
proto->getModuleContext()->lookupExistentialConformance(substType,
proto);
if (optConformance)
return optConformance;
return ProtocolConformanceRef::forInvalid();
}
// Check the conformance map.
return conformances(origType->getCanonicalType(), substType, proto);
}
ProtocolConformanceRef ProtocolConformanceRef::mapConformanceOutOfContext() const {
if (!isConcrete())
return *this;
auto *concrete = getConcrete()->subst(
[](SubstitutableType *type) -> Type {
if (auto *archetypeType = type->getAs<ArchetypeType>())
return archetypeType->getInterfaceType();
return type;
},
MakeAbstractConformanceForGenericType());
return ProtocolConformanceRef(concrete);
}
Type
ProtocolConformanceRef::getTypeWitnessByName(Type type, Identifier name) const {
assert(!isInvalid());
// Find the named requirement.
ProtocolDecl *proto = getRequirement();
auto *assocType = proto->getAssociatedType(name);
// FIXME: Shouldn't this be a hard error?
if (!assocType)
return ErrorType::get(proto->getASTContext());
return assocType->getDeclaredInterfaceType().subst(
SubstitutionMap::getProtocolSubstitutions(proto, type, *this));
}
ConcreteDeclRef
ProtocolConformanceRef::getWitnessByName(Type type, DeclName name) const {
// Find the named requirement.
auto *proto = getRequirement();
auto *requirement = proto->getSingleRequirement(name);
if (requirement == nullptr)
return ConcreteDeclRef();
// For a type with dependent conformance, just return the requirement from
// the protocol. There are no protocol conformance tables.
if (!isConcrete()) {
auto subs = SubstitutionMap::getProtocolSubstitutions(proto, type, *this);
return ConcreteDeclRef(requirement, subs);
}
return getConcrete()->getWitnessDeclRef(requirement);
}
#define CONFORMANCE_SUBCLASS_DISPATCH(Method, Args) \ #define CONFORMANCE_SUBCLASS_DISPATCH(Method, Args) \
switch (getKind()) { \ switch (getKind()) { \
case ProtocolConformanceKind::Normal: \ case ProtocolConformanceKind::Normal: \
@@ -455,26 +329,6 @@ ArrayRef<Requirement> ProtocolConformance::getConditionalRequirements() const {
CONFORMANCE_SUBCLASS_DISPATCH(getConditionalRequirements, ()); CONFORMANCE_SUBCLASS_DISPATCH(getConditionalRequirements, ());
} }
Optional<ArrayRef<Requirement>>
ProtocolConformanceRef::getConditionalRequirementsIfAvailable() const {
if (isConcrete())
return getConcrete()->getConditionalRequirementsIfAvailable();
else
// An abstract conformance is never conditional: any conditionality in the
// concrete types that will eventually pass through this at runtime is
// completely pre-checked and packaged up.
return ArrayRef<Requirement>();
}
ArrayRef<Requirement>
ProtocolConformanceRef::getConditionalRequirements() const {
if (isConcrete())
return getConcrete()->getConditionalRequirements();
else
// An abstract conformance is never conditional, as above.
return {};
}
Optional<ArrayRef<Requirement>> Optional<ArrayRef<Requirement>>
NormalProtocolConformance::getConditionalRequirementsIfAvailable() const { NormalProtocolConformance::getConditionalRequirementsIfAvailable() const {
const auto &eval = getDeclContext()->getASTContext().evaluator; const auto &eval = getDeclContext()->getASTContext().evaluator;
@@ -682,54 +536,6 @@ Type ProtocolConformance::getAssociatedType(Type assocType) const {
return ref.getAssociatedType(getType(), assocType); return ref.getAssociatedType(getType(), assocType);
} }
Type ProtocolConformanceRef::getAssociatedType(Type conformingType,
Type assocType) const {
assert(!isConcrete() || getConcrete()->getType()->isEqual(conformingType));
auto type = assocType->getCanonicalType();
auto proto = getRequirement();
// Fast path for generic parameters.
if (isa<GenericTypeParamType>(type)) {
assert(type->isEqual(proto->getSelfInterfaceType()) &&
"type parameter in protocol was not Self");
return conformingType;
}
// Fast path for dependent member types on 'Self' of our associated types.
auto memberType = cast<DependentMemberType>(type);
if (memberType.getBase()->isEqual(proto->getSelfInterfaceType()) &&
memberType->getAssocType()->getProtocol() == proto &&
isConcrete())
return getConcrete()->getTypeWitness(memberType->getAssocType());
// General case: consult the substitution map.
auto substMap =
SubstitutionMap::getProtocolSubstitutions(proto, conformingType, *this);
return type.subst(substMap);
}
ProtocolConformanceRef
ProtocolConformanceRef::getAssociatedConformance(Type conformingType,
Type assocType,
ProtocolDecl *protocol) const {
// If this is a concrete conformance, look up the associated conformance.
if (isConcrete()) {
auto conformance = getConcrete();
assert(conformance->getType()->isEqual(conformingType));
return conformance->getAssociatedConformance(assocType, protocol);
}
// Otherwise, apply the substitution {self -> conformingType}
// to the abstract conformance requirement laid upon the dependent type
// by the protocol.
auto subMap =
SubstitutionMap::getProtocolSubstitutions(getRequirement(),
conformingType, *this);
auto abstractConf = ProtocolConformanceRef(protocol);
return abstractConf.subst(assocType, subMap);
}
ProtocolConformanceRef ProtocolConformanceRef
ProtocolConformance::getAssociatedConformance(Type assocType, ProtocolConformance::getAssociatedConformance(Type assocType,
ProtocolDecl *protocol) const { ProtocolDecl *protocol) const {
@@ -1669,20 +1475,6 @@ ProtocolConformance *ProtocolConformance::getCanonicalConformance() {
llvm_unreachable("bad ProtocolConformanceKind"); llvm_unreachable("bad ProtocolConformanceKind");
} }
/// Check of all types used by the conformance are canonical.
bool ProtocolConformanceRef::isCanonical() const {
if (isAbstract() || isInvalid())
return true;
return getConcrete()->isCanonical();
}
ProtocolConformanceRef
ProtocolConformanceRef::getCanonicalConformanceRef() const {
if (isAbstract() || isInvalid())
return *this;
return ProtocolConformanceRef(getConcrete()->getCanonicalConformance());
}
BuiltinProtocolConformance::BuiltinProtocolConformance( BuiltinProtocolConformance::BuiltinProtocolConformance(
Type conformingType, ProtocolDecl *protocol, Type conformingType, ProtocolDecl *protocol,
GenericSignature genericSig, GenericSignature genericSig,
@@ -1742,76 +1534,3 @@ void swift::simple_display(llvm::raw_ostream &out,
SourceLoc swift::extractNearestSourceLoc(const ProtocolConformance *conformance) { SourceLoc swift::extractNearestSourceLoc(const ProtocolConformance *conformance) {
return extractNearestSourceLoc(conformance->getDeclContext()); return extractNearestSourceLoc(conformance->getDeclContext());
} }
void swift::simple_display(llvm::raw_ostream &out, ProtocolConformanceRef conformanceRef) {
if (conformanceRef.isAbstract()) {
simple_display(out, conformanceRef.getAbstract());
} else if (conformanceRef.isConcrete()) {
simple_display(out, conformanceRef.getConcrete());
}
}
SourceLoc swift::extractNearestSourceLoc(const ProtocolConformanceRef conformanceRef) {
if (conformanceRef.isAbstract()) {
return extractNearestSourceLoc(conformanceRef.getAbstract());
} else if (conformanceRef.isConcrete()) {
return extractNearestSourceLoc(conformanceRef.getConcrete());
}
return SourceLoc();
}
bool ProtocolConformanceRef::hasUnavailableConformance() const {
if (isInvalid())
return false;
// Abstract conformances are never unavailable.
if (!isConcrete())
return false;
// Check whether this conformance is on an unavailable extension.
auto concrete = getConcrete();
auto ext = dyn_cast<ExtensionDecl>(concrete->getDeclContext());
if (ext && AvailableAttr::isUnavailable(ext))
return true;
// Check the conformances in the substitution map.
auto module = concrete->getDeclContext()->getParentModule();
auto subMap = concrete->getSubstitutions(module);
for (auto subConformance : subMap.getConformances()) {
if (subConformance.hasUnavailableConformance())
return true;
}
return false;
}
bool ProtocolConformanceRef::hasMissingConformance(ModuleDecl *module) const {
return forEachMissingConformance(module,
[](BuiltinProtocolConformance *builtin) {
return true;
});
}
bool ProtocolConformanceRef::forEachMissingConformance(
ModuleDecl *module,
llvm::function_ref<bool(BuiltinProtocolConformance *missing)> fn) const {
if (!isConcrete())
return false;
// Is this a missing conformance?
ProtocolConformance *concreteConf = getConcrete();
RootProtocolConformance *rootConf = concreteConf->getRootConformance();
if (auto builtinConformance = dyn_cast<BuiltinProtocolConformance>(rootConf)){
if (builtinConformance->isMissing() && fn(builtinConformance))
return true;
}
// Check conformances that are part of this conformance.
auto subMap = concreteConf->getSubstitutions(module);
for (auto conformance : subMap.getConformances()) {
if (conformance.forEachMissingConformance(module, fn))
return true;
}
return false;
}

View File

@@ -0,0 +1,305 @@
//===--- ProtocolConformance.cpp - AST Protocol Conformance Reference -----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 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
//
//===----------------------------------------------------------------------===//
//
// This file implements the ProtocolConformanceRef structure, which wraps a
// concrete or abstract conformance, or is invalid.
//
//===----------------------------------------------------------------------===//
#include "swift/AST/ProtocolConformanceRef.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Availability.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Module.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/Types.h"
#define DEBUG_TYPE "AST"
using namespace swift;
ProtocolConformanceRef::ProtocolConformanceRef(ProtocolDecl *protocol,
ProtocolConformance *conf) {
assert(protocol != nullptr &&
"cannot construct ProtocolConformanceRef with null protocol");
if (conf) {
assert(protocol == conf->getProtocol() && "protocol conformance mismatch");
Union = conf;
} else {
Union = protocol;
}
}
ProtocolDecl *ProtocolConformanceRef::getRequirement() const {
assert(!isInvalid());
if (isConcrete()) {
return getConcrete()->getProtocol();
} else {
return getAbstract();
}
}
ProtocolConformanceRef
ProtocolConformanceRef::subst(Type origType,
SubstitutionMap subMap,
SubstOptions options) const {
return subst(origType,
QuerySubstitutionMap{subMap},
LookUpConformanceInSubstitutionMap(subMap),
options);
}
ProtocolConformanceRef
ProtocolConformanceRef::subst(Type origType,
TypeSubstitutionFn subs,
LookupConformanceFn conformances,
SubstOptions options) const {
if (isInvalid())
return *this;
// If we have a concrete conformance, we need to substitute the
// conformance to apply to the new type.
if (isConcrete())
return ProtocolConformanceRef(getConcrete()->subst(subs, conformances,
options));
// If the type is an opaque archetype, the conformance will remain abstract,
// unless we're specifically substituting opaque types.
if (auto origArchetype = origType->getAs<ArchetypeType>()) {
if (!options.contains(SubstFlags::SubstituteOpaqueArchetypes)
&& isa<OpaqueTypeArchetypeType>(origArchetype)) {
return *this;
}
}
// Otherwise, compute the substituted type.
auto substType = origType.subst(subs, conformances, options);
auto *proto = getRequirement();
// If the type is an existential, it must be self-conforming.
if (substType->isExistentialType()) {
auto optConformance =
proto->getModuleContext()->lookupExistentialConformance(substType,
proto);
if (optConformance)
return optConformance;
return ProtocolConformanceRef::forInvalid();
}
// Check the conformance map.
return conformances(origType->getCanonicalType(), substType, proto);
}
ProtocolConformanceRef ProtocolConformanceRef::mapConformanceOutOfContext() const {
if (!isConcrete())
return *this;
auto *concrete = getConcrete()->subst(
[](SubstitutableType *type) -> Type {
if (auto *archetypeType = type->getAs<ArchetypeType>())
return archetypeType->getInterfaceType();
return type;
},
MakeAbstractConformanceForGenericType());
return ProtocolConformanceRef(concrete);
}
Type
ProtocolConformanceRef::getTypeWitnessByName(Type type, Identifier name) const {
assert(!isInvalid());
// Find the named requirement.
ProtocolDecl *proto = getRequirement();
auto *assocType = proto->getAssociatedType(name);
// FIXME: Shouldn't this be a hard error?
if (!assocType)
return ErrorType::get(proto->getASTContext());
return assocType->getDeclaredInterfaceType().subst(
SubstitutionMap::getProtocolSubstitutions(proto, type, *this));
}
ConcreteDeclRef
ProtocolConformanceRef::getWitnessByName(Type type, DeclName name) const {
// Find the named requirement.
auto *proto = getRequirement();
auto *requirement = proto->getSingleRequirement(name);
if (requirement == nullptr)
return ConcreteDeclRef();
// For a type with dependent conformance, just return the requirement from
// the protocol. There are no protocol conformance tables.
if (!isConcrete()) {
auto subs = SubstitutionMap::getProtocolSubstitutions(proto, type, *this);
return ConcreteDeclRef(requirement, subs);
}
return getConcrete()->getWitnessDeclRef(requirement);
}
Optional<ArrayRef<Requirement>>
ProtocolConformanceRef::getConditionalRequirementsIfAvailable() const {
if (isConcrete())
return getConcrete()->getConditionalRequirementsIfAvailable();
else
// An abstract conformance is never conditional: any conditionality in the
// concrete types that will eventually pass through this at runtime is
// completely pre-checked and packaged up.
return ArrayRef<Requirement>();
}
ArrayRef<Requirement>
ProtocolConformanceRef::getConditionalRequirements() const {
if (isConcrete())
return getConcrete()->getConditionalRequirements();
else
// An abstract conformance is never conditional, as above.
return {};
}
Type ProtocolConformanceRef::getAssociatedType(Type conformingType,
Type assocType) const {
assert(!isConcrete() || getConcrete()->getType()->isEqual(conformingType));
auto type = assocType->getCanonicalType();
auto proto = getRequirement();
// Fast path for generic parameters.
if (isa<GenericTypeParamType>(type)) {
assert(type->isEqual(proto->getSelfInterfaceType()) &&
"type parameter in protocol was not Self");
return conformingType;
}
// Fast path for dependent member types on 'Self' of our associated types.
auto memberType = cast<DependentMemberType>(type);
if (memberType.getBase()->isEqual(proto->getSelfInterfaceType()) &&
memberType->getAssocType()->getProtocol() == proto &&
isConcrete())
return getConcrete()->getTypeWitness(memberType->getAssocType());
// General case: consult the substitution map.
auto substMap =
SubstitutionMap::getProtocolSubstitutions(proto, conformingType, *this);
return type.subst(substMap);
}
ProtocolConformanceRef
ProtocolConformanceRef::getAssociatedConformance(Type conformingType,
Type assocType,
ProtocolDecl *protocol) const {
// If this is a concrete conformance, look up the associated conformance.
if (isConcrete()) {
auto conformance = getConcrete();
assert(conformance->getType()->isEqual(conformingType));
return conformance->getAssociatedConformance(assocType, protocol);
}
// Otherwise, apply the substitution {self -> conformingType}
// to the abstract conformance requirement laid upon the dependent type
// by the protocol.
auto subMap =
SubstitutionMap::getProtocolSubstitutions(getRequirement(),
conformingType, *this);
auto abstractConf = ProtocolConformanceRef(protocol);
return abstractConf.subst(assocType, subMap);
}
/// Check of all types used by the conformance are canonical.
bool ProtocolConformanceRef::isCanonical() const {
if (isAbstract() || isInvalid())
return true;
return getConcrete()->isCanonical();
}
ProtocolConformanceRef
ProtocolConformanceRef::getCanonicalConformanceRef() const {
if (isAbstract() || isInvalid())
return *this;
return ProtocolConformanceRef(getConcrete()->getCanonicalConformance());
}
bool ProtocolConformanceRef::hasUnavailableConformance() const {
if (isInvalid())
return false;
// Abstract conformances are never unavailable.
if (!isConcrete())
return false;
// Check whether this conformance is on an unavailable extension.
auto concrete = getConcrete();
auto ext = dyn_cast<ExtensionDecl>(concrete->getDeclContext());
if (ext && AvailableAttr::isUnavailable(ext))
return true;
// Check the conformances in the substitution map.
auto module = concrete->getDeclContext()->getParentModule();
auto subMap = concrete->getSubstitutions(module);
for (auto subConformance : subMap.getConformances()) {
if (subConformance.hasUnavailableConformance())
return true;
}
return false;
}
bool ProtocolConformanceRef::hasMissingConformance(ModuleDecl *module) const {
return forEachMissingConformance(module,
[](BuiltinProtocolConformance *builtin) {
return true;
});
}
bool ProtocolConformanceRef::forEachMissingConformance(
ModuleDecl *module,
llvm::function_ref<bool(BuiltinProtocolConformance *missing)> fn) const {
if (!isConcrete())
return false;
// Is this a missing conformance?
ProtocolConformance *concreteConf = getConcrete();
RootProtocolConformance *rootConf = concreteConf->getRootConformance();
if (auto builtinConformance = dyn_cast<BuiltinProtocolConformance>(rootConf)){
if (builtinConformance->isMissing() && fn(builtinConformance))
return true;
}
// Check conformances that are part of this conformance.
auto subMap = concreteConf->getSubstitutions(module);
for (auto conformance : subMap.getConformances()) {
if (conformance.forEachMissingConformance(module, fn))
return true;
}
return false;
}
void swift::simple_display(llvm::raw_ostream &out, ProtocolConformanceRef conformanceRef) {
if (conformanceRef.isAbstract()) {
simple_display(out, conformanceRef.getAbstract());
} else if (conformanceRef.isConcrete()) {
simple_display(out, conformanceRef.getConcrete());
}
}
SourceLoc swift::extractNearestSourceLoc(const ProtocolConformanceRef conformanceRef) {
if (conformanceRef.isAbstract()) {
return extractNearestSourceLoc(conformanceRef.getAbstract());
} else if (conformanceRef.isConcrete()) {
return extractNearestSourceLoc(conformanceRef.getConcrete());
}
return SourceLoc();
}