Files
swift-mirror/include/swift/AST/ArgumentList.h

583 lines
22 KiB
C++

//===--- ArgumentList.h - Function and subscript argument lists -*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the Argument and ArgumentList classes and support logic.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_AST_ARGUMENTLIST_H
#define SWIFT_AST_ARGUMENTLIST_H
#include "swift/AST/ASTAllocated.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Debug.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/TrailingObjects.h"
#include <optional>
namespace swift {
/// Forward declared trampoline for Expr::getType.
Type __Expr_getType(Expr *E);
/// Represents a single argument in an argument list, including its label
/// information and inout-ness.
///
/// \code
/// foo.bar(arg, label: arg2, other: &arg3)
/// ^^^ ^^^^^^^^^^^^
/// an unlabeled 'other' is the label, 'arg3' is the expr,
/// argument and isInout() is true.
/// \endcode
class Argument final {
SourceLoc LabelLoc;
Identifier Label;
Expr *ArgExpr;
// TODO: Store inout bit here.
public:
Argument(SourceLoc labelLoc, Identifier label, Expr *expr)
: LabelLoc(labelLoc), Label(label), ArgExpr(expr) {}
/// Make an unlabeled argument.
static Argument unlabeled(Expr *expr) {
return Argument(SourceLoc(), Identifier(), expr);
}
/// Make an implicit unlabeled 'inout' argument.
static Argument implicitInOut(ASTContext &ctx, Expr *expr);
SourceLoc getStartLoc() const { return getSourceRange().Start; }
SourceLoc getEndLoc() const { return getSourceRange().End; }
SourceRange getSourceRange() const;
/// If the argument has a label with location information, returns the
/// location.
///
/// Note this may be present even if the label is empty, e.g for multiple
/// trailing closures where labels are required such as '_: {}'.
SourceLoc getLabelLoc() const { return LabelLoc; }
/// The argument label written in the call.
Identifier getLabel() const { return Label; }
/// Whether the argument has a non-empty label. Note that this returns `false`
/// for an explicitly specified empty label e.g `_: {}` for a trailing
/// closure.
bool hasLabel() const { return !Label.empty(); }
/// Set a new argument label.
///
/// Note this is marked \c & to prevent its use on an rvalue Argument vended
/// from an ArgumentList.
void setLabel(Identifier newLabel) & { Label = newLabel; }
/// The argument expression.
Expr *getExpr() const { return ArgExpr; }
/// Set a new argument expression.
///
/// Note this is marked \c & to prevent its use on an rvalue Argument vended
/// from an ArgumentList.
void setExpr(Expr *newExpr) & { ArgExpr = newExpr; }
/// Whether the argument is \c inout, denoted with the '&' prefix.
bool isInOut() const;
/// Whether the argument is a compile-time constant value.
bool isCompileTimeLiteral() const;
bool operator==(const Argument &other) const {
return LabelLoc == other.LabelLoc && Label == other.Label &&
ArgExpr == other.ArgExpr;
}
bool operator!=(const Argument &other) const { return !(*this == other); }
};
/// Represents the argument list of a function call or subscript access.
/// \code
/// foo.bar(arg, label: arg2, other: &arg3)
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/// \endcode
///
/// Note that arguments are split up and stored in separate buffers to optimize
/// ArgumentList's memory footprint for the unlabeled cases.
class alignas(Argument) ArgumentList final
: public ASTAllocated<ArgumentList>,
private llvm::TrailingObjects<ArgumentList, Expr *, Identifier,
SourceLoc, ArgumentList *> {
friend TrailingObjects;
SourceLoc LParenLoc;
SourceLoc RParenLoc;
/// The number of arguments in the list.
unsigned NumArgs : 16;
/// The index of the first trailing closure, or \c NumArgs if there are
/// either no trailing closures, or the trailing closure info is present in
/// the original args.
unsigned RawFirstTrailingClosureIndex : 16;
/// Whether an original set of arguments are available.
bool HasOriginalArgs : 1;
/// Whether the argument list was implicitly generated.
bool IsImplicit : 1;
/// Whether any of the arguments are labeled.
bool HasLabels : 1;
/// Whether any of the arguments have labels with location info.
bool HasLabelLocs : 1;
ArgumentList(SourceLoc lParenLoc, SourceLoc rParenLoc, unsigned numArgs,
std::optional<unsigned> firstTrailingClosureIndex,
ArgumentList *originalArgs, bool isImplicit, bool hasLabels,
bool hasLabelLocs)
: LParenLoc(lParenLoc), RParenLoc(rParenLoc), NumArgs(numArgs),
HasOriginalArgs(originalArgs), IsImplicit(isImplicit),
HasLabels(hasLabels), HasLabelLocs(hasLabelLocs) {
assert(LParenLoc.isValid() == RParenLoc.isValid());
assert(!(firstTrailingClosureIndex && originalArgs) &&
"Cannot have trailing closure info if original args present");
assert(!(originalArgs && originalArgs->getStoredOriginalArgs()) &&
"Cannot chain original argument lists");
assert(NumArgs == numArgs && "Num args truncated");
assert(!firstTrailingClosureIndex || *firstTrailingClosureIndex < numArgs &&
"Invalid trailing closure index");
RawFirstTrailingClosureIndex =
firstTrailingClosureIndex.value_or(numArgs);
}
ArgumentList(const ArgumentList &) = delete;
ArgumentList &operator=(const ArgumentList &) = delete;
size_t numTrailingObjects(OverloadToken<Expr *>) const {
return NumArgs;
}
size_t numTrailingObjects(OverloadToken<Identifier>) const {
return HasLabels ? NumArgs : 0;
}
size_t numTrailingObjects(OverloadToken<SourceLoc>) const {
return HasLabelLocs ? NumArgs : 0;
}
size_t numTrailingObjects(OverloadToken<ArgumentList *>) const {
return HasOriginalArgs ? 1 : 0;
}
ArrayRef<Expr *> getExprsBuffer() const {
return {getTrailingObjects<Expr *>(), NumArgs};
}
MutableArrayRef<Expr *> getExprsBuffer() {
return {getTrailingObjects<Expr *>(), NumArgs};
}
ArrayRef<Identifier> getLabelsBuffer() const {
assert(HasLabels);
return {getTrailingObjects<Identifier>(), NumArgs};
}
MutableArrayRef<Identifier> getLabelsBuffer() {
assert(HasLabels);
return {getTrailingObjects<Identifier>(), NumArgs};
}
ArrayRef<SourceLoc> getLabelLocsBuffer() const {
assert(HasLabelLocs);
return {getTrailingObjects<SourceLoc>(), NumArgs};
}
MutableArrayRef<SourceLoc> getLabelLocsBuffer() {
assert(HasLabelLocs);
return {getTrailingObjects<SourceLoc>(), NumArgs};
}
ArgumentList *getStoredOriginalArgs() const {
if (!HasOriginalArgs)
return nullptr;
return *getTrailingObjects<ArgumentList *>();
}
public:
/// Create a new ArgumentList.
///
/// \param lParenLoc The location of the opening '('. Note that for a
/// subscript argument list, this will be for the opening '['.
/// \param args The list of arguments in the call.
/// \param rParenLoc The location of the closing ')'. Note that for a
/// subscript argument list, this will be for the closing ']'.
/// \param firstTrailingClosureIndex The index of the first trailing closure.
/// \param isImplicit Whether this is an implicitly generated argument list.
/// \param originalArgs An original argument list prior to being rewritten by
/// the expression type-checker. Note \p firstTrailingClosureIndex must be
/// \c None if an original argument list is available.
/// \param arena The arena to allocate the ArgumentList in.
static ArgumentList *
create(ASTContext &ctx, SourceLoc lParenLoc, ArrayRef<Argument> args,
SourceLoc rParenLoc, std::optional<unsigned> firstTrailingClosureIndex,
bool isImplicit, ArgumentList *originalArgs = nullptr,
AllocationArena arena = AllocationArena::Permanent);
/// Create a new explicit parsed ArgumentList.
///
/// \param lParenLoc The location of the opening '('. Note that for a
/// subscript argument list, this will be for the opening '['.
/// \param args The list of arguments in the call.
/// \param rParenLoc The location of the closing ')'. Note that for a
/// subscript argument list, this will be for the closing ']'.
/// \param firstTrailingClosureIndex The index of the first trailing closure.
static ArgumentList *
createParsed(ASTContext &ctx, SourceLoc lParenLoc, ArrayRef<Argument> args,
SourceLoc rParenLoc,
std::optional<unsigned> firstTrailingClosureIndex);
/// Create a new type-checked ArgumentList from an original set of arguments.
///
/// \param originalArgs An original argument list prior to being rewritten by
/// the type-checker.
/// \param newArgs The new set of arguments for the call.
static ArgumentList *createTypeChecked(ASTContext &ctx,
ArgumentList *originalArgs,
ArrayRef<Argument> newArgs);
/// Create a new implicit ArgumentList.
///
/// \param lParenLoc The location of the opening '('. Note that for a
/// subscript argument list, this will be for the opening '['.
/// \param args The list of arguments in the call.
/// \param rParenLoc The location of the closing ')'. Note that for a
/// subscript argument list, this will be for the closing ']'.
/// \param arena The arena to allocate the ArgumentList in.
static ArgumentList *createImplicit(
ASTContext &ctx, SourceLoc lParenLoc, ArrayRef<Argument> args,
SourceLoc rParenLoc,
std::optional<unsigned> firstTrailingClosureIndex = std::nullopt,
AllocationArena arena = AllocationArena::Permanent);
/// Create a new implicit ArgumentList with a set of \p args.
static ArgumentList *createImplicit(
ASTContext &ctx, ArrayRef<Argument> args,
std::optional<unsigned> firstTrailingClosureIndex = std::nullopt,
AllocationArena arena = AllocationArena::Permanent);
/// Create a new implicit ArgumentList with a single labeled argument
/// expression.
static ArgumentList *forImplicitSingle(ASTContext &ctx, Identifier label,
Expr *arg);
/// Create a new implicit ArgumentList with a set of unlabeled arguments.
static ArgumentList *forImplicitUnlabeled(ASTContext &ctx,
ArrayRef<Expr *> argExprs);
/// Create a new implicit ArgumentList with a set of argument expressions,
/// and a DeclNameRef from which to infer the argument labels.
static ArgumentList *forImplicitCallTo(DeclNameRef fnNameRef,
ArrayRef<Expr *> argExprs,
ASTContext &ctx);
/// Create a new implicit ArgumentList with a set of argument expressions,
/// and a ParameterList from which to infer the argument labels.
static ArgumentList *forImplicitCallTo(ParameterList *params,
ArrayRef<Expr *> argExprs,
ASTContext &ctx);
/// The location of the opening '('. Note that for a subscript argument list,
/// this will be for the opening '['.
SourceLoc getLParenLoc() const { return LParenLoc; }
/// The location of the closing ')'. Note that for a subscript argument list,
/// this will be for the closing ']'.
SourceLoc getRParenLoc() const { return RParenLoc; }
/// Whether this is an implicitly generated argument list, not written in the
/// source.
bool isImplicit() const { return IsImplicit; }
/// Whether the argument list has any non-empty argument labels.
bool hasAnyArgumentLabels() const { return HasLabels; }
SourceLoc getLoc() const;
SourceLoc getStartLoc() const { return getSourceRange().Start; }
SourceLoc getEndLoc() const { return getSourceRange().End; }
SourceRange getSourceRange() const;
/// Retrieve the argument expression at a given index.
Expr *getExpr(unsigned idx) const { return getExprsBuffer()[idx]; }
void setExpr(unsigned idx, Expr *newExpr) { getExprsBuffer()[idx] = newExpr; }
/// Retrieve the argument label at a given index.
Identifier getLabel(unsigned idx) const {
if (!HasLabels)
return Identifier();
return getLabelsBuffer()[idx];
}
/// Retrieve the argument label location at a given index.
SourceLoc getLabelLoc(unsigned idx) const {
if (!HasLabelLocs)
return SourceLoc();
return getLabelLocsBuffer()[idx];
}
/// Retrieve the argument at a given index.
Argument get(unsigned idx) const {
return Argument(getLabelLoc(idx), getLabel(idx), getExpr(idx));
}
// MARK: Iterator Logic
unsigned size() const { return NumArgs; }
bool empty() const { return size() == 0; }
class iterator final
: public llvm::indexed_accessor_iterator<iterator, const ArgumentList *,
Argument, const Argument *,
Argument> {
public:
iterator(const ArgumentList *argList, unsigned idx)
: indexed_accessor_iterator(argList, idx) {}
Argument operator*() const {
assert(getBase());
return getBase()->get(getIndex());
}
Argument operator[](unsigned i) const {
assert(getBase());
return getBase()->get(getIndex() + i);
}
};
iterator begin() const { return {this, 0}; }
iterator end() const { return {this, size()}; }
Argument front() const { return get(0); }
Argument back() const { return get(size() - 1); }
Argument operator[](unsigned idx) const { return get(idx); }
// MARK: Trailing Closure Queries
//
// Trailing closure queries cannot be done on a type-checked argument list,
// instead they must be done on the original argument list prior to rewriting
// in CSApply. This can be obtained by querying the \c getOriginalArgs method.
//
// This restriction is due to the fact that trailing closures in type-checked
// argument lists can have an awkward representation. They may be:
// - Nested within variadic expansion exprs, and may not be the first element
// of that expansion.
// - In a position *before* default argument expressions, or even explicitly
// written non-trailing arguments in certain backward scanning argument
// matching cases.
/// Returns the index of the first trailing closure in the argument list, or
/// \c None if there are no trailing closures.
///
/// Note for a type-checked argument list, this must be queried on
/// \c getOriginalArgs instead.
std::optional<unsigned> getFirstTrailingClosureIndex() const {
assert(!HasOriginalArgs && "Query original args instead");
if (RawFirstTrailingClosureIndex == NumArgs)
return std::nullopt;
return RawFirstTrailingClosureIndex;
}
/// The number of trailing closures in the argument list.
///
/// Note for a type-checked argument list, this must be queried on
/// \c getOriginalArgs instead.
unsigned getNumTrailingClosures() const {
assert(!HasOriginalArgs && "Query original args instead");
if (!getFirstTrailingClosureIndex())
return 0;
return size() - *getFirstTrailingClosureIndex();
}
/// Whether any unlabeled or labeled trailing closures are present.
bool hasAnyTrailingClosures() const {
return getOriginalArgs()->getFirstTrailingClosureIndex().has_value();
}
/// Whether a given index is for an unlabeled trailing closure, which is the
/// first trailing closure in the argument list.
///
/// Note for a type-checked argument list, this must be queried on
/// \c getOriginalArgs instead.
bool isUnlabeledTrailingClosureIndex(unsigned i) const {
assert(!HasOriginalArgs && "Query original args instead");
return hasAnyTrailingClosures() && *getFirstTrailingClosureIndex() == i;
}
/// Whether a given index is for a labeled trailing closure in an argument
/// list with multiple trailing closures.
///
/// Note for a type-checked argument list, this must be queried on
/// \c getOriginalArgs instead.
bool isLabeledTrailingClosureIndex(unsigned i) const {
assert(!HasOriginalArgs && "Query original args instead");
if (!hasAnyTrailingClosures())
return false;
return i > *getFirstTrailingClosureIndex() && i < size();
}
/// Whether a given index is for a labeled or unlabeled trailing closure.
///
/// Note for a type-checked argument list, this must be queried on
/// \c getOriginalArgs instead.
bool isTrailingClosureIndex(unsigned i) const {
assert(!HasOriginalArgs && "Query original args instead");
if (!hasAnyTrailingClosures())
return false;
return i >= *getFirstTrailingClosureIndex() && i < size();
}
/// Returns the range of arguments excluding any trailing closures.
///
/// Note for a type-checked argument list, this must be queried on
/// \c getOriginalArgs instead.
iterator_range<iterator> getNonTrailingArgs() const {
assert(!HasOriginalArgs && "Query original args instead");
auto idx = getFirstTrailingClosureIndex();
if (!idx.has_value())
return *this;
return {begin(), iterator(this, *idx)};
}
/// Returns the range of trailing closure arguments.
///
/// Note for a type-checked argument list, this must be queried on
/// \c getOriginalArgs instead.
iterator_range<iterator> getTrailingClosures() const {
assert(!HasOriginalArgs && "Query original args instead");
auto idx = getFirstTrailingClosureIndex();
if (!idx.has_value())
return {end(), end()};
return {iterator(this, *idx), end()};
}
/// Retrieve the first trailing closure argument in the argument list, or
/// \c None if there are no trailing closures.
///
/// Note for a type-checked argument list, this must be queried on
/// \c getOriginalArgs instead.
std::optional<Argument> getFirstTrailingClosure() const {
assert(!HasOriginalArgs && "Query original args instead");
auto idx = getFirstTrailingClosureIndex();
if (!idx.has_value())
return std::nullopt;
return get(*idx);
}
/// Retrieve the source range of any trailing closure arguments, or an empty
/// range if there are no trailing closures.
///
/// Note for a type-checked argument list, this must be queried on
/// \c getOriginalArgs instead.
SourceRange getTrailingSourceRange() const {
assert(!HasOriginalArgs && "Query original args instead");
auto firstClosure = getFirstTrailingClosure();
if (!firstClosure)
return SourceRange();
return SourceRange(firstClosure->getStartLoc(), back().getEndLoc());
}
// MARK: Misc Queries
/// Whether any of the arguments in the argument are inout.
bool hasAnyInOutArgs() const {
return llvm::any_of(*this, [](auto arg) { return arg.isInOut(); });
}
/// Whether the argument list has a single argument, which may be labeled or
/// unlabeled.
bool isUnary() const { return size() == 1; }
/// Whether the argument list has a single unlabeled argument.
bool isUnlabeledUnary() const { return isUnary() && getLabel(0).empty(); }
/// If the argument list has a single argument, which may be labeled or
/// unlabeled, returns its expression, or \c nullptr if this isn't a unary
/// argument list.
Expr *getUnaryExpr() const { return isUnary() ? getExpr(0) : nullptr; }
/// If the argument list has a single unlabeled argument, returns its
/// expression, or \c nullptr if this isn't an unlabeled unary argument list.
Expr *getUnlabeledUnaryExpr() const {
return isUnlabeledUnary() ? getExpr(0) : nullptr;
}
/// Retrieve an array of the argument expressions used in the argument list.
ArrayRef<Expr *> getArgExprs() const { return getExprsBuffer(); }
/// Retrieve an array of the argument labels used in the argument list.
ArrayRef<Identifier>
getArgumentLabels(SmallVectorImpl<Identifier> &scratch) const;
/// If the provided expression appears as one of the argument list's
/// arguments, returns its index. Otherwise returns \c None. By default this
/// will match against semantic sub-expressions, but that may be disabled by
/// passing \c false for \c allowSemantic.
std::optional<unsigned> findArgumentExpr(Expr *expr,
bool allowSemantic = true) const;
/// Creates a TupleExpr or ParenExpr that holds the argument exprs. A
/// ParenExpr will be returned for a single argument, otherwise a TupleExpr.
/// Don't use this function unless you actually need an AST transform.
Expr *packIntoImplicitTupleOrParen(
ASTContext &ctx,
llvm::function_ref<Type(Expr *)> getType = __Expr_getType) const;
/// Whether the argument list matches a given parameter list. This will return
/// \c false if the arity doesn't match, or any of the canonical types or
/// labels don't match. Note that this expects types to be present for the
/// arguments and parameters.
bool matches(ArrayRef<AnyFunctionType::Param> params,
llvm::function_ref<Type(Expr *)> getType = __Expr_getType) const;
/// Returns the argument list prior to being rewritten by the expression
/// type-checker. If the argument list either hasn't been type-checked yet,
/// or was implicitly generated as type-checked, it returns itself.
///
/// Note that returned argument list will still have rewritten argument
/// expressions with types. However the order of the arguments will reflect
/// the call-site as written by the user, default arguments will be stripped,
/// and variadic arguments will be expanded.
ArgumentList *getOriginalArgs() {
if (auto *originalArgs = getStoredOriginalArgs())
return originalArgs;
return this;
}
/// Returns the argument list prior to being rewritten by the expression
/// type-checker. If the argument list either hasn't been type-checked yet,
/// or was implicitly generated as type-checked, it returns itself.
///
/// Note that returned argument list will still have rewritten argument
/// expressions with types. However the order of the arguments will reflect
/// the call-site as written by the user, default arguments will be stripped,
/// and variadic arguments will be expanded.
const ArgumentList *getOriginalArgs() const {
if (auto *originalArgs = getStoredOriginalArgs())
return originalArgs;
return this;
}
ArgumentList *walk(ASTWalker &walker);
SWIFT_DEBUG_DUMP;
void dump(raw_ostream &OS, unsigned Indent = 0) const;
};
} // end namespace swift
#endif