Skip to content

Archetypes now store an interface type #12329

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
63 changes: 1 addition & 62 deletions include/swift/AST/GenericEnvironment.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,37 +37,17 @@ class SILType;
/// Describes the mapping between archetypes and interface types for the
/// generic parameters of a DeclContext.
class alignas(1 << DeclAlignInBits) GenericEnvironment final
: private llvm::TrailingObjects<GenericEnvironment, Type,
std::pair<ArchetypeType *,
GenericTypeParamType *>> {
: private llvm::TrailingObjects<GenericEnvironment, Type> {
GenericSignature *Signature = nullptr;
GenericSignatureBuilder *Builder = nullptr;
DeclContext *OwningDC = nullptr;

// The number of generic type parameter -> context type mappings we have
// recorded so far. This saturates at the number of generic type parameters,
// at which point the archetype-to-interface trailing array is sorted.
unsigned NumMappingsRecorded : 16;

// The number of archetype-to-interface type mappings. This is always <=
// \c NumMappingsRecorded.
unsigned NumArchetypeToInterfaceMappings : 16;

friend TrailingObjects;

/// An entry in the array mapping from archetypes to their corresponding
/// generic type parameters.
typedef std::pair<ArchetypeType *, GenericTypeParamType *>
ArchetypeToInterfaceMapping;

size_t numTrailingObjects(OverloadToken<Type>) const {
return Signature->getGenericParams().size();
}

size_t numTrailingObjects(OverloadToken<ArchetypeToInterfaceMapping>) const {
return Signature->getGenericParams().size();
}

/// Retrieve the array containing the context types associated with the
/// generic parameters, stored in parallel with the generic parameters of the
/// generic signature.
Expand All @@ -84,30 +64,6 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final
Signature->getGenericParams().size());
}

/// Retrieve the active set of archetype-to-interface mappings.
ArrayRef<ArchetypeToInterfaceMapping>
getActiveArchetypeToInterfaceMappings() const {
return { getTrailingObjects<ArchetypeToInterfaceMapping>(),
NumArchetypeToInterfaceMappings };
}

/// Retrieve the active set of archetype-to-interface mappings.
MutableArrayRef<ArchetypeToInterfaceMapping>
getActiveArchetypeToInterfaceMappings() {
return { getTrailingObjects<ArchetypeToInterfaceMapping>(),
NumArchetypeToInterfaceMappings };
}

/// Retrieve the buffer for the archetype-to-interface mappings.
///
/// Only the first \c NumArchetypeToInterfaceMappings elements in the buffer
/// are valid.
MutableArrayRef<ArchetypeToInterfaceMapping>
getArchetypeToInterfaceMappingsBuffer() {
return { getTrailingObjects<ArchetypeToInterfaceMapping>(),
Signature->getGenericParams().size() };
}

GenericEnvironment(GenericSignature *signature,
GenericSignatureBuilder *builder);

Expand All @@ -129,19 +85,6 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final
};
friend class QueryInterfaceTypeSubstitutions;

/// Query function suitable for use as a \c TypeSubstitutionFn that queries
/// the mapping of archetypes back to interface types.
class QueryArchetypeToInterfaceSubstitutions {
const GenericEnvironment *self;

public:
QueryArchetypeToInterfaceSubstitutions(const GenericEnvironment *self)
: self(self) { }

Type operator()(SubstitutableType *type) const;
};
friend class QueryArchetypeToInterfaceSubstitutions;

public:
GenericSignature *getGenericSignature() const {
return Signature;
Expand All @@ -151,10 +94,6 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final
return Signature->getGenericParams();
}

/// Determine whether this generic environment contains the given
/// primary archetype.
bool containsPrimaryArchetype(ArchetypeType *archetype) const;

/// Create a new, "incomplete" generic environment that will be populated
/// by calls to \c addMapping().
static
Expand Down
18 changes: 10 additions & 8 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -4041,7 +4041,7 @@ class ArchetypeType final : public SubstitutableType,

llvm::PointerUnion3<ArchetypeType *, TypeBase *,
GenericEnvironment *> ParentOrOpenedOrEnvironment;
llvm::PointerUnion<AssociatedTypeDecl *, Identifier> AssocTypeOrName;
Type InterfaceType;
MutableArrayRef<std::pair<Identifier, Type>> NestedTypes;

void populateNestedTypes() const;
Expand All @@ -4053,7 +4053,7 @@ class ArchetypeType final : public SubstitutableType,
/// The ConformsTo array will be copied into the ASTContext by this routine.
static CanTypeWrapper<ArchetypeType>
getNew(const ASTContext &Ctx, ArchetypeType *Parent,
AssociatedTypeDecl *AssocType,
DependentMemberType *InterfaceType,
SmallVectorImpl<ProtocolDecl *> &ConformsTo,
Type Superclass, LayoutConstraint Layout);

Expand All @@ -4063,8 +4063,8 @@ class ArchetypeType final : public SubstitutableType,
/// by this routine.
static CanTypeWrapper<ArchetypeType>
getNew(const ASTContext &Ctx,
GenericEnvironment *genericEnvironment,
Identifier Name,
GenericEnvironment *GenericEnv,
GenericTypeParamType *InterfaceType,
SmallVectorImpl<ProtocolDecl *> &ConformsTo,
Type Superclass, LayoutConstraint Layout);

Expand Down Expand Up @@ -4108,15 +4108,17 @@ class ArchetypeType final : public SubstitutableType,
/// Note: opened archetypes currently don't have generic environments.
GenericEnvironment *getGenericEnvironment() const;

/// Retrieve the interface type of this associated type, which will either
/// be a GenericTypeParamType or a DependentMemberType.
Type getInterfaceType() const { return InterfaceType; }

/// Retrieve the associated type to which this archetype (if it is a nested
/// archetype) corresponds.
///
/// This associated type will have the same name as the archetype and will
/// be a member of one of the protocols to which the parent archetype
/// conforms.
AssociatedTypeDecl *getAssocType() const {
return AssocTypeOrName.dyn_cast<AssociatedTypeDecl *>();
}
AssociatedTypeDecl *getAssocType() const;

/// getConformsTo - Retrieve the set of protocols to which this substitutable
/// type shall conform.
Expand Down Expand Up @@ -4224,7 +4226,7 @@ class ArchetypeType final : public SubstitutableType,
const ASTContext &Ctx,
llvm::PointerUnion<ArchetypeType *, GenericEnvironment *>
ParentOrGenericEnv,
llvm::PointerUnion<AssociatedTypeDecl *, Identifier> AssocTypeOrName,
Type InterfaceType,
ArrayRef<ProtocolDecl *> ConformsTo,
Type Superclass, LayoutConstraint Layout);

Expand Down
3 changes: 1 addition & 2 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4055,8 +4055,7 @@ GenericEnvironment *GenericEnvironment::getIncomplete(

// Allocate and construct the new environment.
unsigned numGenericParams = signature->getGenericParams().size();
size_t bytes = totalSizeToAlloc<Type, ArchetypeToInterfaceMapping>(
numGenericParams, numGenericParams);
size_t bytes = totalSizeToAlloc<Type>(numGenericParams);
void *mem = ctx.Allocate(bytes, alignof(GenericEnvironment));
return new (mem) GenericEnvironment(signature, builder);
}
Expand Down
3 changes: 1 addition & 2 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -892,8 +892,7 @@ void ASTMangler::appendType(Type type) {

// Find the archetype information.
const DeclContext *DC = DeclCtx;
auto GTPT = GenericEnvironment::mapTypeOutOfContext(GenericEnv, archetype)
->castTo<GenericTypeParamType>();
auto GTPT = archetype->getInterfaceType()->castTo<GenericTypeParamType>();

// The DWARF output created by Swift is intentionally flat,
// therefore archetypes are emitted with their DeclContext if
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,11 @@ struct LazyGenericEnvironment {
if (isLazy()) return true;

if (auto genericEnv = storage.dyn_cast<GenericEnvironment *>())
return genericEnv->containsPrimaryArchetype(archetype);
return archetype->getGenericEnvironment() == genericEnv;

if (auto dc = storage.dyn_cast<DeclContext *>()) {
if (auto genericEnv = dc->getGenericEnvironmentOfContext())
return genericEnv->containsPrimaryArchetype(archetype);
return archetype->getGenericEnvironment() == genericEnv;
}

return false;
Expand Down
130 changes: 3 additions & 127 deletions lib/AST/GenericEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ GenericEnvironment::GenericEnvironment(GenericSignature *signature,
GenericSignatureBuilder *builder)
: Signature(signature), Builder(builder)
{
NumMappingsRecorded = 0;
NumArchetypeToInterfaceMappings = 0;

// Clear out the memory that holds the context types.
std::uninitialized_fill(getContextTypes().begin(), getContextTypes().end(),
Type());
Expand Down Expand Up @@ -73,55 +70,6 @@ void GenericEnvironment::addMapping(GenericParamKey key,
// Add the mapping from the generic parameter to the context type.
assert(getContextTypes()[index].isNull() && "Already recoded this mapping");
getContextTypes()[index] = contextType;

// If we mapped the generic parameter to an archetype, add it to the
// reverse mapping.
if (auto *archetype = contextType->getAs<ArchetypeType>()) {
auto genericParam = genericParams[index];

// Check whether we've already recorded a generic parameter for this
// archetype. Note that we always perform a linear search, because we
// won't have sorted the list yet.
bool found = false;
for (auto &mapping : getActiveArchetypeToInterfaceMappings()) {
if (mapping.first != archetype) continue;

// Multiple generic parameters map to the same archetype. If the
// existing entry comes from a later generic parameter, replace it with
// the earlier generic parameter. This gives us a deterministic reverse
// mapping.
auto otherGP = mapping.second->castTo<GenericTypeParamType>();
if (GenericParamKey(genericParam) < GenericParamKey(otherGP))
mapping.second = genericParam;
found = true;
break;
}

// If we haven't recorded a generic parameter for this archetype, do so now.
if (!found) {
void *ptr = getArchetypeToInterfaceMappingsBuffer().data()
+ NumArchetypeToInterfaceMappings;
new (ptr) ArchetypeToInterfaceMapping(archetype, genericParam);
++NumArchetypeToInterfaceMappings;
}
}

// Note that we've recorded this mapping.
++NumMappingsRecorded;

// If we've recorded all of the mappings, go ahead and sort the array of
// archetype-to-interface-type mappings.
if (NumMappingsRecorded == genericParams.size()) {
llvm::array_pod_sort(getActiveArchetypeToInterfaceMappings().begin(),
getActiveArchetypeToInterfaceMappings().end(),
[](const ArchetypeToInterfaceMapping *lhs,
const ArchetypeToInterfaceMapping *rhs) -> int {
std::less<ArchetypeType *> compare;
if (compare(lhs->first, rhs->first)) return -1;
if (compare(rhs->first, lhs->first)) return 1;
return 0;
});
}
}

Optional<Type> GenericEnvironment::getMappingIfPresent(
Expand All @@ -138,12 +86,6 @@ Optional<Type> GenericEnvironment::getMappingIfPresent(
return None;
}

bool GenericEnvironment::containsPrimaryArchetype(
ArchetypeType *archetype) const {
return static_cast<bool>(
QueryArchetypeToInterfaceSubstitutions(this)(archetype));
}

Type GenericEnvironment::mapTypeIntoContext(GenericEnvironment *env,
Type type) {
assert(!type->hasArchetype() && "already have a contextual type");
Expand All @@ -166,7 +108,9 @@ GenericEnvironment::mapTypeOutOfContext(GenericEnvironment *env,
}

Type GenericEnvironment::mapTypeOutOfContext(Type type) const {
type = type.subst(QueryArchetypeToInterfaceSubstitutions(this),
type = type.subst([&](SubstitutableType *t) -> Type {
return cast<ArchetypeType>(t)->getInterfaceType();
},
MakeAbstractConformanceForGenericType(),
SubstFlags::AllowLoweredTypes);
assert(!type->hasArchetype() && "not fully substituted");
Expand Down Expand Up @@ -210,74 +154,6 @@ Type GenericEnvironment::QueryInterfaceTypeSubstitutions::operator()(
return Type();
}

Type GenericEnvironment::QueryArchetypeToInterfaceSubstitutions::operator()(
SubstitutableType *type) const {
auto archetype = type->getAs<ArchetypeType>();
if (!archetype) return Type();

// Only top-level archetypes need to be substituted directly; nested
// archetypes will be handled via their root archetypes.
if (archetype->getParent()) return Type();

// If not all generic parameters have had their context types recorded,
// perform a linear search.
auto genericParams = self->Signature->getGenericParams();
unsigned numGenericParams = genericParams.size();
if (self->NumMappingsRecorded < numGenericParams) {
// Search through all of the active archetype-to-interface mappings.
for (auto &mapping : self->getActiveArchetypeToInterfaceMappings())
if (mapping.first == archetype) return mapping.second;

// We don't know if the archetype is from a different context or if we
// simply haven't recorded it yet. Spin through all of the generic
// parameters looking for one that provides this mapping.
for (auto gp : genericParams) {
// Map the generic parameter into our context. If we get back an
// archetype that matches, we're done.
auto gpArchetype = self->mapTypeIntoContext(gp)->getAs<ArchetypeType>();
if (gpArchetype == archetype) return gp;
}

// We have checked all of the generic parameters and not found anything;
// there is no substitution.
return Type();
}

// All generic parameters have ad their context types recorded, which means
// that the archetypes-to-interface-types array is sorted by address. Use a
// binary search.
struct MappingComparison {
bool operator()(const ArchetypeToInterfaceMapping &lhs,
const ArchetypeType *rhs) const {
std::less<const ArchetypeType *> compare;

return compare(lhs.first, rhs);
}

bool operator()(const ArchetypeType *lhs,
const ArchetypeToInterfaceMapping &rhs) const {
std::less<const ArchetypeType *> compare;

return compare(lhs, rhs.first);
}

bool operator()(const ArchetypeToInterfaceMapping &lhs,
const ArchetypeToInterfaceMapping &rhs) const {
std::less<const ArchetypeType *> compare;

return compare(lhs.first, rhs.first);
}
} mappingComparison;

auto mappings = self->getActiveArchetypeToInterfaceMappings();
auto known = std::lower_bound(mappings.begin(), mappings.end(), archetype,
mappingComparison);
if (known != mappings.end() && known->first == archetype)
return known->second;

return Type();
}

Type GenericEnvironment::mapTypeIntoContext(
Type type,
LookupConformanceFn lookupConformance) const {
Expand Down
9 changes: 4 additions & 5 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1856,16 +1856,16 @@ Type EquivalenceClass::getTypeInContext(GenericSignatureBuilder &builder,
ASTContext &ctx = builder.getASTContext();
if (parentArchetype) {
// Create a nested archetype.
archetype = ArchetypeType::getNew(ctx, parentArchetype, assocType, protos,
auto *depMemTy = anchor->castTo<DependentMemberType>();
archetype = ArchetypeType::getNew(ctx, parentArchetype, depMemTy, protos,
superclass, layout);

// Register this archetype with its parent.
parentArchetype->registerNestedType(assocType->getName(), archetype);
} else {
// Create a top-level archetype.
auto genericParam = anchor->castTo<GenericTypeParamType>();
Identifier name = genericParam->getName();
archetype = ArchetypeType::getNew(ctx, genericEnv, name, protos,
archetype = ArchetypeType::getNew(ctx, genericEnv, genericParam, protos,
superclass, layout);

// Register the archetype with the generic environment.
Expand Down Expand Up @@ -2685,8 +2685,7 @@ void ArchetypeType::resolveNestedType(
auto genericEnv = getGenericEnvironment();
auto &builder = *genericEnv->getGenericSignatureBuilder();

Type interfaceType =
genericEnv->mapTypeOutOfContext(const_cast<ArchetypeType *>(this));
Type interfaceType = getInterfaceType();
Type memberInterfaceType =
DependentMemberType::get(interfaceType, nested.first);
auto equivClass =
Expand Down
Loading