//===--- ArgumentList.cpp - 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 logic for the Argument and ArgumentList classes. // //===----------------------------------------------------------------------===// #include "swift/AST/ArgumentList.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Expr.h" #include "swift/AST/ParameterList.h" #include "swift/Basic/Assertions.h" using namespace swift; Type swift::__Expr_getType(Expr *E) { return E->getType(); } SourceRange Argument::getSourceRange() const { return SourceRange::combine(getLabelLoc(), getExpr()->getSourceRange()); } Argument Argument::implicitInOut(ASTContext &ctx, Expr *expr) { assert(!isa(expr) && "Cannot nest InOutExpr"); // Eventually this will set an 'inout' bit on Argument, but for now, // synthesize in the InOutExpr. Type objectTy; if (auto subTy = expr->getType()) objectTy = subTy->castTo()->getObjectType(); return Argument::unlabeled( new (ctx) InOutExpr(SourceLoc(), expr, objectTy, /*isImplicit*/ true)); } bool Argument::isInOut() const { return ArgExpr->isSemanticallyInOutExpr(); } bool Argument::isCompileTimeLiteral() const { return ArgExpr->isSemanticallyConstExpr(); } ArgumentList *ArgumentList::create( ASTContext &ctx, SourceLoc lParenLoc, ArrayRef args, SourceLoc rParenLoc, std::optional firstTrailingClosureIndex, bool isImplicit, ArgumentList *originalArgs, AllocationArena arena) { SmallVector exprs; SmallVector labels; SmallVector labelLocs; bool hasLabels = false; bool hasLabelLocs = false; for (auto &arg : args) { exprs.push_back(arg.getExpr()); hasLabels |= !arg.getLabel().empty(); labels.push_back(arg.getLabel()); hasLabelLocs |= arg.getLabelLoc().isValid(); labelLocs.push_back(arg.getLabelLoc()); } if (!hasLabels) labels.clear(); if (!hasLabelLocs) labelLocs.clear(); auto numBytes = totalSizeToAlloc( exprs.size(), labels.size(), labelLocs.size(), originalArgs ? 1 : 0); auto *mem = ctx.Allocate(numBytes, alignof(ArgumentList), arena); auto *argList = new (mem) ArgumentList(lParenLoc, rParenLoc, args.size(), firstTrailingClosureIndex, originalArgs, isImplicit, hasLabels, hasLabelLocs); std::uninitialized_copy(exprs.begin(), exprs.end(), argList->getExprsBuffer().begin()); if (hasLabels) { std::uninitialized_copy(labels.begin(), labels.end(), argList->getLabelsBuffer().begin()); } if (hasLabelLocs) { std::uninitialized_copy(labelLocs.begin(), labelLocs.end(), argList->getLabelLocsBuffer().begin()); } if (originalArgs) { *argList->getTrailingObjects() = originalArgs; } return argList; } ArgumentList * ArgumentList::createParsed(ASTContext &ctx, SourceLoc lParenLoc, ArrayRef args, SourceLoc rParenLoc, std::optional firstTrailingClosureIndex) { return create(ctx, lParenLoc, args, rParenLoc, firstTrailingClosureIndex, /*implicit*/ false); } ArgumentList *ArgumentList::createTypeChecked(ASTContext &ctx, ArgumentList *originalArgs, ArrayRef newArgs) { return create(ctx, originalArgs->getLParenLoc(), newArgs, originalArgs->getRParenLoc(), /*trailingClosureIdx*/ std::nullopt, originalArgs->isImplicit(), originalArgs); } ArgumentList * ArgumentList::createImplicit(ASTContext &ctx, SourceLoc lParenLoc, ArrayRef args, SourceLoc rParenLoc, std::optional firstTrailingClosureIndex, AllocationArena arena) { return create(ctx, lParenLoc, args, rParenLoc, firstTrailingClosureIndex, /*implicit*/ true, /*originalArgs*/ nullptr, arena); } ArgumentList * ArgumentList::createImplicit(ASTContext &ctx, ArrayRef args, std::optional firstTrailingClosureIndex, AllocationArena arena) { return createImplicit(ctx, SourceLoc(), args, SourceLoc(), firstTrailingClosureIndex, arena); } ArgumentList *ArgumentList::forImplicitSingle(ASTContext &ctx, Identifier label, Expr *arg) { return createImplicit(ctx, {Argument(SourceLoc(), label, arg)}); } ArgumentList *ArgumentList::forImplicitUnlabeled(ASTContext &ctx, ArrayRef argExprs) { SmallVector args; for (auto *argExpr : argExprs) args.push_back(Argument::unlabeled(argExpr)); return createImplicit(ctx, args); } ArgumentList *ArgumentList::forImplicitCallTo(DeclNameRef fnNameRef, ArrayRef argExprs, ASTContext &ctx) { auto labels = fnNameRef.getArgumentNames(); assert(labels.size() == argExprs.size()); SmallVector args; for (auto idx : indices(argExprs)) args.emplace_back(SourceLoc(), labels[idx], argExprs[idx]); return createImplicit(ctx, args); } ArgumentList *ArgumentList::forImplicitCallTo(ParameterList *params, ArrayRef argExprs, ASTContext &ctx) { assert(params->size() == argExprs.size()); SmallVector args; for (auto idx : indices(argExprs)) { auto *param = params->get(idx); assert(param->isInOut() == argExprs[idx]->isSemanticallyInOutExpr()); args.emplace_back(SourceLoc(), param->getArgumentName(), argExprs[idx]); } return createImplicit(ctx, args); } SourceLoc ArgumentList::getLoc() const { // If we have an unlabeled unary arg, return the start loc of the expr. This // preserves the behavior of when such argument lists were represented by // ParenExprs. if (auto *unary = getUnlabeledUnaryExpr()) return unary->getStartLoc(); return getStartLoc(); } SourceRange ArgumentList::getSourceRange() const { auto start = LParenLoc; if (start.isInvalid()) { // Scan forward for the first valid source loc. for (auto arg : *this) { start = arg.getStartLoc(); if (start.isValid()) break; } } auto end = RParenLoc; if (hasAnyTrailingClosures() || RParenLoc.isInvalid()) { // Scan backward for the first valid source loc. We use getOriginalArgs to // filter out default arguments and get accurate trailing closure info. for (auto arg : llvm::reverse(*getOriginalArgs())) { end = arg.getEndLoc(); if (end.isValid()) break; } } if (start.isInvalid() || end.isInvalid()) return SourceRange(); return SourceRange(start, end); } ArrayRef ArgumentList::getArgumentLabels(SmallVectorImpl &scratch) const { assert(scratch.empty()); if (HasLabels) return getLabelsBuffer(); scratch.append(size(), Identifier()); return scratch; } std::optional ArgumentList::findArgumentExpr(Expr *expr, bool allowSemantic) const { if (allowSemantic) expr = expr->getSemanticsProvidingExpr(); for (auto idx : indices(*this)) { auto *argExpr = getExpr(idx); if (allowSemantic) argExpr = argExpr->getSemanticsProvidingExpr(); if (expr == argExpr) return idx; } return std::nullopt; } Expr *ArgumentList::packIntoImplicitTupleOrParen( ASTContext &ctx, llvm::function_ref getType) const { assert(!hasAnyInOutArgs() && "Cannot construct bare tuple/paren with inout"); // Make sure to preserve the source location info here and below as it may be // needed for e.g serialization of its textual representation. if (auto *unary = getUnlabeledUnaryExpr()) { auto *paren = new (ctx) ParenExpr(getLParenLoc(), unary, getRParenLoc()); if (auto ty = getType(unary)) paren->setType(ty); paren->setImplicit(); return paren; } SmallVector argExprs; SmallVector argLabels; SmallVector argLabelLocs; SmallVector tupleEltTypes; for (auto arg : *this) { auto *argExpr = arg.getExpr(); argExprs.push_back(argExpr); argLabels.push_back(arg.getLabel()); argLabelLocs.push_back(arg.getLabelLoc()); if (auto ty = getType(argExpr)) tupleEltTypes.emplace_back(ty, arg.getLabel()); } assert(tupleEltTypes.empty() || tupleEltTypes.size() == argExprs.size()); auto *tuple = TupleExpr::create(ctx, getLParenLoc(), argExprs, argLabels, argLabelLocs, getRParenLoc(), /*implicit*/ true); if (empty() || !tupleEltTypes.empty()) tuple->setType(TupleType::get(tupleEltTypes, ctx)); return tuple; } bool ArgumentList::matches(ArrayRef params, llvm::function_ref getType) const { if (size() != params.size()) return false; for (auto i : indices(*this)) { auto arg = get(i); auto ¶m = params[i]; if (arg.getLabel() != param.getLabel()) return false; if (arg.isInOut() != param.isInOut()) return false; auto argTy = getType(arg.getExpr()); assert(argTy && "Expected type for argument"); auto paramTy = param.getParameterType(); assert(paramTy && "Expected a type for param"); if (arg.isInOut()) argTy = argTy->getInOutObjectType(); if (!argTy->isEqual(paramTy)) return false; } return true; }