mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
These will be more useful once substitutions in protocol conformances are moved to use interface types. At first, these are only going to be used by the SIL optimizer.
182 lines
6.3 KiB
C++
182 lines
6.3 KiB
C++
//===--- Substitution.cpp - Type substitutions ----------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the Substitution class and operations on it.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/AST/Substitution.h"
|
|
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/GenericEnvironment.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
using namespace swift;
|
|
|
|
bool Substitution::operator==(const Substitution &other) const {
|
|
// The archetypes may be missing, but we can compare them directly
|
|
// because archetypes are always canonical.
|
|
return
|
|
Replacement->getCanonicalType() == other.Replacement->getCanonicalType() &&
|
|
Conformance.equals(other.Conformance);
|
|
}
|
|
|
|
Substitution::Substitution(Type Replacement,
|
|
ArrayRef<ProtocolConformanceRef> Conformance)
|
|
: Replacement(Replacement), Conformance(Conformance)
|
|
{
|
|
// The replacement type must be materializable.
|
|
assert(Replacement->isMaterializable()
|
|
&& "cannot substitute with a non-materializable type");
|
|
}
|
|
|
|
Substitution Substitution::subst(Module *module,
|
|
GenericSignature *sig,
|
|
GenericEnvironment *env,
|
|
ArrayRef<Substitution> subs) const {
|
|
TypeSubstitutionMap subMap;
|
|
ArchetypeConformanceMap conformanceMap;
|
|
|
|
assert(sig && env);
|
|
env->getSubstitutionMap(module, sig, subs, subMap, conformanceMap);
|
|
return subst(module, subMap, conformanceMap);
|
|
}
|
|
|
|
static Optional<ProtocolConformanceRef>
|
|
lookupArchetypeConformance(ProtocolDecl *proto,
|
|
ArrayRef<ProtocolConformanceRef> conformances) {
|
|
for (ProtocolConformanceRef found : conformances) {
|
|
auto foundProto = found.getRequirement();
|
|
if (foundProto == proto) {
|
|
return found;
|
|
} else if (foundProto->inheritsFrom(proto)) {
|
|
if (found.isConcrete()) {
|
|
return ProtocolConformanceRef(
|
|
found.getConcrete()->getInheritedConformance(proto));
|
|
}
|
|
|
|
return found;
|
|
}
|
|
}
|
|
|
|
return None;
|
|
}
|
|
|
|
static Optional<ProtocolConformanceRef>
|
|
lookupArchetypeConformance(ArchetypeType *replacement,
|
|
ProtocolDecl *proto,
|
|
ArchetypeConformanceMap &conformanceMap) {
|
|
// Check for conformances for the type that apply to the original
|
|
// substituted archetype.
|
|
auto it = conformanceMap.find(replacement);
|
|
if (it != conformanceMap.end()) {
|
|
if (auto conformance = lookupArchetypeConformance(proto, it->second)) {
|
|
return conformance;
|
|
}
|
|
}
|
|
|
|
// Check if we have substitutions for the parent.
|
|
if (auto *parent = replacement->getParent()) {
|
|
auto *assocType = replacement->getAssocType();
|
|
auto *parentProto = assocType->getProtocol();
|
|
auto conformance =
|
|
lookupArchetypeConformance(parent, parentProto, conformanceMap);
|
|
|
|
if (conformance) {
|
|
if (!conformance->isConcrete())
|
|
return ProtocolConformanceRef(proto);
|
|
|
|
auto sub = conformance->getConcrete()->getTypeWitnessSubstAndDecl(
|
|
assocType, nullptr).first;
|
|
|
|
return lookupArchetypeConformance(proto, sub.getConformances());
|
|
}
|
|
}
|
|
|
|
return None;
|
|
}
|
|
|
|
Substitution Substitution::subst(Module *module,
|
|
TypeSubstitutionMap &subMap,
|
|
ArchetypeConformanceMap &conformanceMap) const {
|
|
// Substitute the replacement.
|
|
Type substReplacement = Replacement.subst(module, subMap, None);
|
|
assert(substReplacement && "substitution replacement failed");
|
|
|
|
if (substReplacement->isEqual(Replacement))
|
|
return *this;
|
|
|
|
if (Conformance.empty()) {
|
|
return {substReplacement, Conformance};
|
|
}
|
|
|
|
bool conformancesChanged = false;
|
|
SmallVector<ProtocolConformanceRef, 4> substConformances;
|
|
substConformances.reserve(Conformance.size());
|
|
|
|
for (auto c : Conformance) {
|
|
// If we have a concrete conformance, we need to substitute the
|
|
// conformance to apply to the new type.
|
|
if (c.isConcrete()) {
|
|
auto substC = c.getConcrete()->subst(module, substReplacement,
|
|
subMap, conformanceMap);
|
|
substConformances.push_back(ProtocolConformanceRef(substC));
|
|
if (c != substConformances.back())
|
|
conformancesChanged = true;
|
|
continue;
|
|
}
|
|
|
|
// Otherwise, we may need to fill in the conformance.
|
|
ProtocolDecl *proto = c.getAbstract();
|
|
Optional<ProtocolConformanceRef> conformance;
|
|
|
|
// If the original type was an archetype, check the conformance map.
|
|
if (auto replacementArch = Replacement->getAs<ArchetypeType>()) {
|
|
conformance = lookupArchetypeConformance(replacementArch, proto,
|
|
conformanceMap);
|
|
}
|
|
|
|
// If that didn't find anything, we can still synthesize AnyObject
|
|
// conformances from thin air. FIXME: gross.
|
|
if (!conformance &&
|
|
proto->isSpecificProtocol(KnownProtocolKind::AnyObject)) {
|
|
auto classDecl
|
|
= substReplacement->getClassOrBoundGenericClass();
|
|
SmallVector<ProtocolConformance *, 1> lookupResults;
|
|
classDecl->lookupConformance(classDecl->getParentModule(),
|
|
proto, lookupResults);
|
|
conformance = ProtocolConformanceRef(lookupResults.front());
|
|
}
|
|
|
|
if (conformance) {
|
|
if (conformance->isConcrete())
|
|
conformancesChanged = true;
|
|
substConformances.push_back(*conformance);
|
|
} else {
|
|
assert(substReplacement->hasDependentProtocolConformances() &&
|
|
"couldn't find concrete conformance for concrete type?");
|
|
substConformances.push_back(ProtocolConformanceRef(proto));
|
|
}
|
|
}
|
|
assert(substConformances.size() == Conformance.size());
|
|
|
|
ArrayRef<ProtocolConformanceRef> substConfs;
|
|
if (conformancesChanged)
|
|
substConfs = module->getASTContext().AllocateCopy(substConformances);
|
|
else
|
|
substConfs = Conformance;
|
|
|
|
return Substitution{substReplacement, substConfs};
|
|
}
|