Skip to content
179 changes: 179 additions & 0 deletions include/swift/AST/InFlightSubstitution.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
//===--- InFlightSubstitution.h - In-flight substitution data ---*- C++ -*-===//
//
// 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 defines the InFlightSubstitution structure, which captures
// all the information about a type substitution that's currently in
// progress. For now, this is meant to be an internal implementation
// detail of the substitution system, and other systems should not use
// it (unless they are part of the extended substitution system, such as
// the SIL type substituter)
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_AST_INFLIGHTSUBSTITUTION_H
#define SWIFT_AST_INFLIGHTSUBSTITUTION_H

#include "swift/AST/SubstitutionMap.h"

namespace swift {
class SubstitutionMap;

class InFlightSubstitution {
SubstOptions Options;
TypeSubstitutionFn BaselineSubstType;
LookupConformanceFn BaselineLookupConformance;

struct ActivePackExpansion {
bool isSubstExpansion = false;
unsigned expansionIndex = 0;
};
SmallVector<ActivePackExpansion, 4> ActivePackExpansions;

public:
InFlightSubstitution(TypeSubstitutionFn substType,
LookupConformanceFn lookupConformance,
SubstOptions options)
: Options(options),
BaselineSubstType(substType),
BaselineLookupConformance(lookupConformance) {}

InFlightSubstitution(const InFlightSubstitution &) = delete;
InFlightSubstitution &operator=(const InFlightSubstitution &) = delete;

// TODO: when we add PackElementType, we should recognize it during
// substitution and either call different methods on this class or
// pass an extra argument for the pack-expansion depth D. We should
// be able to rely on that to mark a pack-element reference instead
// of checking whether the original type was a pack. Substitution
// should use the D'th entry from the end of ActivePackExpansions to
// guide the element substitution:
// - project the given index of the pack substitution
// - wrap it in a PackElementType if it's a subst expansion
// - the depth of that PackElementType is the number of subst
// expansions between the depth entry and the end of
// ActivePackExpansions

/// Perform primitive substitution on the given type. Returns Type()
/// if the type should not be substituted as a whole.
Type substType(SubstitutableType *origType);

/// Perform primitive conformance lookup on the given type.
ProtocolConformanceRef lookupConformance(CanType dependentType,
Type conformingReplacementType,
ProtocolDecl *conformedProtocol);

/// Given the shape type of a pack expansion, invoke the given callback
/// for each expanded component of it. If the substituted component
/// is an expansion component, the desired shape of that expansion
/// is passed as the argument; otherwise, the argument is Type().
/// In either case, an active expansion is entered on this IFS for
/// the duration of the call to handleComponent, and subsequent
/// pack-element type references will substitute to the corresponding
/// element of the substitution of the pack.
void expandPackExpansionShape(Type origShape,
llvm::function_ref<void(Type substComponentShape)> handleComponent);

/// Call the given function for each expanded component type of the
/// given pack expansion type. The function will be invoked with the
/// active expansion still active.
void expandPackExpansionType(PackExpansionType *origExpansionType,
llvm::function_ref<void(Type substType)> handleComponentType) {
expandPackExpansionShape(origExpansionType->getCountType(),
[&](Type substComponentShape) {
auto origPatternType = origExpansionType->getPatternType();
auto substEltType = origPatternType.subst(*this);

auto substComponentType =
(substComponentShape
? PackExpansionType::get(substEltType, substComponentShape)
: substEltType);
handleComponentType(substComponentType);
});
}

/// Return a list of component types that the pack expansion expands to.
SmallVector<Type, 8>
expandPackExpansionType(PackExpansionType *origExpansionType) {
SmallVector<Type, 8> substComponentTypes;
expandPackExpansionType(origExpansionType, substComponentTypes);
return substComponentTypes;
}

/// Expand the list of component types that the pack expansion expands
/// to into the given array.
void expandPackExpansionType(PackExpansionType *origExpansionType,
SmallVectorImpl<Type> &substComponentTypes) {
expandPackExpansionType(origExpansionType, [&](Type substComponentType) {
substComponentTypes.push_back(substComponentType);
});
}

class OptionsAdjustmentScope {
InFlightSubstitution &IFS;
SubstOptions SavedOptions;

public:
OptionsAdjustmentScope(InFlightSubstitution &IFS, SubstOptions newOptions)
: IFS(IFS), SavedOptions(IFS.Options) {
IFS.Options = newOptions;
}

OptionsAdjustmentScope(const OptionsAdjustmentScope &) = delete;
OptionsAdjustmentScope &operator=(const OptionsAdjustmentScope &) = delete;

~OptionsAdjustmentScope() {
IFS.Options = SavedOptions;
}
};

template <class Fn>
auto withNewOptions(SubstOptions options, Fn &&fn)
-> decltype(std::forward<Fn>(fn)()) {
OptionsAdjustmentScope scope(*this, options);
return std::forward<Fn>(fn)();
}

SubstOptions getOptions() const {
return Options;
}

bool shouldSubstituteOpaqueArchetypes() const {
return Options.contains(SubstFlags::SubstituteOpaqueArchetypes);
}

/// Is the given type invariant to substitution?
bool isInvariant(Type type) const;
};

/// A helper classes that provides stable storage for the query
/// functions against a SubstitutionMap.
struct InFlightSubstitutionViaSubMapHelper {
QuerySubstitutionMap QueryType;
LookUpConformanceInSubstitutionMap QueryConformance;

InFlightSubstitutionViaSubMapHelper(SubstitutionMap subMap)
: QueryType{subMap}, QueryConformance(subMap) {}
};
class InFlightSubstitutionViaSubMap :
private InFlightSubstitutionViaSubMapHelper,
public InFlightSubstitution {

public:
InFlightSubstitutionViaSubMap(SubstitutionMap subMap,
SubstOptions options)
: InFlightSubstitutionViaSubMapHelper(subMap),
InFlightSubstitution(QueryType, QueryConformance, options) {}
};

} // end namespace swift

#endif
7 changes: 7 additions & 0 deletions include/swift/AST/PackConformance.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ class alignas(1 << DeclAlignInBits) PackConformance final
LookupConformanceFn conformances,
SubstOptions options=None) const;

/// Apply an in-flight substitution to the conformances in this
/// protocol conformance ref.
///
/// This function should generally not be used outside of the
/// substitution subsystem.
ProtocolConformanceRef subst(InFlightSubstitution &IFS) const;

SWIFT_DEBUG_DUMP;
void dump(llvm::raw_ostream &out, unsigned indent = 0) const;
};
Expand Down
7 changes: 7 additions & 0 deletions include/swift/AST/ProtocolConformance.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,13 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
LookupConformanceFn conformances,
SubstOptions options=None) const;

/// Substitute the conforming type and produce a ProtocolConformance that
/// applies to the substituted type.
///
/// This function should generally not be used outside of the substitution
/// subsystem.
ProtocolConformance *subst(InFlightSubstitution &IFS) const;

SWIFT_DEBUG_DUMP;
void dump(llvm::raw_ostream &out, unsigned indent = 0) const;
};
Expand Down
7 changes: 7 additions & 0 deletions include/swift/AST/ProtocolConformanceRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ class ProtocolConformanceRef {
LookupConformanceFn conformances,
SubstOptions options=None) const;

/// Apply a substitution to the conforming type.
///
/// This function should generally not be used outside of the substitution
/// subsystem.
ProtocolConformanceRef subst(Type origType,
InFlightSubstitution &IFS) const;

/// Map contextual types to interface types in the conformance.
ProtocolConformanceRef mapConformanceOutOfContext() const;

Expand Down
15 changes: 15 additions & 0 deletions include/swift/AST/SubstitutionMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ class SubstitutionMap {
TypeSubstitutionFn subs,
LookupConformanceFn lookupConformance);

/// Build a substitution map from the substitutions represented by
/// the given in-flight substitution.
///
/// This function should generally only be used by the substitution
/// subsystem.
static SubstitutionMap get(GenericSignature genericSig,
InFlightSubstitution &IFS);

/// Retrieve the generic signature describing the environment in which
/// substitutions occur.
GenericSignature getGenericSignature() const;
Expand Down Expand Up @@ -182,6 +190,13 @@ class SubstitutionMap {
LookupConformanceFn conformances,
SubstOptions options=None) const;

/// Apply an in-flight substitution to all replacement types in the map.
/// Does not change keys.
///
/// This should generally not be used outside of the substitution
/// subsystem.
SubstitutionMap subst(InFlightSubstitution &subs) const;

/// Apply type expansion lowering to all types in the substitution map. Opaque
/// archetypes will be lowered to their underlying types if the type expansion
/// context allows.
Expand Down
7 changes: 7 additions & 0 deletions include/swift/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class ClassDecl;
class CanType;
class EnumDecl;
class GenericSignatureImpl;
class InFlightSubstitution;
class ModuleDecl;
class NominalTypeDecl;
class GenericTypeDecl;
Expand Down Expand Up @@ -343,6 +344,12 @@ class Type {
LookupConformanceFn conformances,
SubstOptions options=None) const;

/// Apply an in-flight substitution to this type.
///
/// This should generally not be used outside of the substitution
/// subsystem.
Type subst(InFlightSubstitution &subs) const;

bool isPrivateStdlibType(bool treatNonBuiltinProtocolsAsPublic = true) const;

SWIFT_DEBUG_DUMP;
Expand Down
11 changes: 3 additions & 8 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -2449,8 +2449,6 @@ class TupleType final : public TypeBase, public llvm::FoldingSetNode,

bool containsPackExpansionType() const;

Type flattenPackTypes();

private:
TupleType(ArrayRef<TupleTypeElt> elements, const ASTContext *CanCtx,
RecursiveTypeProperties properties)
Expand Down Expand Up @@ -3520,8 +3518,6 @@ class AnyFunctionType : public TypeBase {

static bool containsPackExpansionType(ArrayRef<Param> params);

AnyFunctionType *flattenPackTypes();

static void printParams(ArrayRef<Param> Params, raw_ostream &OS,
const PrintOptions &PO = PrintOptions());
static void printParams(ArrayRef<Param> Params, ASTPrinter &Printer,
Expand Down Expand Up @@ -5168,6 +5164,9 @@ class SILFunctionType final
TypeSubstitutionFn subs,
LookupConformanceFn conformances,
TypeExpansionContext context);
CanSILFunctionType substGenericArgs(SILModule &silModule,
InFlightSubstitution &IFS,
TypeExpansionContext context);
CanSILFunctionType substituteOpaqueArchetypes(Lowering::TypeConverter &TC,
TypeExpansionContext context);

Expand Down Expand Up @@ -6839,8 +6838,6 @@ class PackType final : public TypeBase, public llvm::FoldingSetNode,

bool containsPackExpansionType() const;

PackType *flattenPackTypes();

CanTypeWrapper<PackType> getReducedShape();

public:
Expand Down Expand Up @@ -6935,8 +6932,6 @@ class PackExpansionType : public TypeBase, public llvm::FoldingSetNode {
/// Retrieves the count type of this pack expansion.
Type getCountType() const { return countType; }

PackExpansionType *expand();

CanType getReducedShape();

public:
Expand Down
4 changes: 4 additions & 0 deletions include/swift/SIL/SILType.h
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,10 @@ class SILType {
CanGenericSignature genericSig = CanGenericSignature(),
bool shouldSubstituteOpaqueArchetypes = false) const;

SILType subst(Lowering::TypeConverter &tc,
InFlightSubstitution &IFS,
CanGenericSignature genericSig) const;

SILType subst(Lowering::TypeConverter &tc, SubstitutionMap subs) const;

SILType subst(SILModule &M, SubstitutionMap subs) const;
Expand Down
19 changes: 18 additions & 1 deletion lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3257,6 +3257,12 @@ CanPackExpansionType::get(CanType patternType, CanType countType) {
}

PackExpansionType *PackExpansionType::get(Type patternType, Type countType) {
assert(!patternType->is<PackExpansionType>());
assert(!countType->is<PackExpansionType>());
// FIXME: stop doing this deliberately in PackExpansionMatcher
//assert(!patternType->is<PackType>());
//assert(!countType->is<PackType>());

auto properties = patternType->getRecursiveProperties();
properties |= countType->getRecursiveProperties();

Expand All @@ -3272,8 +3278,19 @@ PackExpansionType *PackExpansionType::get(Type patternType, Type countType) {
.PackExpansionTypes.FindNodeOrInsertPos(id, insertPos))
return expType;

// The canonical pack expansion type uses the canonical shape.
// For interface types, we'd need a signature to do this properly,
// but for archetypes we can do it directly.
bool countIsCanonical = countType->isCanonical();
if (countIsCanonical) {
if (auto archetype = dyn_cast<PackArchetypeType>(countType.getPointer())) {
auto reducedShape = archetype->getReducedShape();
countIsCanonical = (reducedShape.getPointer() == archetype);
}
}

const ASTContext *canCtx =
(patternType->isCanonical() && countType->isCanonical())
(patternType->isCanonical() && countIsCanonical)
? &context : nullptr;
PackExpansionType *expansionType =
new (context, arena) PackExpansionType(patternType, countType, properties,
Expand Down
Loading