Skip to content

Initial Semantics for Variadic Generics #40587

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 12 commits into from
Dec 20, 2021
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 include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,7 @@ class ASTContext final {
const CanType TheErrorType; /// This is the ErrorType singleton.
const CanType TheUnresolvedType; /// This is the UnresolvedType singleton.
const CanType TheEmptyTupleType; /// This is '()', aka Void
const CanType TheEmptyPackType;
const CanType TheAnyType; /// This is 'Any', the empty protocol composition
#define SINGLETON_TYPE(SHORT_ID, ID) \
const CanType The##SHORT_ID##Type;
Expand Down
6 changes: 4 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -3700,8 +3700,8 @@ NOTE(specialize_explicit_type_instead,none,
"did you mean to explicitly reference %0 instead?", (Type))
ERROR(type_parameter_count_mismatch,none,
"generic type %0 specialized with %select{too many|too few}3 type "
"parameters (got %2, but expected %1)",
(Identifier, unsigned, unsigned, bool))
"parameters (got %2, but expected %select{%1|at least %1}4)",
(Identifier, unsigned, unsigned, bool, bool))
ERROR(generic_type_requires_arguments,none,
"reference to generic type %0 requires arguments in <...>", (Type))
NOTE(descriptive_generic_type_declared_here,none,
Expand Down Expand Up @@ -4813,6 +4813,8 @@ ERROR(tuple_single_element,none,
"cannot create a single-element tuple with an element label", ())
ERROR(tuple_ellipsis,none,
"cannot create a variadic tuple", ())
ERROR(expansion_not_variadic,none,
"cannot create expansion with non-variadic type %0", (Type))
ERROR(tuple_duplicate_label,none,
"cannot create a tuple with a duplicate element label", ())
ERROR(enum_element_ellipsis,none,
Expand Down
71 changes: 70 additions & 1 deletion include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
IsPlaceholder : 1
);

SWIFT_INLINE_BITFIELD_FULL(PackExpr, Expr, 32,
: NumPadBits,
NumElements : 32
);
} Bits;

private:
Expand Down Expand Up @@ -3314,6 +3318,18 @@ class BridgeToObjCExpr : public ImplicitConversionExpr {
}
};

/// ReifyPackExpr - Drop the pack structure and reify it either as a tuple or
/// single value.
class ReifyPackExpr : public ImplicitConversionExpr {
public:
ReifyPackExpr(Expr *subExpr, Type type)
: ImplicitConversionExpr(ExprKind::ReifyPack, subExpr, type) {}

static bool classof(const Expr *E) {
return E->getKind() == ExprKind::ReifyPack;
}
};

/// UnresolvedSpecializeExpr - Represents an explicit specialization using
/// a type parameter list (e.g. "Vector<Int>") that has not been resolved.
class UnresolvedSpecializeExpr final : public Expr,
Expand Down Expand Up @@ -3413,10 +3429,13 @@ class InOutExpr : public Expr {
class VarargExpansionExpr : public Expr {
Expr *SubExpr;

public:
VarargExpansionExpr(Expr *subExpr, bool implicit, Type type = Type())
: Expr(ExprKind::VarargExpansion, implicit, type), SubExpr(subExpr) {}

public:
static VarargExpansionExpr *createParamExpansion(ASTContext &ctx, Expr *E);
static VarargExpansionExpr *createArrayExpansion(ASTContext &ctx, ArrayExpr *AE);

SWIFT_FORWARD_SOURCE_LOCS_TO(SubExpr)

Expr *getSubExpr() const { return SubExpr; }
Expand Down Expand Up @@ -5758,6 +5777,56 @@ class OneWayExpr : public Expr {
}
};

/// An expression node that aggregates a set of heterogeneous arguments into a
/// parameter pack suitable for passing off to a variadic generic function
/// argument.
///
/// There is no user-visible way to spell a pack expression, they are always
/// implicitly created at applies. As such, any appearance of pack types outside
/// of applies are illegal. In general, packs appearing in such positions should
/// have a \c ReifyPackExpr to convert them to a user-available AST type.
class PackExpr final : public Expr,
private llvm::TrailingObjects<PackExpr, Expr *> {
friend TrailingObjects;

size_t numTrailingObjects() const {
return getNumElements();
}

PackExpr(ArrayRef<Expr *> SubExprs, Type Ty);

public:
/// Create a pack.
static PackExpr *create(ASTContext &ctx, ArrayRef<Expr *> SubExprs, Type Ty);

/// Create an empty pack.
static PackExpr *createEmpty(ASTContext &ctx);

SourceLoc getLoc() const { return SourceLoc(); }
SourceRange getSourceRange() const { return SourceRange(); }

/// Retrieve the elements of this pack.
MutableArrayRef<Expr *> getElements() {
return { getTrailingObjects<Expr *>(), getNumElements() };
}

/// Retrieve the elements of this pack.
ArrayRef<Expr *> getElements() const {
return { getTrailingObjects<Expr *>(), getNumElements() };
}

unsigned getNumElements() const { return Bits.PackExpr.NumElements; }

Expr *getElement(unsigned i) const {
return getElements()[i];
}
void setElement(unsigned i, Expr *e) {
getElements()[i] = e;
}

static bool classof(const Expr *E) { return E->getKind() == ExprKind::Pack; }
};

inline bool Expr::isInfixOperator() const {
return isa<BinaryExpr>(this) || isa<IfExpr>(this) ||
isa<AssignExpr>(this) || isa<ExplicitCastExpr>(this);
Expand Down
6 changes: 4 additions & 2 deletions include/swift/AST/ExprNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ ABSTRACT_EXPR(ImplicitConversion, Expr)
EXPR(DifferentiableFunctionExtractOriginal, ImplicitConversionExpr)
EXPR(LinearFunctionExtractOriginal, ImplicitConversionExpr)
EXPR(LinearToDifferentiableFunction, ImplicitConversionExpr)
EXPR_RANGE(ImplicitConversion, Load, LinearToDifferentiableFunction)
EXPR(ReifyPack, ImplicitConversionExpr)
EXPR_RANGE(ImplicitConversion, Load, ReifyPack)
ABSTRACT_EXPR(ExplicitCast, Expr)
ABSTRACT_EXPR(CheckedCast, ExplicitCastExpr)
EXPR(ForcedCheckedCast, CheckedCastExpr)
Expand All @@ -203,7 +204,8 @@ EXPR(KeyPath, Expr)
UNCHECKED_EXPR(KeyPathDot, Expr)
UNCHECKED_EXPR(OneWay, Expr)
EXPR(Tap, Expr)
LAST_EXPR(Tap)
EXPR(Pack, Expr)
LAST_EXPR(Pack)

#undef EXPR_RANGE
#undef LITERAL_EXPR
Expand Down
11 changes: 11 additions & 0 deletions include/swift/AST/TypeDifferenceVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,17 @@ class CanTypeDifferenceVisitor : public CanTypePairVisitor<Impl, bool> {
return asImpl().visit(type1.getElementType(), type2.getElementType());
}

bool visitPackType(CanPackType type1, CanPackType type2) {
return visitComponentArray(type1, type2,
type1->getElementTypes(),
type2->getElementTypes());
}

bool visitPackExpansionType(CanPackExpansionType type1,
CanPackExpansionType type2) {
return asImpl().visit(type1.getPatternType(), type2.getPatternType());
}

bool visitTupleType(CanTupleType type1, CanTupleType type2) {
return visitComponentArray(type1, type2,
type1->getElements(), type2->getElements());
Expand Down
36 changes: 36 additions & 0 deletions include/swift/AST/TypeMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,42 @@ class TypeMatcher {
return mismatch(firstTuple.getPointer(), secondType, sugaredFirstType);
}

bool visitPackType(CanPackType firstTuple, Type secondType,
Type sugaredFirstType) {
if (auto secondTuple = secondType->getAs<PackType>()) {
auto sugaredFirstTuple = sugaredFirstType->getAs<PackType>();
if (firstTuple->getNumElements() != secondTuple->getNumElements())
return mismatch(firstTuple.getPointer(), secondTuple,
sugaredFirstType);

for (unsigned i = 0, n = firstTuple->getNumElements(); i != n; ++i) {
Type secondElt = secondTuple->getElementType(i);

// Recurse on the pack elements.
if (!this->visit(firstTuple.getElementType(i), secondElt,
sugaredFirstTuple->getElementType(i)))
return false;
}

return true;
}

// Pack/non-pack mismatch.
return mismatch(firstTuple.getPointer(), secondType, sugaredFirstType);
}

bool visitPackExpansionType(CanPackExpansionType firstPE, Type secondType,
Type sugaredFirstType) {
if (auto secondInOut = secondType->getAs<PackExpansionType>()) {
return this->visit(firstPE.getPatternType(),
secondInOut->getPatternType(),
sugaredFirstType->castTo<PackExpansionType>()
->getPatternType());
}

return mismatch(firstPE.getPointer(), secondType, sugaredFirstType);
}

bool visitReferenceStorageType(CanReferenceStorageType firstStorage,
Type secondType, Type sugaredFirstType) {
auto _secondStorage = secondType->getCanonicalType();
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/TypeNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ TYPE(ProtocolComposition, Type)
TYPE(Existential, Type)
TYPE(LValue, Type)
TYPE(InOut, Type)
TYPE(Pack, Type)
TYPE(PackExpansion, Type)
UNCHECKED_TYPE(TypeVariable, Type)
ABSTRACT_SUGARED_TYPE(Sugar, Type)
SUGARED_TYPE(Paren, SugarType)
Expand Down
Loading