Skip to content

Introduce type sugar for InlineArray #80087

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 5 commits into from
Mar 23, 2025
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
1 change: 1 addition & 0 deletions docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,7 @@ productions:
type ::= base-type "XSq" // sugared Optional type
type ::= base-type "XSa" // sugared Array type
type ::= key-type value-type "XSD" // sugared Dictionary type
type ::= count-type element-type "XSA" // sugared InlineArray type

Generics
~~~~~~~~
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -2702,6 +2702,11 @@ BridgedImplicitlyUnwrappedOptionalTypeRepr_createParsed(
BridgedASTContext cContext, BridgedTypeRepr base,
BridgedSourceLoc cExclamationLoc);

SWIFT_NAME("BridgedInlineArrayTypeRepr.createParsed(_:count:element:brackets:)")
BridgedInlineArrayTypeRepr BridgedInlineArrayTypeRepr_createParsed(
BridgedASTContext cContext, BridgedTypeRepr cCountType,
BridgedTypeRepr cElementType, BridgedSourceRange cBracketsRange);

SWIFT_NAME("BridgedInverseTypeRepr.createParsed(_:tildeLoc:constraint:)")
BridgedInverseTypeRepr
BridgedInverseTypeRepr_createParsed(BridgedASTContext cContext,
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/ASTDemangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ class ASTBuilder {

Type createArrayType(Type base);

Type createInlineArrayType(Type count, Type element);

Type createDictionaryType(Type key, Type value);

Type createIntegerType(intptr_t value);
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,10 @@ NOTE(subscript_array_element_fix_it_add_comma, none, "add a separator between "
NOTE(subscript_array_element_fix_it_remove_space, none,
"remove the space between the elements to silence this warning", ())

// Inline Array
ERROR(expected_rsquare_inline_array,none,
"expected ']' in inline array type", ())

// Tuple Types
ERROR(expected_rparen_tuple_type_list,none,
"expected ')' at end of tuple list", ())
Expand Down
5 changes: 4 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6069,7 +6069,7 @@ NOTE(note_recursive_enum_case_here,none,

ERROR(sugar_type_not_found,none,
"broken standard library: cannot find "
"%select{Array|Optional|ImplicitlyUnwrappedOptional|Dictionary|"
"%select{Array|Optional|InlineArray|Dictionary|"
"Error}0 type", (unsigned))
ERROR(optional_intrinsics_not_found,none,
"broken standard library: cannot find intrinsic operations on "
Expand Down Expand Up @@ -8334,6 +8334,9 @@ ERROR(invalid_value_for_type_same_type,none,
ERROR(inlinearray_literal_incorrect_count,none,
"expected %0 elements in inline array literal, but got %1", (Type, Type))

ERROR(inline_array_type_backwards,none,
"element count must precede inline array element type", ())

//===----------------------------------------------------------------------===//
// MARK: @abi Attribute
//===----------------------------------------------------------------------===//
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/PrintOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,10 @@ struct PrintOptions {
/// Whether to always desugar array types from `[base_type]` to `Array<base_type>`
bool AlwaysDesugarArraySliceTypes = false;

/// Whether to always desugar inline array types from
/// `[<count> x <element>]` to `InlineArray<count, element>`
bool AlwaysDesugarInlineArrayTypes = false;

/// Whether to always desugar dictionary types
/// from `[key_type:value_type]` to `Dictionary<key_type,value_type>`
bool AlwaysDesugarDictionaryTypes = false;
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TypeNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ ABSTRACT_SUGARED_TYPE(Sugar, Type)
SUGARED_TYPE(Optional, UnarySyntaxSugarType)
SUGARED_TYPE(VariadicSequence, UnarySyntaxSugarType)
TYPE_RANGE(UnarySyntaxSugar, ArraySlice, VariadicSequence)
SUGARED_TYPE(InlineArray, SyntaxSugarType)
SUGARED_TYPE(Dictionary, SyntaxSugarType)
TYPE_RANGE(SyntaxSugar, ArraySlice, Dictionary)
TYPE_RANGE(Sugar, TypeAlias, Dictionary)
Expand Down
30 changes: 30 additions & 0 deletions include/swift/AST/TypeRepr.h
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,35 @@ class ArrayTypeRepr : public TypeRepr {
friend class TypeRepr;
};

/// An InlineArray type e.g `[2 x Foo]`, sugar for `InlineArray<2, Foo>`.
class InlineArrayTypeRepr : public TypeRepr {
TypeRepr *Count;
TypeRepr *Element;
SourceRange Brackets;

InlineArrayTypeRepr(TypeRepr *count, TypeRepr *element, SourceRange brackets)
: TypeRepr(TypeReprKind::InlineArray), Count(count), Element(element),
Brackets(brackets) {}

public:
static InlineArrayTypeRepr *create(ASTContext &ctx, TypeRepr *count,
TypeRepr *element, SourceRange brackets);

TypeRepr *getCount() const { return Count; }
TypeRepr *getElement() const { return Element; }
SourceRange getBrackets() const { return Brackets; }

static bool classof(const TypeRepr *T) {
return T->getKind() == TypeReprKind::InlineArray;
}

private:
SourceLoc getStartLocImpl() const { return Brackets.Start; }
SourceLoc getEndLocImpl() const { return Brackets.End; }
void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const;
friend class TypeRepr;
};

/// A dictionary type.
/// \code
/// [K : V]
Expand Down Expand Up @@ -1626,6 +1655,7 @@ inline bool TypeRepr::isSimple() const {
case TypeReprKind::Fixed:
case TypeReprKind::Self:
case TypeReprKind::Array:
case TypeReprKind::InlineArray:
case TypeReprKind::SILBox:
case TypeReprKind::Isolated:
case TypeReprKind::Sending:
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TypeReprNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ ABSTRACT_TYPEREPR(DeclRef, TypeRepr)
TYPEREPR(UnqualifiedIdent, DeclRefTypeRepr)
TYPEREPR(QualifiedIdent, DeclRefTypeRepr)
TYPEREPR(Function, TypeRepr)
TYPEREPR(InlineArray, TypeRepr)
TYPEREPR(Array, TypeRepr)
TYPEREPR(Dictionary, TypeRepr)
TYPEREPR(Optional, TypeRepr)
Expand Down
19 changes: 19 additions & 0 deletions include/swift/AST/TypeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,25 @@ case TypeKind::Id:
return ArraySliceType::get(baseTy);
}

case TypeKind::InlineArray: {
auto ty = cast<InlineArrayType>(base);
auto countTy = doIt(ty->getCountType(), TypePosition::Invariant);
if (!countTy)
return Type();

// Currently the element type is invariant for InlineArray.
// FIXME: Should we allow covariance?
auto eltTy = doIt(ty->getElementType(), TypePosition::Invariant);
if (!eltTy)
return Type();

if (countTy.getPointer() == ty->getCountType().getPointer() &&
eltTy.getPointer() == ty->getElementType().getPointer())
return t;

return InlineArrayType::get(countTy, eltTy);
}

case TypeKind::Optional: {
auto optional = cast<OptionalType>(base);
auto baseTy = doIt(optional->getBaseType(), pos);
Expand Down
21 changes: 21 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -6197,6 +6197,27 @@ class ArraySliceType : public UnarySyntaxSugarType {
}
};

/// An InlineArray type e.g `[2 x Foo]`, sugar for `InlineArray<2, Foo>`.
class InlineArrayType : public SyntaxSugarType {
Type Count;
Type Elt;

InlineArrayType(const ASTContext &ctx, Type count, Type elt,
RecursiveTypeProperties properties)
: SyntaxSugarType(TypeKind::InlineArray, ctx, properties), Count(count),
Elt(elt) {}

public:
static InlineArrayType *get(Type count, Type elt);

Type getCountType() const { return Count; }
Type getElementType() const { return Elt; }

static bool classof(const TypeBase *T) {
return T->getKind() == TypeKind::InlineArray;
}
};

/// The type T?, which is always sugar for a library type.
class OptionalType : public UnarySyntaxSugarType {
OptionalType(const ASTContext &ctx,Type base,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,9 @@ EXPERIMENTAL_FEATURE(SwiftSettings, false)
/// Syntax sugar features for concurrency.
EXPERIMENTAL_FEATURE(ConcurrencySyntaxSugar, true)

/// Enable syntax sugar type '[3 x Int]' for Inline Array
EXPERIMENTAL_FEATURE(InlineArrayTypeSugar, false)

/// Allow declaration of compile-time values
EXPERIMENTAL_FEATURE(CompileTimeValues, true)

Expand Down
1 change: 1 addition & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ NODE(AssociatedTypeGenericParamRef)
NODE(SugaredOptional)
NODE(SugaredArray)
NODE(SugaredDictionary)
NODE(SugaredInlineArray)
NODE(SugaredParen) // Removed in Swift 6.TBD

// Added in Swift 5.1
Expand Down
16 changes: 16 additions & 0 deletions include/swift/Demangling/TypeDecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,22 @@ class TypeDecoder {

return Builder.createArrayType(base.getType());
}
case NodeKind::SugaredInlineArray: {
if (Node->getNumChildren() < 2) {
return MAKE_NODE_TYPE_ERROR(Node,
"fewer children (%zu) than required (2)",
Node->getNumChildren());
}
auto count = decodeMangledType(Node->getChild(0), depth + 1);
if (count.isError())
return count;

auto element = decodeMangledType(Node->getChild(1), depth + 1);
if (element.isError())
return element;

return Builder.createInlineArrayType(count.getType(), element.getType());
}
case NodeKind::SugaredDictionary: {
if (Node->getNumChildren() < 2)
return MAKE_NODE_TYPE_ERROR(Node,
Expand Down
14 changes: 14 additions & 0 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1458,6 +1458,14 @@ class Parser {
ParserResult<TypeRepr> parseTypeTupleBody();
ParserResult<TypeRepr> parseTypeArray(ParserResult<TypeRepr> Base);

/// Whether the parser is at the start of an InlineArray type body.
bool isStartOfInlineArrayTypeBody();

/// Parse an InlineArray type '[' integer 'x' type ']'.
///
/// NOTE: 'isStartOfInlineArrayTypeBody' must be true.
ParserResult<TypeRepr> parseTypeInlineArray(SourceLoc lSquare);

/// Parse a collection type.
/// type-simple:
/// '[' type ']'
Expand Down Expand Up @@ -1691,8 +1699,14 @@ class Parser {
/// and the expression will parse with the '<' as an operator.
bool canParseAsGenericArgumentList();

bool canParseTypeSimple();
bool canParseTypeSimpleOrComposition();
bool canParseTypeScalar();
bool canParseType();

bool canParseStartOfInlineArrayType();
bool canParseCollectionType();

/// Returns true if a simple type identifier can be parsed.
///
/// \verbatim
Expand Down
6 changes: 6 additions & 0 deletions include/swift/RemoteInspection/TypeRefBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,12 @@ class TypeRefBuilder {
return nullptr;
}

const TypeRef *createInlineArrayType(const TypeRef *count,
const TypeRef *element) {
// TypeRefs don't contain sugared types
return nullptr;
}

const TypeRef *createDictionaryType(const TypeRef *key,
const TypeRef *value) {
// TypeRefs don't contain sugared types
Expand Down
16 changes: 16 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ struct ASTContext::Implementation {
llvm::DenseMap<llvm::PointerIntPair<TypeBase*, 3, unsigned>,
ExistentialMetatypeType*> ExistentialMetatypeTypes;
llvm::DenseMap<Type, ArraySliceType*> ArraySliceTypes;
llvm::DenseMap<std::pair<Type, Type>, InlineArrayType *> InlineArrayTypes;
llvm::DenseMap<Type, VariadicSequenceType*> VariadicSequenceTypes;
llvm::DenseMap<std::pair<Type, Type>, DictionaryType *> DictionaryTypes;
llvm::DenseMap<Type, OptionalType*> OptionalTypes;
Expand Down Expand Up @@ -5427,6 +5428,21 @@ ArraySliceType *ArraySliceType::get(Type base) {
return entry = new (C, arena) ArraySliceType(C, base, properties);
}

InlineArrayType *InlineArrayType::get(Type count, Type elt) {
auto properties =
count->getRecursiveProperties() | elt->getRecursiveProperties();
auto arena = getArena(properties);

const ASTContext &C = count->getASTContext();

auto *&entry = C.getImpl().getArena(arena).InlineArrayTypes[{count, elt}];
if (entry)
return entry;

entry = new (C, arena) InlineArrayType(C, count, elt, properties);
return entry;
}

VariadicSequenceType *VariadicSequenceType::get(Type base) {
auto properties = base->getRecursiveProperties();
auto arena = getArena(properties);
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTDemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,10 @@ Type ASTBuilder::createArrayType(Type base) {
return ArraySliceType::get(base);
}

Type ASTBuilder::createInlineArrayType(Type count, Type element) {
return InlineArrayType::get(count, element);
}

Type ASTBuilder::createDictionaryType(Type key, Type value) {
return DictionaryType::get(key, value);
}
Expand Down
14 changes: 14 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4544,6 +4544,13 @@ class PrintTypeRepr : public TypeReprVisitor<PrintTypeRepr, void, Label>,
printFoot();
}

void visitInlineArrayTypeRepr(InlineArrayTypeRepr *T, Label label) {
printCommon("type_inline_array", label);
printRec(T->getCount(), Label::always("count"));
printRec(T->getElement(), Label::always("element"));
printFoot();
}

void visitDictionaryTypeRepr(DictionaryTypeRepr *T, Label label) {
printCommon("type_dictionary", label);
printRec(T->getKey(), Label::optional("key"));
Expand Down Expand Up @@ -6388,6 +6395,13 @@ namespace {
printFoot();
}

void visitInlineArrayType(InlineArrayType *T, Label label) {
printCommon("inline_array_type", label);
printRec(T->getCountType(), Label::always("count"));
printRec(T->getElementType(), Label::always("element"));
printFoot();
}

void visitOptionalType(OptionalType *T, Label label) {
printCommon("optional_type", label);
printRec(T->getBaseType(), Label::optional("base_type"));
Expand Down
12 changes: 12 additions & 0 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1574,6 +1574,18 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
appendOperator("XSa");
return;

case TypeKind::InlineArray: {
assert(DWARFMangling && "sugared types are only legal for the debugger");
auto *T = cast<InlineArrayType>(tybase);
appendType(T->getCountType(), sig, forDecl);
appendType(T->getElementType(), sig, forDecl);
// Note we don't have a known-type mangling for InlineArray, we can
// use 'A' since it's incredibly unlikely
// AutoreleasingUnsafeMutablePointer will ever receive type sugar.
appendOperator("XSA");
return;
}

case TypeKind::VariadicSequence:
assert(DWARFMangling && "sugared types are only legal for the debugger");
appendType(cast<VariadicSequenceType>(tybase)->getBaseType(), sig, forDecl);
Expand Down
12 changes: 12 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7066,6 +7066,18 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
}
}

void visitInlineArrayType(InlineArrayType *T) {
if (Options.AlwaysDesugarInlineArrayTypes) {
visit(T->getDesugaredType());
} else {
Printer << "[";
visit(T->getCountType());
Printer << " x ";
visit(T->getElementType());
Printer << "]";
}
}

void visitDictionaryType(DictionaryType *T) {
if (Options.AlwaysDesugarDictionaryTypes) {
visit(T->getDesugaredType());
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2248,6 +2248,10 @@ bool Traversal::visitArrayTypeRepr(ArrayTypeRepr *T) {
return doIt(T->getBase());
}

bool Traversal::visitInlineArrayTypeRepr(InlineArrayTypeRepr *T) {
return doIt(T->getCount()) || doIt(T->getElement());
}

bool Traversal::visitDictionaryTypeRepr(DictionaryTypeRepr *T) {
return doIt(T->getKey()) || doIt(T->getValue());
}
Expand Down
Loading