Skip to content

Add a pass to specialize opaque type archetypes. #24224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
4 changes: 0 additions & 4 deletions include/swift/AST/ProtocolConformanceRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,6 @@ class ProtocolConformanceRef {
LookupConformanceFn conformances,
SubstOptions options = None) const;

/// Replace opaque types in the conforming type with their underlying types,
/// and resolve opaque conformances to their underlying conformances.
ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypes(Type origType) const;

/// Given a dependent type (expressed in terms of this conformance's
/// protocol), follow it from the conforming type.
Type getAssociatedType(Type origType, Type dependentType,
Expand Down
4 changes: 0 additions & 4 deletions include/swift/AST/SubstitutionMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,6 @@ class SubstitutionMap {
LookupConformanceFn conformances,
SubstOptions options = None) const;

/// Replace opaque types in the replacement types in the map with their
/// underlying types. Does not change keys.
SubstitutionMap substOpaqueTypesWithUnderlyingTypes() const;

/// Create a substitution map for a protocol conformance.
static SubstitutionMap
getProtocolSubstitutions(ProtocolDecl *protocol,
Expand Down
4 changes: 0 additions & 4 deletions include/swift/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,6 @@ class Type {
/// Replace references to substitutable types with error types.
Type substDependentTypesWithErrorTypes() const;

/// Replace opaque types with their underlying types when visible at the given
/// resilience expansion.
Type substOpaqueTypesWithUnderlyingTypes() const;

bool isPrivateStdlibType(bool treatNonBuiltinProtocolsAsPublic = true) const;

void dump() const;
Expand Down
19 changes: 14 additions & 5 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -4092,7 +4092,8 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
/// std::error_code. This is only meant to be used in assertions. When
/// assertions are disabled, this just returns true.
ABICompatibilityCheckResult
isABICompatibleWith(CanSILFunctionType other) const;
isABICompatibleWith(CanSILFunctionType other,
SILFunction *context = nullptr) const;

CanSILFunctionType substGenericArgs(SILModule &silModule,
SubstitutionMap subs);
Expand Down Expand Up @@ -4827,17 +4828,25 @@ END_CAN_TYPE_WRAPPER(OpaqueTypeArchetypeType, ArchetypeType)
/// to their underlying types.
class ReplaceOpaqueTypesWithUnderlyingTypes {
public:
ReplaceOpaqueTypesWithUnderlyingTypes() {}

SILFunction *context;
ReplaceOpaqueTypesWithUnderlyingTypes(
SILFunction *context)
: context(context) {}

/// TypeSubstitutionFn
Type operator()(SubstitutableType *maybeOpaqueType) const;

/// LookupConformanceFn
Optional<ProtocolConformanceRef> operator()(CanType maybeOpaqueType,
Type replacementType,
ProtocolDecl *protocol) const;

bool shouldPerformSubstitution(OpaqueTypeDecl *opaque) const;

static bool shouldPerformSubstitution(OpaqueTypeDecl *opaque,
SILFunction *context);
};

/// An archetype that represents the dynamic type of an opened existential.
class OpenedArchetypeType final : public ArchetypeType,
private ArchetypeTrailingObjects<OpenedArchetypeType>
Expand Down
9 changes: 6 additions & 3 deletions include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ class SILCloner : protected SILInstructionVisitor<ImplClass> {
/// after mapping all blocks.
void cloneReachableBlocks(SILBasicBlock *startBB,
ArrayRef<SILBasicBlock *> exitBlocks,
SILBasicBlock *insertAfterBB = nullptr);
SILBasicBlock *insertAfterBB = nullptr,
bool havePrepopulatedFunctionArgs = false);

/// Clone all blocks in this function and all instructions in those
/// blocks.
Expand Down Expand Up @@ -579,13 +580,15 @@ void SILCloner<ImplClass>::visitInstructionsInBlock(SILBasicBlock* BB) {
template <typename ImplClass>
void SILCloner<ImplClass>::cloneReachableBlocks(
SILBasicBlock *startBB, ArrayRef<SILBasicBlock *> exitBlocks,
SILBasicBlock *insertAfterBB) {
SILBasicBlock *insertAfterBB,
bool havePrepopulatedFunctionArgs) {

SILFunction *F = startBB->getParent();
assert(F == &Builder.getFunction()
&& "cannot clone region across functions.");
assert(BBMap.empty() && "This API does not allow clients to map blocks.");
assert(ValueMap.empty() && "Stale ValueMap.");
assert((havePrepopulatedFunctionArgs || ValueMap.empty()) &&
"Stale ValueMap.");

auto *clonedStartBB = insertAfterBB ? F->createBasicBlockAfter(insertAfterBB)
: F->createBasicBlock();
Expand Down
6 changes: 3 additions & 3 deletions include/swift/SIL/SILType.h
Original file line number Diff line number Diff line change
Expand Up @@ -444,10 +444,10 @@ class SILType {
///
/// If the replacement types are generic, you must push a generic context
/// first.
SILType subst(SILModule &silModule,
TypeSubstitutionFn subs,
SILType subst(SILModule &silModule, TypeSubstitutionFn subs,
LookupConformanceFn conformances,
CanGenericSignature genericSig=CanGenericSignature()) const;
CanGenericSignature genericSig = CanGenericSignature(),
bool shouldSubstituteOpaqueArchetypes = false) const;

SILType subst(SILModule &silModule, SubstitutionMap subs) const;

Expand Down
2 changes: 2 additions & 0 deletions include/swift/SILOptimizer/PassManager/Passes.def
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ PASS(NoReturnFolding, "noreturn-folding",
"Prune Control Flow at No-Return Calls Using SIL unreachable")
PASS(ObjectOutliner, "object-outliner",
"Outlining of Global Objects")
PASS(OpaqueArchetypeSpecializer, "opaque-archetype-specializer",
"Opaque archetype specializer")
PASS(Outliner, "outliner",
"Function Outlining Optimization")
PASS(OwnershipModelEliminator, "ownership-model-eliminator",
Expand Down
7 changes: 0 additions & 7 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,6 @@ ProtocolConformanceRef::subst(Type origType,
llvm_unreachable("Invalid conformance substitution");
}

ProtocolConformanceRef
ProtocolConformanceRef::substOpaqueTypesWithUnderlyingTypes(Type origType) const {
ReplaceOpaqueTypesWithUnderlyingTypes replacer;
return subst(origType, replacer, replacer,
SubstFlags::SubstituteOpaqueArchetypes);
}

Type
ProtocolConformanceRef::getTypeWitnessByName(Type type,
ProtocolConformanceRef conformance,
Expand Down
7 changes: 0 additions & 7 deletions lib/AST/SubstitutionMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,13 +492,6 @@ SubstitutionMap SubstitutionMap::subst(TypeSubstitutionFn subs,
return SubstitutionMap(genericSig, newSubs, newConformances);
}

SubstitutionMap
SubstitutionMap::substOpaqueTypesWithUnderlyingTypes()
const {
ReplaceOpaqueTypesWithUnderlyingTypes replacer;
return subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes);
}

SubstitutionMap
SubstitutionMap::getProtocolSubstitutions(ProtocolDecl *protocol,
Type selfType,
Expand Down
79 changes: 48 additions & 31 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2472,84 +2472,101 @@ getArchetypeAndRootOpaqueArchetype(Type maybeOpaqueType) {
return std::make_pair(archetype, opaqueRoot);
}

Type ReplaceOpaqueTypesWithUnderlyingTypes::operator()(
SubstitutableType *maybeOpaqueType) const {
bool ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution(
OpaqueTypeDecl *opaque) const {
return shouldPerformSubstitution(opaque, context);
}

static Type substOpaqueTypesWithUnderlyingTypes(
Type ty, SILFunction *context) {
ReplaceOpaqueTypesWithUnderlyingTypes replacer(context);
return ty.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes);
}

Type ReplaceOpaqueTypesWithUnderlyingTypes::
operator()(SubstitutableType *maybeOpaqueType) const {
auto archetypeAndRoot = getArchetypeAndRootOpaqueArchetype(maybeOpaqueType);
if (!archetypeAndRoot)
return maybeOpaqueType;

auto archetype = archetypeAndRoot->first;
auto opaqueRoot = archetypeAndRoot->second;

if (!shouldPerformSubstitution(opaqueRoot->getDecl())) {
return maybeOpaqueType;
}

auto subs = opaqueRoot->getDecl()->getUnderlyingTypeSubstitutions();
// TODO: Check the resilience expansion, and handle opaque types with
// unknown underlying types. For now, all opaque types are always
// fragile.
assert(subs.hasValue() && "resilient opaque types not yet supported");

// Apply the underlying type substitutions to the interface type of the
// archetype in question. This will map the inner generic signature of the
// opaque type to its outer signature.
auto partialSubstTy = archetype->getInterfaceType().subst(*subs);
// Then apply the substitutions from the root opaque archetype, to specialize
// for its type arguments.
auto substTy = partialSubstTy.subst(opaqueRoot->getSubstitutions());

// If the type still contains opaque types, recur.
if (substTy->hasOpaqueArchetype()) {
return substTy.substOpaqueTypesWithUnderlyingTypes();
return substOpaqueTypesWithUnderlyingTypes(substTy, context);
}
return substTy;
}

Optional<ProtocolConformanceRef>
ReplaceOpaqueTypesWithUnderlyingTypes::operator()(CanType maybeOpaqueType,
Type replacementType,
ProtocolDecl *protocol) const {
static ProtocolConformanceRef
substOpaqueTypesWithUnderlyingTypes(ProtocolConformanceRef ref, Type origType,
SILFunction *context) {
ReplaceOpaqueTypesWithUnderlyingTypes replacer(context);
return ref.subst(origType, replacer, replacer,
SubstFlags::SubstituteOpaqueArchetypes);
}

Optional<ProtocolConformanceRef> ReplaceOpaqueTypesWithUnderlyingTypes::
operator()(CanType maybeOpaqueType, Type replacementType,
ProtocolDecl *protocol) const {
auto abstractRef = ProtocolConformanceRef(protocol);

auto archetypeAndRoot = getArchetypeAndRootOpaqueArchetype(maybeOpaqueType);
if (!archetypeAndRoot) {
assert(maybeOpaqueType->isTypeParameter()
|| maybeOpaqueType->is<ArchetypeType>());
assert(maybeOpaqueType->isTypeParameter() ||
maybeOpaqueType->is<ArchetypeType>());
return abstractRef;
}

auto archetype = archetypeAndRoot->first;
auto opaqueRoot = archetypeAndRoot->second;

if (!shouldPerformSubstitution(opaqueRoot->getDecl())) {
return abstractRef;
}

auto subs = opaqueRoot->getDecl()->getUnderlyingTypeSubstitutions();
assert(subs.hasValue());

// Apply the underlying type substitutions to the interface type of the
// archetype in question. This will map the inner generic signature of the
// opaque type to its outer signature.
auto partialSubstTy = archetype->getInterfaceType().subst(*subs);
auto partialSubstRef = abstractRef.subst(archetype->getInterfaceType(),
*subs);
auto partialSubstRef =
abstractRef.subst(archetype->getInterfaceType(), *subs);

// Then apply the substitutions from the root opaque archetype, to specialize
// for its type arguments.
auto substTy = partialSubstTy.subst(opaqueRoot->getSubstitutions());
auto substRef = partialSubstRef.subst(partialSubstTy,
opaqueRoot->getSubstitutions());
auto substRef =
partialSubstRef.subst(partialSubstTy, opaqueRoot->getSubstitutions());

// If the type still contains opaque types, recur.
if (substTy->hasOpaqueArchetype()) {
return substRef.substOpaqueTypesWithUnderlyingTypes(substTy);
return substOpaqueTypesWithUnderlyingTypes(substRef, substTy, context);
}
return substRef;
}

Type Type::substOpaqueTypesWithUnderlyingTypes() const {
ReplaceOpaqueTypesWithUnderlyingTypes replacer;
return subst(replacer, replacer,
SubstFlags::SubstituteOpaqueArchetypes
// TODO(opaque): Currently lowered types always get opaque types
// substituted out of them. When we support opaque type resilience
// this won't be true anymore and we'll need to handle
// opaque type substitution in SILType::subst.
| SubstFlags::AllowLoweredTypes);
}

CanNestedArchetypeType NestedArchetypeType::getNew(
const ASTContext &Ctx,
ArchetypeType *Parent,
Expand Down
Loading