Add a BuiltinGenericType base class.

Builtin.FixedArray was introduced as the first generic builtin type, with
special case handling in all the various recursive visitors. Introduce
a base class, and move the handling to that base class, so it is easier
to introduce other generic builtins in the future.
This commit is contained in:
Joe Groff
2025-10-27 22:52:56 -07:00
parent 4c9a9ca9ad
commit cf5f7c8637
11 changed files with 250 additions and 92 deletions

View File

@@ -1609,6 +1609,10 @@ public:
OutputBackend = std::move(OutBackend);
}
private:
friend class BuiltinGenericType;
GenericSignature &getCachedBuiltinGenericTypeSignature(TypeKind kind);
private:
friend Decl;

View File

@@ -134,13 +134,28 @@ public:
CanBuiltinUnboundGenericType type2) {
return asImpl().visitDifferentTypeStructure(type1, type2);
}
bool visitBuiltinFixedArrayType(CanBuiltinFixedArrayType type1,
CanBuiltinFixedArrayType type2) {
if (asImpl().visit(type1->getSize(), type2->getSize())) {
bool visitBuiltinGenericType(CanBuiltinGenericType type1,
CanBuiltinGenericType type2) {
if (type1->getBuiltinTypeKind() != type2->getBuiltinTypeKind()) {
return true;
}
return asImpl().visit(type1->getElementType(), type2->getElementType());
auto subs1 = type1->getSubstitutions();
auto subs2 = type2->getSubstitutions();
for (unsigned i : indices(subs1.getReplacementTypes())) {
if (asImpl().visit(CanType(subs1.getReplacementTypes()[i]),
CanType(subs2.getReplacementTypes()[i]))) {
return true;
}
}
for (unsigned i : indices(subs1.getConformances())) {
if (subs1.getConformances()[i] != subs2.getConformances()[i]) {
return true;
}
}
return false;
}
bool visitPackType(CanPackType type1, CanPackType type2) {

View File

@@ -112,7 +112,7 @@ private:
TRIVIAL_CASE(BuiltinFloatType)
TRIVIAL_CASE(BuiltinVectorType)
TRIVIAL_CASE(BuiltinUnboundGenericType)
TRIVIAL_CASE(BuiltinFixedArrayType)
TRIVIAL_CASE(BuiltinGenericType)
TRIVIAL_CASE(IntegerType)
#define SINGLETON_TYPE(SHORT_ID, ID) TRIVIAL_CASE(ID##Type)
#include "swift/AST/TypeNodes.def"

View File

@@ -141,7 +141,9 @@ ABSTRACT_TYPE(Builtin, Type)
BUILTIN_CONCRETE_TYPE(BuiltinDefaultActorStorage, BuiltinType)
BUILTIN_CONCRETE_TYPE(BuiltinNonDefaultDistributedActorStorage, BuiltinType)
BUILTIN_CONCRETE_TYPE(BuiltinVector, BuiltinType)
BUILTIN_GENERIC_TYPE(BuiltinFixedArray, BuiltinType)
ABSTRACT_TYPE(BuiltinGeneric, BuiltinType)
BUILTIN_GENERIC_TYPE(BuiltinFixedArray, BuiltinGenericType)
TYPE_RANGE(BuiltinGeneric, BuiltinFixedArray, BuiltinFixedArray)
BUILTIN_CONCRETE_TYPE(BuiltinUnboundGeneric, BuiltinType)
BUILTIN_CONCRETE_TYPE(BuiltinImplicitActor, BuiltinType)
TYPE_RANGE(Builtin, BuiltinInteger, BuiltinImplicitActor)

View File

@@ -21,6 +21,8 @@
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/SILLayout.h"
#include "swift/AST/LifetimeDependence.h"
#include "swift/AST/SubstitutionMap.h"
#include "llvm/ADT/SmallVector.h"
namespace swift {
@@ -117,29 +119,26 @@ case TypeKind::Id:
case TypeKind::Integer:
return t;
// BuiltinGenericType subclasses
case TypeKind::BuiltinFixedArray: {
auto bfaTy = cast<BuiltinFixedArrayType>(base);
Type transSize = doIt(bfaTy->getSize(),
TypePosition::Invariant);
if (!transSize) {
return Type();
auto bgaTy = cast<BuiltinGenericType>(base);
llvm::SmallVector<Type, 2> transReplacements;
for (auto t : bgaTy->getSubstitutions().getReplacementTypes()) {
Type transTy = doIt(t, TypePosition::Invariant);
if (!transTy) {
return Type();
}
transReplacements.push_back(transTy);
}
Type transElement = doIt(bfaTy->getElementType(),
TypePosition::Invariant);
if (!transElement) {
return Type();
}
CanType canTransSize = transSize->getCanonicalType();
CanType canTransElement = transElement->getCanonicalType();
if (canTransSize != bfaTy->getSize()
|| canTransElement != bfaTy->getElementType()) {
return BuiltinFixedArrayType::get(canTransSize, canTransElement);
}
return bfaTy;
// TODO: translate conformances. No builtin types yet have conformance
// requirements in their generic signatures.
auto transSubs = SubstitutionMap::get(bgaTy->getGenericSignature(),
transReplacements,
ArrayRef<ProtocolConformanceRef>{});
return bgaTy->getWithSubstitutions(transSubs);
}
case TypeKind::PrimaryArchetype:

View File

@@ -1780,12 +1780,45 @@ public:
};
DEFINE_EMPTY_CAN_TYPE_WRAPPER(BuiltinUnboundGenericType, BuiltinType)
/// BuiltinGenericType - Base class for builtins that are parameterized by
/// a generic signature.
class BuiltinGenericType : public BuiltinType {
protected:
BuiltinGenericType(TypeKind kind, ASTContext &context,
RecursiveTypeProperties properties)
: BuiltinType(kind, context, properties)
{}
/// The substitution map is cached here once requested.
mutable std::optional<SubstitutionMap> CachedSubstitutionMap = std::nullopt;
public:
static bool classof(const TypeBase *T) {
return T->getKind() >= TypeKind::First_BuiltinGenericType
&& T->getKind() <= TypeKind::Last_BuiltinGenericType;
}
// Get the generic signature describing the parameterization of types of
// this class.
GenericSignature getGenericSignature() const;
// Get the substitution map for this particular type.
SubstitutionMap getSubstitutions() const;
// Produce another type of the same class but with different arguments.
CanTypeWrapper<BuiltinGenericType>
getWithSubstitutions(SubstitutionMap newSubs) const;
};
DEFINE_EMPTY_CAN_TYPE_WRAPPER(BuiltinGenericType, BuiltinType)
/// BuiltinFixedArrayType - The builtin type representing N values stored
/// inline contiguously.
///
/// All elements of a value of this type must be fully initialized any time the
/// value may be copied, moved, or destroyed.
class BuiltinFixedArrayType : public BuiltinType, public llvm::FoldingSetNode {
class BuiltinFixedArrayType : public BuiltinGenericType,
public llvm::FoldingSetNode {
friend class ASTContext;
CanType Size;
@@ -1793,12 +1826,17 @@ class BuiltinFixedArrayType : public BuiltinType, public llvm::FoldingSetNode {
BuiltinFixedArrayType(CanType Size, CanType ElementType,
RecursiveTypeProperties properties)
: BuiltinType(TypeKind::BuiltinFixedArray, ElementType->getASTContext(),
properties),
: BuiltinGenericType(TypeKind::BuiltinFixedArray,
ElementType->getASTContext(),
properties),
Size(Size),
ElementType(ElementType)
{}
friend BuiltinGenericType;
/// Get the generic arguments as a substitution map.
SubstitutionMap buildSubstitutions() const;
public:
/// Arrays with more elements than this are always treated as in-memory values.
///
@@ -1826,7 +1864,7 @@ public:
/// Get the element type.
CanType getElementType() const { return ElementType; }
void Profile(llvm::FoldingSetNodeID &ID) const {
Profile(ID, getSize(), getElementType());
}
@@ -1836,7 +1874,7 @@ public:
ID.AddPointer(ElementType.getPointer());
}
};
DEFINE_EMPTY_CAN_TYPE_WRAPPER(BuiltinFixedArrayType, BuiltinType)
DEFINE_EMPTY_CAN_TYPE_WRAPPER(BuiltinFixedArrayType, BuiltinGenericType)
/// BuiltinRawPointerType - The builtin raw (and dangling) pointer type. This
/// pointer is completely unmanaged and is equivalent to i8* in LLVM IR.

View File

@@ -533,6 +533,13 @@ struct ASTContext::Implementation {
/// Local and closure discriminators per context.
llvm::DenseMap<const DeclContext *, unsigned> NextDiscriminator;
/// Cached generic signatures for generic builtin types.
static const unsigned NumBuiltinGenericTypes
= unsigned(TypeKind::Last_BuiltinGenericType)
- unsigned(TypeKind::First_BuiltinGenericType) + 1;
std::array<GenericSignature, NumBuiltinGenericTypes>
BuiltinGenericTypeSignatures = {};
/// Structure that captures data that is segregated into different
/// arenas.
struct Arena {
@@ -7309,3 +7316,13 @@ AvailabilityDomain ASTContext::getTargetAvailabilityDomain() const {
// Fall back to the universal domain for triples without a platform.
return AvailabilityDomain::forUniversal();
}
GenericSignature &
ASTContext::getCachedBuiltinGenericTypeSignature(TypeKind kind) {
ASSERT(kind >= TypeKind::First_BuiltinGenericType
&& kind <= TypeKind::Last_BuiltinGenericType
&& "not a builtin generic type kind");
return getImpl().BuiltinGenericTypeSignatures
[unsigned(kind) - unsigned(TypeKind::First_BuiltinGenericType)];
}

View File

@@ -21,6 +21,7 @@
#include "swift/AST/FileUnit.h"
#include "swift/AST/Module.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
@@ -30,6 +31,7 @@
#include "llvm/IR/Attributes.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ManagedStatic.h"
#include <tuple>
@@ -3649,11 +3651,10 @@ BuiltinUnboundGenericType::getBuiltinTypeNameString() const {
return getBuiltinTypeName();
}
GenericSignature
BuiltinUnboundGenericType::getGenericSignature() const {
auto &C = getASTContext();
switch (BoundGenericTypeKind) {
static GenericSignature
getBuiltinGenericSignature(ASTContext &C,
TypeKind kind) {
switch (kind) {
case TypeKind::BuiltinFixedArray: {
auto Count = GenericTypeParamType::get(C.getIdentifier("Count"),
GenericTypeParamKind::Value,
@@ -3663,35 +3664,48 @@ BuiltinUnboundGenericType::getGenericSignature() const {
0, 1, Type(), C);
return GenericSignature::get({Count, Element}, {});
}
case TypeKind::BuiltinInteger: {
auto bits = GenericTypeParamType::get(C.getIdentifier("Bits"),
GenericTypeParamKind::Type,
0, 0, C.getIntType(), C);
return GenericSignature::get(bits, {});
}
default:
llvm_unreachable("not a generic builtin");
llvm_unreachable("not a generic builtin type");
}
}
Type
BuiltinUnboundGenericType::getBound(SubstitutionMap subs) const {
if (!subs.getGenericSignature()->isEqual(getGenericSignature())) {
return ErrorType::get(const_cast<BuiltinUnboundGenericType*>(this));
GenericSignature
BuiltinUnboundGenericType::getGenericSignature() const {
return getBuiltinGenericSignature(getASTContext(), BoundGenericTypeKind);
}
static Type
getBuiltinBoundGenericType(ASTContext &C,
TypeKind kind,
SubstitutionMap subs,
bool validate) {
if (!subs.getGenericSignature()->isEqual(getBuiltinGenericSignature(C, kind))) {
return ErrorType::get(BuiltinUnboundGenericType::get(kind, C));
}
switch (BoundGenericTypeKind) {
switch (kind) {
case TypeKind::BuiltinFixedArray: {
auto types = subs.getReplacementTypes();
auto size = types[0]->getCanonicalType();
if (size->getMatchingParamKind() != GenericTypeParamKind::Value) {
return ErrorType::get(const_cast<BuiltinUnboundGenericType*>(this));
if (validate
&& size->getMatchingParamKind() != GenericTypeParamKind::Value
&& !size->hasTypeVariableOrPlaceholder()) {
return ErrorType::get(BuiltinUnboundGenericType::get(kind, C));
}
auto element = types[1]->getCanonicalType();
if (element->getMatchingParamKind() != GenericTypeParamKind::Type) {
return ErrorType::get(const_cast<BuiltinUnboundGenericType*>(this));
if (validate
&& element->getMatchingParamKind() != GenericTypeParamKind::Type
&& !element->hasTypeVariableOrPlaceholder()) {
return ErrorType::get(BuiltinUnboundGenericType::get(kind, C));
}
return BuiltinFixedArrayType::get(size, element);
@@ -3699,19 +3713,20 @@ BuiltinUnboundGenericType::getBound(SubstitutionMap subs) const {
case TypeKind::BuiltinInteger: {
auto size = subs.getReplacementTypes()[0];
if (size->getMatchingParamKind() != GenericTypeParamKind::Value) {
return ErrorType::get(const_cast<BuiltinUnboundGenericType*>(this));
if (validate
&& size->getMatchingParamKind() != GenericTypeParamKind::Value) {
return ErrorType::get(BuiltinUnboundGenericType::get(kind, C));
}
// TODO: support actual generic parameters
auto literalSize = size->getAs<IntegerType>();
if (!literalSize) {
return ErrorType::get(const_cast<BuiltinUnboundGenericType*>(this));
assert(validate && "can't represent an unbound generic sized integer yet");
return ErrorType::get(BuiltinUnboundGenericType::get(kind, C));
}
return BuiltinIntegerType::get(literalSize->getValue().getLimitedValue(),
getASTContext());
return BuiltinIntegerType::get(literalSize->getValue().getLimitedValue(),C);
}
default:
@@ -3719,6 +3734,59 @@ BuiltinUnboundGenericType::getBound(SubstitutionMap subs) const {
}
}
Type
BuiltinUnboundGenericType::getBound(SubstitutionMap subs) const {
return getBuiltinBoundGenericType(getASTContext(), BoundGenericTypeKind,
subs, /*validate*/ true);
}
CanBuiltinGenericType
BuiltinGenericType::getWithSubstitutions(SubstitutionMap subs) const {
auto t = getBuiltinBoundGenericType(getASTContext(), getKind(), subs,
/*validate*/ false);
return CanBuiltinGenericType(t->castTo<BuiltinGenericType>());
}
GenericSignature
BuiltinGenericType::getGenericSignature() const {
auto &cached = getASTContext()
.getCachedBuiltinGenericTypeSignature(getKind());
if (!cached) {
cached = getBuiltinGenericSignature(getASTContext(), getKind());
}
return cached;
}
SubstitutionMap
BuiltinGenericType::getSubstitutions() const {
if (auto cached = CachedSubstitutionMap) {
return *cached;
}
auto buildSubstitutions = [&]() -> SubstitutionMap {
#define BUILTIN_GENERIC_SUBCLASS(c) \
if (auto t = dyn_cast<c>(this)) { \
return t->c::buildSubstitutions(); \
}
BUILTIN_GENERIC_SUBCLASS(BuiltinFixedArrayType)
llvm_unreachable("unhandled BuiltinGenericType subclass");
};
auto subs = buildSubstitutions();
CachedSubstitutionMap = subs;
return subs;
}
SubstitutionMap
BuiltinFixedArrayType::buildSubstitutions() const {
return SubstitutionMap::get(getGenericSignature(),
{getSize(), getElementType()},
ArrayRef<ProtocolConformanceRef>{});
}
std::optional<uint64_t>
BuiltinFixedArrayType::getFixedInhabitedSize() const {
if (auto intSize = getSize()->getAs<IntegerType>()) {

View File

@@ -17,6 +17,7 @@
#include "swift/AST/TypeWalker.h"
#include "swift/AST/TypeVisitor.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
using namespace swift;
@@ -301,17 +302,16 @@ class Traversal : public TypeVisitor<Traversal, bool>
}
return false;
}
bool visitBuiltinFixedArrayType(BuiltinFixedArrayType *ty) {
if (ty->getSize() && doIt(ty->getSize())) {
return true;
}
if (ty->getElementType() && doIt(ty->getElementType())) {
return true;
bool visitBuiltinGenericType(BuiltinGenericType *ty) {
for (auto replacement : ty->getSubstitutions().getReplacementTypes()) {
if (replacement && doIt(replacement)) {
return true;
}
}
return false;
}
public:
explicit Traversal(TypeWalker &walker) : Walker(walker) {}

View File

@@ -15,6 +15,8 @@
//
//===----------------------------------------------------------------------===//
#include "swift/AST/Types.h"
#include "llvm/ADT/SmallVector.h"
#define DEBUG_TYPE "libsil"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ConformanceLookup.h"
@@ -2673,32 +2675,38 @@ public:
// Otherwise, there are no structural type parameters to visit.
return nom;
}
CanType visitBuiltinFixedArrayType(CanBuiltinFixedArrayType bfa,
AbstractionPattern pattern) {
auto orig = pattern.getAs<BuiltinFixedArrayType>();
CanType visitBuiltinGenericType(CanBuiltinGenericType bga,
AbstractionPattern pattern) {
auto orig = pattern.getAs<BuiltinGenericType>();
// If there are no loose type parameters in the pattern here, we don't need
// to do a recursive visit at all.
if (!orig->hasTypeParameter()
&& !orig->hasArchetype()
&& !orig->hasOpaqueArchetype()) {
return bfa;
return bga;
}
CanType newSize = visit(bfa->getSize(),
AbstractionPattern(pattern.getGenericSubstitutions(),
pattern.getGenericSignatureOrNull(),
orig->getSize()));
CanType newElement = visit(bfa->getElementType(),
AbstractionPattern(pattern.getGenericSubstitutions(),
pattern.getGenericSignatureOrNull(),
orig->getElementType()));
return BuiltinFixedArrayType::get(newSize, newElement)
->getCanonicalType();
SmallVector<Type, 2> newReplacements;
for (unsigned i : indices(bga->getSubstitutions().getReplacementTypes())) {
CanType newReplacement
= visit(CanType(bga->getSubstitutions().getReplacementTypes()[i]),
AbstractionPattern(pattern.getGenericSubstitutions(),
pattern.getGenericSignatureOrNull(),
CanType(orig->getSubstitutions().getReplacementTypes()[i])));
newReplacements.push_back(newReplacement);
}
// TODO: handle conformances. no generic builtins currently have protocol
// requirements.
auto newSubs = SubstitutionMap::get(bga->getGenericSignature(),
newReplacements,
ArrayRef<ProtocolConformanceRef>{});
return bga->getWithSubstitutions(newSubs);
}
CanType visitBoundGenericType(CanBoundGenericType bgt,
AbstractionPattern pattern) {
return handleGenericNominalType(pattern, bgt);

View File

@@ -7471,21 +7471,28 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
case TypeKind::Error:
return getTypeMatchFailure(locator);
// BuiltinGenericType subclasses
case TypeKind::BuiltinFixedArray: {
auto *fixed1 = cast<BuiltinFixedArrayType>(desugar1);
auto *fixed2 = cast<BuiltinFixedArrayType>(desugar2);
auto *fixed1 = cast<BuiltinGenericType>(desugar1);
auto *fixed2 = cast<BuiltinGenericType>(desugar2);
if (fixed1->getBuiltinTypeKind() != fixed2->getBuiltinTypeKind()) {
return getTypeMatchFailure(locator);
}
auto result = matchTypes(fixed1->getSize(), fixed2->getSize(),
ConstraintKind::Bind, subflags,
locator.withPathElement(
LocatorPathElt::GenericArgument(0)));
if (result.isFailure())
return result;
auto result = ConstraintSystem::TypeMatchResult::success();
for (unsigned i
: indices(fixed1->getSubstitutions().getReplacementTypes())) {
result = matchTypes(fixed1->getSubstitutions().getReplacementTypes()[i],
fixed2->getSubstitutions().getReplacementTypes()[i],
ConstraintKind::Bind, subflags,
locator.withPathElement(
LocatorPathElt::GenericArgument(i)));
if (result.isFailure()) {
return result;
}
}
return matchTypes(fixed1->getElementType(), fixed2->getElementType(),
ConstraintKind::Bind, subflags,
locator.withPathElement(
LocatorPathElt::GenericArgument(0)));
return result;
}
case TypeKind::Placeholder: {