Skip to content

Start of Type::subst() rework #75875

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
merged 6 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/swift/AST/ASTMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ class ASTMangler : public Mangler {

void appendClosureEntity(const AbstractClosureExpr *closure);

void appendClosureComponents(Type Ty, unsigned discriminator, bool isImplicit,
void appendClosureComponents(CanType Ty, unsigned discriminator, bool isImplicit,
const DeclContext *parentContext,
ArrayRef<GenericEnvironment *> capturedEnvs);

Expand Down
55 changes: 32 additions & 23 deletions include/swift/AST/TypeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -367,30 +367,13 @@ case TypeKind::Id:

case TypeKind::OpaqueTypeArchetype: {
auto opaque = cast<OpaqueTypeArchetypeType>(base);
if (opaque->getSubstitutions().empty())
auto subMap = opaque->getSubstitutions();
auto newSubMap = asDerived().transformSubMap(subMap);
if (newSubMap == subMap)
return t;

SmallVector<Type, 4> newSubs;
bool anyChanged = false;
for (auto replacement : opaque->getSubstitutions().getReplacementTypes()) {
Type newReplacement = doIt(replacement, TypePosition::Invariant);
if (!newReplacement)
return Type();
newSubs.push_back(newReplacement);
if (replacement.getPointer() != newReplacement.getPointer())
anyChanged = true;
}

if (!anyChanged)
return t;

// FIXME: This re-looks-up conformances instead of transforming them in
// a systematic way.
auto sig = opaque->getDecl()->getGenericSignature();
auto newSubMap =
SubstitutionMap::get(sig,
QueryReplacementTypeArray{sig, newSubs},
LookUpConformanceInModule());
if (!newSubMap)
return Type();

return OpaqueTypeArchetypeType::get(opaque->getDecl(),
opaque->getInterfaceType(),
newSubMap);
Expand Down Expand Up @@ -999,6 +982,32 @@ case TypeKind::Id:

llvm_unreachable("Unhandled type in transformation");
}

// If original was non-empty and transformed is empty, we're
// signaling failure, that is, a Type() return from doIt().
SubstitutionMap transformSubMap(SubstitutionMap subs) {
if (subs.empty())
return subs;

SmallVector<Type, 4> newSubs;
bool anyChanged = false;
for (auto replacement : subs.getReplacementTypes()) {
Type newReplacement = doIt(replacement, TypePosition::Invariant);
if (!newReplacement)
return SubstitutionMap();
newSubs.push_back(newReplacement);
if (replacement.getPointer() != newReplacement.getPointer())
anyChanged = true;
}

if (!anyChanged)
return subs;

auto sig = subs.getGenericSignature();
return SubstitutionMap::get(sig,
QueryReplacementTypeArray{sig, newSubs},
LookUpConformanceInModule());
}
};

}
Expand Down
19 changes: 11 additions & 8 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3752,10 +3752,12 @@ void ASTMangler::appendAssociatedTypeName(DependentMemberType *dmt,

void ASTMangler::appendClosureEntity(
const SerializedAbstractClosureExpr *closure) {
assert(!closure->getType()->hasLocalArchetype() &&
auto canType = closure->getType()->getCanonicalType();
assert(!canType->hasLocalArchetype() &&
"Not enough information here to handle this case");

appendClosureComponents(closure->getType(), closure->getDiscriminator(),
appendClosureComponents(canType,
closure->getDiscriminator(),
closure->isImplicit(), closure->getParent(),
ArrayRef<GenericEnvironment *>());
}
Expand All @@ -3769,17 +3771,18 @@ void ASTMangler::appendClosureEntity(const AbstractClosureExpr *closure) {
// code; the type-checker currently isn't strict about producing typed
// expression nodes when it fails. Once we enforce that, we can remove this.
if (!type)
type = ErrorType::get(closure->getASTContext());
type = CanType(ErrorType::get(closure->getASTContext()));

if (type->hasLocalArchetype())
auto canType = type->getCanonicalType();
if (canType->hasLocalArchetype())
capturedEnvs = closure->getCaptureInfo().getGenericEnvironments();

appendClosureComponents(type, closure->getDiscriminator(),
appendClosureComponents(canType, closure->getDiscriminator(),
isa<AutoClosureExpr>(closure), closure->getParent(),
capturedEnvs);
}

void ASTMangler::appendClosureComponents(Type Ty, unsigned discriminator,
void ASTMangler::appendClosureComponents(CanType Ty, unsigned discriminator,
bool isImplicit,
const DeclContext *parentContext,
ArrayRef<GenericEnvironment *> capturedEnvs) {
Expand All @@ -3793,9 +3796,9 @@ void ASTMangler::appendClosureComponents(Type Ty, unsigned discriminator,

Ty = Ty.subst(MapLocalArchetypesOutOfContext(Sig, capturedEnvs),
MakeAbstractConformanceForGenericType(),
SubstFlags::PreservePackExpansionLevel);
SubstFlags::PreservePackExpansionLevel)->getCanonicalType();

appendType(Ty->getCanonicalType(), Sig);
appendType(Ty, Sig);
appendOperator(isImplicit ? "fu" : "fU", Index(discriminator));
}

Expand Down
55 changes: 26 additions & 29 deletions lib/AST/TypeSubstitution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,8 @@ class TypeSubstituter : public TypeTransform<TypeSubstituter> {
: level(level), IFS(IFS) {}

std::optional<Type> transform(TypeBase *type, TypePosition position);

SubstitutionMap transformSubstitutionMap(SubstitutionMap subs);
};

}
Expand Down Expand Up @@ -624,6 +626,11 @@ TypeSubstituter::transform(TypeBase *type, TypePosition position) {
level);
}

SubstitutionMap TypeSubstituter::transformSubstitutionMap(SubstitutionMap subs) {
// FIXME: Take level into account? Move level down into IFS?
return subs.subst(IFS);
}

Type Type::subst(SubstitutionMap substitutions,
SubstOptions options) const {
InFlightSubstitutionViaSubMap IFS(substitutions, options);
Expand Down Expand Up @@ -1038,18 +1045,6 @@ Type TypeBase::adjustSuperclassMemberDeclType(const ValueDecl *baseDecl,
// Replacing opaque result archetypes with their underlying types
//===----------------------------------------------------------------------===//

static std::optional<std::pair<ArchetypeType *, OpaqueTypeArchetypeType *>>
getArchetypeAndRootOpaqueArchetype(Type maybeOpaqueType) {
auto archetype = dyn_cast<ArchetypeType>(maybeOpaqueType.getPointer());
if (!archetype)
return std::nullopt;
auto opaqueRoot = dyn_cast<OpaqueTypeArchetypeType>(archetype->getRoot());
if (!opaqueRoot)
return std::nullopt;

return std::make_pair(archetype, opaqueRoot);
}

OpaqueSubstitutionKind
ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution(
OpaqueTypeDecl *opaque) const {
Expand Down Expand Up @@ -1187,19 +1182,20 @@ ReplaceOpaqueTypesWithUnderlyingTypes::ReplaceOpaqueTypesWithUnderlyingTypes(

Type ReplaceOpaqueTypesWithUnderlyingTypes::
operator()(SubstitutableType *maybeOpaqueType) const {
auto archetypeAndRoot = getArchetypeAndRootOpaqueArchetype(maybeOpaqueType);
if (!archetypeAndRoot)
auto *archetype = dyn_cast<OpaqueTypeArchetypeType>(maybeOpaqueType);
if (!archetype)
return maybeOpaqueType;

auto archetype = archetypeAndRoot->first;
auto opaqueRoot = archetypeAndRoot->second;
auto *genericEnv = archetype->getGenericEnvironment();
auto *decl = genericEnv->getOpaqueTypeDecl();
auto outerSubs = genericEnv->getOpaqueSubstitutions();

auto substitutionKind = shouldPerformSubstitution(opaqueRoot->getDecl());
auto substitutionKind = shouldPerformSubstitution(decl);
if (substitutionKind == OpaqueSubstitutionKind::DontSubstitute) {
return maybeOpaqueType;
}

auto subs = opaqueRoot->getDecl()->getUniqueUnderlyingTypeSubstitutions();
auto subs = decl->getUniqueUnderlyingTypeSubstitutions();
// If the body of the opaque decl providing decl has not been type checked we
// don't have a underlying substitution.
if (!subs.has_value())
Expand Down Expand Up @@ -1231,11 +1227,11 @@ operator()(SubstitutableType *maybeOpaqueType) const {
// for its type arguments. We perform this substitution after checking for
// visibility, since we do not want the result of the visibility check to
// depend on the substitutions previously applied.
auto substTy = partialSubstTy.subst(opaqueRoot->getSubstitutions());
auto substTy = partialSubstTy.subst(outerSubs);

// If the type changed, but still contains opaque types, recur.
if (!substTy->isEqual(maybeOpaqueType) && substTy->hasOpaqueArchetype()) {
SeenDecl seenKey(opaqueRoot->getDecl(), opaqueRoot->getSubstitutions());
SeenDecl seenKey(decl, outerSubs);
if (auto *alreadySeen = this->seenDecls) {
// Detect substitution loops. If we find one, just bounce the original
// type back to the caller. This substitution will fail at runtime
Expand Down Expand Up @@ -1305,8 +1301,8 @@ operator()(CanType maybeOpaqueType, Type replacementType,
ProtocolDecl *protocol) const {
auto abstractRef = ProtocolConformanceRef(protocol);

auto archetypeAndRoot = getArchetypeAndRootOpaqueArchetype(maybeOpaqueType);
if (!archetypeAndRoot) {
auto archetype = dyn_cast<OpaqueTypeArchetypeType>(maybeOpaqueType);
if (!archetype) {
if (maybeOpaqueType->isTypeParameter() ||
maybeOpaqueType->is<ArchetypeType>())
return abstractRef;
Expand All @@ -1320,15 +1316,16 @@ operator()(CanType maybeOpaqueType, Type replacementType,
llvm_unreachable("origType should have been an opaque type or type parameter");
}

auto archetype = archetypeAndRoot->first;
auto opaqueRoot = archetypeAndRoot->second;
auto *genericEnv = archetype->getGenericEnvironment();
auto *decl = genericEnv->getOpaqueTypeDecl();
auto outerSubs = genericEnv->getOpaqueSubstitutions();

auto substitutionKind = shouldPerformSubstitution(opaqueRoot->getDecl());
auto substitutionKind = shouldPerformSubstitution(decl);
if (substitutionKind == OpaqueSubstitutionKind::DontSubstitute) {
return abstractRef;
}

auto subs = opaqueRoot->getDecl()->getUniqueUnderlyingTypeSubstitutions();
auto subs = decl->getUniqueUnderlyingTypeSubstitutions();
// If the body of the opaque decl providing decl has not been type checked we
// don't have a underlying substitution.
if (!subs.has_value())
Expand Down Expand Up @@ -1359,16 +1356,16 @@ operator()(CanType maybeOpaqueType, Type replacementType,
// for its type arguments. We perform this substitution after checking for
// visibility, since we do not want the result of the visibility check to
// depend on the substitutions previously applied.
auto substTy = partialSubstTy.subst(opaqueRoot->getSubstitutions());
auto substTy = partialSubstTy.subst(outerSubs);

auto partialSubstRef =
abstractRef.subst(archetype->getInterfaceType(), *subs);
auto substRef =
partialSubstRef.subst(partialSubstTy, opaqueRoot->getSubstitutions());
partialSubstRef.subst(partialSubstTy, outerSubs);

// If the type still contains opaque types, recur.
if (substTy->hasOpaqueArchetype()) {
SeenDecl seenKey(opaqueRoot->getDecl(), opaqueRoot->getSubstitutions());
SeenDecl seenKey(decl, outerSubs);

if (auto *alreadySeen = this->seenDecls) {
// Detect substitution loops. If we find one, just bounce the original
Expand Down
8 changes: 2 additions & 6 deletions lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1764,11 +1764,7 @@ void WitnessTableBuilderBase::defineAssociatedTypeWitnessTableAccessFunction(
CanType associatedType,
ProtocolConformanceRef associatedConformance) {
bool hasArchetype = associatedType->hasArchetype();
OpaqueTypeArchetypeType *associatedRootOpaqueType = nullptr;
if (auto assocArchetype = dyn_cast<ArchetypeType>(associatedType)) {
associatedRootOpaqueType = dyn_cast<OpaqueTypeArchetypeType>(
assocArchetype->getRoot());
}
bool isOpaqueArchetype = isa<OpaqueTypeArchetypeType>(associatedType);

assert(isa<NormalProtocolConformance>(Conformance) && "has associated type");

Expand Down Expand Up @@ -1827,7 +1823,7 @@ void WitnessTableBuilderBase::defineAssociatedTypeWitnessTableAccessFunction(
}

// If there are no archetypes, return a reference to the table.
if (!hasArchetype && !associatedRootOpaqueType) {
if (!hasArchetype && !isOpaqueArchetype) {
auto wtable = conformanceI->getTable(IGF, &associatedTypeMetadata);
IGF.Builder.CreateRet(wtable);
return;
Expand Down
10 changes: 5 additions & 5 deletions lib/SIL/IR/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2012,22 +2012,22 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function,

auto options = SILParameterInfo::Options();

Type type;
CanType type;
VarDecl *varDecl = nullptr;
if (auto *expr = capture.getPackElement()) {
type = expr->getType();
type = expr->getType()->getCanonicalType();
} else {
varDecl = cast<VarDecl>(capture.getDecl());
type = varDecl->getTypeInContext();
type = varDecl->getTypeInContext()->getCanonicalType();

// If we're capturing a parameter pack, wrap it in a tuple.
if (type->is<PackExpansionType>()) {
if (isa<PackExpansionType>(type)) {
assert(!cast<ParamDecl>(varDecl)->supportsMutation() &&
"Cannot capture a pack as an lvalue");

SmallVector<TupleTypeElt, 1> elts;
elts.push_back(type);
type = TupleType::get(elts, TC.Context);
type = CanType(TupleType::get(elts, TC.Context));
}

if (isolatedParam == varDecl) {
Expand Down
27 changes: 27 additions & 0 deletions test/SILGen/phantom_existential_typealias.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// RUN: %target-swift-emit-silgen %s | %FileCheck %s

struct S {
var y: Int { fatalError() }
}

struct G<Element> {
func f<T>(_: (Element) -> T) -> [T] { fatalError() }
}

protocol P {
typealias A = S
var x: G<A> { get }
}

// CHECK-LABEL: sil private [ossa] @$s29phantom_existential_typealias5test11pSaySiGAA1P_p_tFSiAA1SVXEfU_ : $@convention(thin) @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <S, Int> {

func test1(p: any P) -> [Int] {
return p.x.f { $0.y }
}

func callee(_: () -> ()) {}

func test2(p: any P) {
let a = p.x
callee { _ = a }
}