//===--- Substitution.cpp - Type substitutions ----------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 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 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/ProtocolConformance.h" #include "swift/AST/SubstitutionMap.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->isEqual(other.Replacement) && Conformance.equals(other.Conformance); } Substitution::Substitution(Type Replacement, ArrayRef Conformance) : Replacement(Replacement), Conformance(Conformance) { // The replacement type must be materializable. assert(Replacement->isMaterializable() && "cannot substitute with a non-materializable type"); } Substitution Substitution::subst(ModuleDecl *module, const SubstitutionMap &subMap) const { return subst(module, QuerySubstitutionMap{subMap}, LookUpConformanceInSubstitutionMap(subMap)); } Substitution Substitution::subst(ModuleDecl *module, TypeSubstitutionFn subs, LookupConformanceFn conformances) const { // Substitute the replacement. Type substReplacement = Replacement.subst(subs, conformances, SubstFlags::UseErrorType); assert(!substReplacement->hasError() && "substitution replacement failed"); if (substReplacement->isEqual(Replacement)) return *this; if (Conformance.empty()) { return {substReplacement, Conformance}; } bool conformancesChanged = false; SmallVector 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, subs, conformances); 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 conformance; // If the original type was an archetype, check the conformance map. if (Replacement->is() || Replacement->is()) { conformance = conformances(Replacement->getCanonicalType(), substReplacement, proto->getDeclaredType()); } // If that didn't find anything, we can still synthesize AnyObject // conformances from thin air. FIXME: gross. if (!conformance && proto->isSpecificProtocol(KnownProtocolKind::AnyObject)) { auto archetype = dyn_cast(substReplacement->getCanonicalType()); // If classDecl is not nullptr, it is a concrete class. auto classDecl = substReplacement->getClassOrBoundGenericClass(); if (!classDecl && archetype->getSuperclass()) { // Replacement type is an archetype with a superclass constraint. classDecl = archetype->getSuperclass()->getClassOrBoundGenericClass(); assert(classDecl); } if (classDecl) { // Create a concrete conformance based on the conforming class. SmallVector lookupResults; classDecl->lookupConformance(classDecl->getParentModule(), proto, lookupResults); conformance = ProtocolConformanceRef(lookupResults.front()); } else if (archetype && archetype->requiresClass()) { // Replacement type is an archetype with a class constraint. // Create an abstract conformance. conformance = ProtocolConformanceRef(proto); } } assert(conformance); if (conformance->isConcrete()) conformancesChanged = true; substConformances.push_back(*conformance); } assert(substConformances.size() == Conformance.size()); ArrayRef substConfs; if (conformancesChanged) substConfs = Replacement->getASTContext().AllocateCopy(substConformances); else substConfs = Conformance; return Substitution{substReplacement, substConfs}; }