diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index e2d623ee84262..5e1eb43713566 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1228,6 +1228,9 @@ ERROR(expected_type_after_is,none, "expected type after 'is'", ()) ERROR(expected_type_after_as,none, "expected type after 'as'", ()) +ERROR(invalid_ellipsis_after_as_type,none, + "coercion to variadic arguments using '%0' is not allowed; did you mean" + " to use 'as'?", (StringRef)) // Extra tokens in string interpolation like in " >> \( $0 } ) << " ERROR(string_interpolation_extra,none, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index fe2985cf46aee..160d090be45e8 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -356,6 +356,23 @@ ERROR(cannot_convert_default_arg_value_nil,none, ERROR(cannot_convert_argument_value,none, "cannot convert value of type %0 to expected argument type %1", (Type,Type)) + +ERROR(cannot_convert_array_to_variadic,none, + "cannot pass an array of type %0 as variadic arguments of type %1", + (Type,Type)) +ERROR(vararg_expansion_must_appear_alone,none, + "array elements coerced to variadic arguments cannot be used alongside " + "additional variadic arguments", + ()) +ERROR(array_to_vararg_coercion_outside_arg_position,none, + "coercion to variadic arguments is only allowed in an argument position", + ()) + +NOTE(suggest_pass_elements_directly,none, + "remove brackets to pass array elements directly", ()) +NOTE(suggest_pass_elements_using_vararg_expansion,none, + "use 'as' to pass array elements as variadic arguments", ()) + ERROR(cannot_convert_argument_value_generic,none, "cannot convert value of type %0 (%1) to expected argument type %2 (%3)", (Type, StringRef, Type, StringRef)) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index f879e5aa9aeb7..a33d6b2f5e3e2 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -61,6 +61,7 @@ namespace swift { class EnumElementDecl; class CallExpr; class KeyPathExpr; + class CoerceExpr; enum class ExprKind : uint8_t { #define EXPR(Id, Parent) Id, @@ -3342,14 +3343,19 @@ class InOutExpr : public Expr { } }; -/// The not-yet-actually-surfaced '...' varargs expansion operator, -/// which splices an array into a sequence of variadic arguments. +/// The varargs expansion operator, which expands an array into a sequence of +/// variadic arguments. Is written in source as part of a coercion operation, +/// e.g. 'as T...' class VarargExpansionExpr : public Expr { Expr *SubExpr; public: VarargExpansionExpr(Expr *subExpr, bool implicit, Type type = Type()) - : Expr(ExprKind::VarargExpansion, implicit, type), SubExpr(subExpr) {} + : Expr(ExprKind::VarargExpansion, implicit, type), SubExpr(subExpr) { + assert(implicit || + isa(subExpr) && + "SubExpr of explicit VarargExpansionExpr must be a CoerceExpr"); + } SWIFT_FORWARD_SOURCE_LOCS_TO(SubExpr) @@ -4414,15 +4420,17 @@ class CoerceExpr : public ExplicitCastExpr { /// we use it to store `start` of the initializer /// call source range to save some storage. SourceLoc InitRangeEnd; + bool IncludesVarargExpansion; public: - CoerceExpr(Expr *sub, SourceLoc asLoc, TypeLoc type) - : ExplicitCastExpr(ExprKind::Coerce, sub, asLoc, type, type.getType()) - { } + CoerceExpr(Expr *sub, SourceLoc asLoc, TypeLoc type, + bool includesVarargExpansion = false) + : ExplicitCastExpr(ExprKind::Coerce, sub, asLoc, type, type.getType()), + IncludesVarargExpansion(includesVarargExpansion) {} - CoerceExpr(SourceLoc asLoc, TypeLoc type) - : CoerceExpr(nullptr, asLoc, type) - { } + CoerceExpr(SourceLoc asLoc, TypeLoc type, + bool includesVarargExpansion = false) + : CoerceExpr(nullptr, asLoc, type, includesVarargExpansion) {} private: CoerceExpr(SourceRange initRange, Expr *literal, TypeLoc type) @@ -4441,6 +4449,8 @@ class CoerceExpr : public ExplicitCastExpr { bool isLiteralInit() const { return InitRangeEnd.isValid(); } + bool includesVarargExpansion() const { return IncludesVarargExpansion; } + SourceRange getSourceRange() const { return isLiteralInit() ? SourceRange(getAsLoc(), InitRangeEnd) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 84f1839b156bd..ab571a17edcd7 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -88,7 +88,7 @@ ParserResult Parser::parseExprIs() { /// parseExprAs /// expr-as: -/// 'as' type +/// 'as' type '...'? /// 'as?' type /// 'as!' type ParserResult Parser::parseExprAs() { @@ -111,16 +111,38 @@ ParserResult Parser::parseExprAs() { if (type.isNull()) return nullptr; + bool includesVarargExpansion = false; + SourceLoc ellipsisLoc; + auto typeRepr = type.get(); + if (Tok.is(tok::oper_postfix) && Tok.isEllipsis()) { + ellipsisLoc = consumeToken(); + includesVarargExpansion = true; + typeRepr = + new (Context) ArrayTypeRepr(typeRepr, typeRepr->getSourceRange()); + } + ParserStatus status; Expr *parsed; if (questionLoc.isValid()) { - parsed = new (Context) ConditionalCheckedCastExpr(asLoc, questionLoc, - type.get()); + if (includesVarargExpansion) { + diagnose(asLoc, diag::invalid_ellipsis_after_as_type, "as?") + .fixItRemove(questionLoc); + status.setIsParseError(); + } + parsed = + new (Context) ConditionalCheckedCastExpr(asLoc, questionLoc, typeRepr); } else if (exclaimLoc.isValid()) { - parsed = new (Context) ForcedCheckedCastExpr(asLoc, exclaimLoc, type.get()); + if (includesVarargExpansion) { + diagnose(asLoc, diag::invalid_ellipsis_after_as_type, "as!") + .fixItRemove(exclaimLoc); + status.setIsParseError(); + } + parsed = new (Context) ForcedCheckedCastExpr(asLoc, exclaimLoc, typeRepr); } else { - parsed = new (Context) CoerceExpr(asLoc, type.get()); + parsed = new (Context) CoerceExpr(asLoc, typeRepr, includesVarargExpansion); } - return makeParserResult(parsed); + return status.isError() ? makeParserErrorResult( + new (Context) ErrorExpr({asLoc, ellipsisLoc})) + : makeParserResult(parsed); } /// parseExprArrow diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 276cb522e8f1f..2c4487201cc9f 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5318,10 +5318,11 @@ Expr *ExprRewriter::coerceCallArguments( auto params = funcType->getParams(); // Local function to produce a locator to refer to the given parameter. - auto getArgLocator = [&](unsigned argIdx, unsigned paramIdx) - -> ConstraintLocatorBuilder { + auto getArgLocator = + [&](unsigned argIdx, unsigned paramIdx, + ParameterTypeFlags flags) -> ConstraintLocatorBuilder { return locator.withPathElement( - LocatorPathElt::getApplyArgToParam(argIdx, paramIdx)); + LocatorPathElt::getApplyArgToParam(argIdx, paramIdx, flags)); }; bool matchCanFail = @@ -5421,20 +5422,39 @@ Expr *ExprRewriter::coerceCallArguments( else newLabelLocs.push_back(SourceLoc()); + bool isVarargExpansion = false; // Convert the arguments. for (auto argIdx : varargIndices) { auto arg = getArg(argIdx); auto argType = cs.getType(arg); + if (isa(arg) && varargIndices.size() > 1) { + tc.diagnose(arg->getLoc(), diag::vararg_expansion_must_appear_alone); + return nullptr; + } else if (isa(arg)) { + isVarargExpansion = true; + } + + auto paramType = + isVarargExpansion ? param.getParameterType() : param.getPlainType(); + // If the argument type exactly matches, this just works. - if (argType->isEqual(param.getPlainType())) { + if (argType->isEqual(paramType)) { variadicArgs.push_back(arg); continue; } // Convert the argument. - auto convertedArg = coerceToType(arg, param.getPlainType(), - getArgLocator(argIdx, paramIdx)); + Expr *convertedArg; + auto argLocator = getArgLocator(argIdx, paramIdx, + param.getParameterFlags()); + if (isVarargExpansion) { + convertedArg = buildCollectionUpcastExpr(arg, paramType, false, + argLocator); + } else { + convertedArg = coerceToType(arg, paramType, argLocator); + } + if (!convertedArg) return nullptr; @@ -5448,21 +5468,22 @@ Expr *ExprRewriter::coerceCallArguments( end = variadicArgs.back()->getEndLoc(); } - // Collect them into an ArrayExpr. - auto *arrayExpr = ArrayExpr::create(tc.Context, - start, - variadicArgs, - {}, end, - param.getParameterType()); - arrayExpr->setImplicit(); - cs.cacheType(arrayExpr); + Expr *varargExpansionExpr; + if (variadicArgs.empty() || !isVarargExpansion) { + // Collect them into an ArrayExpr. + auto *arrayExpr = ArrayExpr::create(tc.Context, start, variadicArgs, {}, + end, param.getParameterType()); + arrayExpr->setImplicit(); + cs.cacheType(arrayExpr); - // Wrap the ArrayExpr in a VarargExpansionExpr. - auto *varargExpansionExpr = - new (tc.Context) VarargExpansionExpr(arrayExpr, - /*implicit=*/true, - arrayExpr->getType()); - cs.cacheType(varargExpansionExpr); + // Wrap the ArrayExpr in a VarargExpansionExpr. + varargExpansionExpr = new (tc.Context) + VarargExpansionExpr(arrayExpr, + /*implicit=*/true, arrayExpr->getType()); + cs.cacheType(varargExpansionExpr); + } else { + varargExpansionExpr = variadicArgs.front(); + } newArgs.push_back(varargExpansionExpr); newParams.push_back(param); @@ -5554,8 +5575,9 @@ Expr *ExprRewriter::coerceCallArguments( convertedArg = cs.TC.buildAutoClosureExpr(dc, arg, closureType); cs.cacheExprTypes(convertedArg); } else { - convertedArg = - coerceToType(arg, paramType, getArgLocator(argIdx, paramIdx)); + convertedArg = coerceToType( + arg, paramType, + getArgLocator(argIdx, paramIdx, param.getParameterFlags())); } if (!convertedArg) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 5c6caee3cb643..f2e76090edaf3 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -2760,7 +2760,10 @@ typeCheckArgumentChildIndependently(Expr *argExpr, Type argType, resultElts.push_back(elExpr); auto resFlags = - ParameterTypeFlags().withInOut(elExpr->isSemanticallyInOutExpr()); + ParameterTypeFlags() + .withInOut(elExpr->isSemanticallyInOutExpr()) + .withVariadic(elExpr->getSemanticsProvidingExpr()->getKind() == + ExprKind::VarargExpansion); resultEltTys.push_back({CS.getType(elExpr)->getInOutObjectType(), TE->getElementName(i), resFlags}); } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 8c8532d38f886..688a493e69e9b 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -813,10 +813,19 @@ bool MissingForcedDowncastFailure::diagnoseAsError() { case CheckedCastKind::DictionaryDowncast: case CheckedCastKind::SetDowncast: case CheckedCastKind::ValueCast: - emitDiagnostic(coerceExpr->getLoc(), diag::missing_forced_downcast, - fromType, toType) - .highlight(coerceExpr->getSourceRange()) - .fixItReplace(coerceExpr->getLoc(), "as!"); + auto diag = emitDiagnostic(coerceExpr->getLoc(), + diag::missing_forced_downcast, fromType, toType); + diag.highlight(coerceExpr->getSourceRange()); + if (coerceExpr->includesVarargExpansion()) { + diag.fixItInsert(coerceExpr->getStartLoc(), "("); + llvm::SmallString<32> insertAfter; + insertAfter += " as! "; + insertAfter += toType->getWithoutParens()->getString(); + insertAfter += ")"; + diag.fixItInsertAfter(subExpr->getEndLoc(), insertAfter); + } else { + diag.fixItReplace(coerceExpr->getLoc(), "as!"); + } return true; } llvm_unreachable("unhandled cast kind"); @@ -3493,3 +3502,48 @@ bool MutatingMemberRefOnImmutableBase::diagnoseAsError() { diagIDmember); return failure.diagnoseAsError(); } + +bool ExpandArrayIntoVarargsFailure::diagnoseAsError() { + if (auto anchor = getAnchor()) { + emitDiagnostic(anchor->getLoc(), diag::cannot_convert_array_to_variadic, + getFromType(), getToType()); + + // Offer to pass the array elements using 'as T...' + auto *DC = getDC(); + auto &TC = getTypeChecker(); + auto asPG = getTypeChecker().lookupPrecedenceGroup( + DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()); + // Parens are never needed on the outside because 'as T...' is only allowed + // as an argument + bool needsParensInside = + !asPG || exprNeedsParensInsideFollowingOperator(TC, DC, anchor, asPG); + + llvm::SmallString<2> insertBefore; + llvm::SmallString<32> insertAfter; + if (needsParensInside) { + insertBefore += "("; + insertAfter += ")"; + } + insertAfter += " as "; + insertAfter += getToType()->getWithoutParens()->getString(); + insertAfter += "..."; + emitDiagnostic(anchor->getLoc(), + diag::suggest_pass_elements_using_vararg_expansion) + .fixItInsert(anchor->getStartLoc(), insertBefore) + .fixItInsertAfter(anchor->getEndLoc(), insertAfter); + + // If this is an array literal, offer to remove the brackets and pass the + // elements directly as variadic arguments. + if (auto *arrayExpr = dyn_cast(anchor)) { + auto diag = emitDiagnostic(arrayExpr->getLoc(), + diag::suggest_pass_elements_directly); + diag.fixItRemove(arrayExpr->getLBracketLoc()) + .fixItRemove(arrayExpr->getRBracketLoc()); + // Handle the case where the array literal has a trailing comma. + if (arrayExpr->getNumCommas() == arrayExpr->getNumElements()) + diag.fixItRemove(arrayExpr->getCommaLocs().back()); + } + return true; + } + return false; +} diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 915a950614898..e7e8d1e4b8dd6 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1457,6 +1457,15 @@ class SkipUnhandledConstructInFunctionBuilderFailure final bool diagnoseAsNote() override; }; +class ExpandArrayIntoVarargsFailure final : public ContextualFailure { +public: + ExpandArrayIntoVarargsFailure(Expr *root, ConstraintSystem &cs, Type lhs, + Type rhs, ConstraintLocator *locator) + : ContextualFailure(root, cs, lhs, rhs, locator) {} + + bool diagnoseAsError() override; +}; + /// Provides information about the application of a function argument to a /// parameter. class FunctionArgApplyInfo { diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 8237bae40aa67..7b73026abb4db 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -657,3 +657,16 @@ AllowMutatingMemberOnRValueBase::create(ConstraintSystem &cs, Type baseType, return new (cs.getAllocator()) AllowMutatingMemberOnRValueBase(cs, baseType, member, name, locator); } + +ExpandArrayIntoVarargs * +ExpandArrayIntoVarargs::create(ConstraintSystem &cs, Type srcType, Type dstType, + ConstraintLocator *locator) { + return new (cs.getAllocator()) + ExpandArrayIntoVarargs(cs, srcType, dstType, locator); +} + +bool ExpandArrayIntoVarargs::diagnose(Expr *root, bool asNote) const { + ExpandArrayIntoVarargsFailure failure( + root, getConstraintSystem(), getFromType(), getToType(), getLocator()); + return failure.diagnose(asNote); +} diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 569042c93b0e9..cb043d9eb831a 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -183,6 +183,10 @@ enum class FixKind : uint8_t { /// Allow invalid reference to a member declared as `mutating` /// when base is an r-value type. AllowMutatingMemberOnRValueBase, + + /// If an array was passed to a variadic argument, offer to coerce it to + /// varargs, or drop the brackets if it's a literal + ExpandArrayIntoVarargs, }; class ConstraintFix { @@ -490,6 +494,10 @@ class ContextualMismatch : public ConstraintFix { Type LHS, RHS; protected: + ContextualMismatch(ConstraintSystem &cs, FixKind fixKind, Type lhs, Type rhs, + ConstraintLocator *locator) + : ConstraintFix(cs, fixKind, locator), LHS(lhs), RHS(rhs) {} + ContextualMismatch(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) : ConstraintFix(cs, FixKind::ContextualMismatch, locator), LHS(lhs), @@ -575,7 +583,8 @@ class GenericArgumentsMismatch final class KeyPathContextualMismatch final : public ContextualMismatch { KeyPathContextualMismatch(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) - : ContextualMismatch(cs, lhs, rhs, locator) {} + : ContextualMismatch(cs, FixKind::ContextualMismatch, lhs, rhs, locator) { + } public: std::string getName() const override { @@ -1100,7 +1109,8 @@ class RemoveReturn final : public ConstraintFix { class CollectionElementContextualMismatch final : public ContextualMismatch { CollectionElementContextualMismatch(ConstraintSystem &cs, Type srcType, Type dstType, ConstraintLocator *locator) - : ContextualMismatch(cs, srcType, dstType, locator) {} + : ContextualMismatch(cs, FixKind::ContextualMismatch, srcType, dstType, + locator) {} public: std::string getName() const override { @@ -1181,6 +1191,26 @@ class SkipUnhandledConstructInFunctionBuilder final : public ConstraintFix { NominalTypeDecl *builder, ConstraintLocator *locator); }; +class ExpandArrayIntoVarargs final : public ContextualMismatch { + + ExpandArrayIntoVarargs(ConstraintSystem &cs, Type srcType, Type dstType, + ConstraintLocator *locator) + : ContextualMismatch(cs, FixKind::ExpandArrayIntoVarargs, srcType, + dstType, locator) {} + +public: + std::string getName() const override { + return "require explicit coercion when passing Array elements as variadic " + "arguments"; + } + + bool diagnose(Expr *root, bool asNote = false) const override; + + static ExpandArrayIntoVarargs *create(ConstraintSystem &cs, Type srcType, + Type dstType, + ConstraintLocator *locator); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index f504feb6a9ed2..fa64a0137fa11 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3351,13 +3351,10 @@ namespace { bool isSyntheticArgumentExpr(const Expr *expr) { if (isa(expr) || - isa(expr)) + isa(expr) || + isa(expr)) return true; - if (auto *varargExpr = dyn_cast(expr)) - if (isa(varargExpr->getSubExpr())) - return true; - return false; } @@ -3929,7 +3926,8 @@ swift::getOriginalArgumentList(Expr *expr) { } if (auto *varargExpr = dyn_cast(arg)) { - if (auto *arrayExpr = dyn_cast(varargExpr->getSubExpr())) { + auto *arrayExpr = dyn_cast(varargExpr->getSubExpr()); + if (varargExpr->isImplicit() && arrayExpr) { for (auto *elt : arrayExpr->getElements()) { result.args.push_back(elt); result.labels.push_back(label); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index a9d241c79492c..fcedc6185aa67 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -403,13 +403,6 @@ matchCallArguments(ArrayRef args, // Record the first argument for the variadic. parameterBindings[paramIdx].push_back(*claimed); - // If the argument is itself variadic, we're forwarding varargs - // with a VarargExpansionExpr; don't collect any more arguments. - if (args[*claimed].isVariadic()) { - skipClaimedArgs(); - return; - } - auto currentNextArgIdx = nextArgIdx; { nextArgIdx = *claimed; @@ -970,9 +963,8 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( // Compare each of the bound arguments for this parameter. for (auto argIdx : parameterBindings[paramIdx]) { - auto loc = locator.withPathElement(LocatorPathElt:: - getApplyArgToParam(argIdx, - paramIdx)); + auto loc = locator.withPathElement(LocatorPathElt::getApplyArgToParam( + argIdx, paramIdx, param.getParameterFlags())); auto argTy = argsWithLabels[argIdx].getOldType(); bool matchingAutoClosureResult = param.isAutoClosure(); @@ -2287,6 +2279,13 @@ bool ConstraintSystem::repairFailures( rhs)) { conversionsOrFixes.push_back(fix); } + + if (isArrayType(lhs) && elt.getParameterFlags().isVariadic()) { + conversionsOrFixes.push_back(ExpandArrayIntoVarargs::create( + *this, lhs, rhs, loc)); + return true; + } + break; } @@ -6961,6 +6960,16 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } + case FixKind::ExpandArrayIntoVarargs: { + auto result = matchTypes(type1, ArraySliceType::get(type2), matchKind, + subflags, locator); + if (result == SolutionKind::Solved) + if (recordFix(fix)) + return SolutionKind::Error; + + return result; + } + case FixKind::UseSubscriptOperator: case FixKind::ExplicitlyEscaping: case FixKind::CoerceToCheckedCast: diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index c7f1460177cd1..1952c46931f2a 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -49,6 +49,12 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor, id.AddPointer(elt.getKeyPath()); break; + case ApplyArgToParam: + id.AddInteger(elt.getValue()); + id.AddInteger(elt.getValue2()); + id.AddInteger(elt.getParameterFlags().toRaw()); + break; + case ApplyArgument: case ApplyFunction: case FunctionArgument: @@ -70,7 +76,6 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor, case GenericArgument: case NamedTupleElement: case TupleElement: - case ApplyArgToParam: case OpenedGeneric: case KeyPathComponent: case ConditionalRequirement: diff --git a/lib/Sema/ConstraintLocator.h b/lib/Sema/ConstraintLocator.h index f5e858de9b732..1e4b1579ef164 100644 --- a/lib/Sema/ConstraintLocator.h +++ b/lib/Sema/ConstraintLocator.h @@ -184,8 +184,10 @@ class ConstraintLocator : public llvm::FoldingSetNode { case TypeParameterRequirement: case ConditionalRequirement: - case ApplyArgToParam: return 2; + + case ApplyArgToParam: + return 3; } llvm_unreachable("Unhandled PathElementKind in switch."); @@ -282,12 +284,12 @@ class ConstraintLocator : public llvm::FoldingSetNode { uint64_t storedKind : 3; /// Encode a path element kind and a value into the storage format. - static uint64_t encodeStorage(PathElementKind kind, unsigned value) { - return ((uint64_t)value << 8) | kind; + static uint64_t encodeStorage(PathElementKind kind, uint64_t value) { + return (value << 8) | kind; } /// Decode a storage value into path element kind and value. - static std::pair + static std::pair decodeStorage(uint64_t storage) { return { (PathElementKind)((unsigned)storage & 0xFF), storage >> 8 }; } @@ -307,6 +309,15 @@ class ConstraintLocator : public llvm::FoldingSetNode { "Path element kind does not require 2 values"); } + PathElement(PathElementKind kind, unsigned value1, unsigned value2, + unsigned value3) + : storage(encodeStorage(kind, ((uint64_t)value1 << 32) | value2 << 16 | + value3)), + storedKind(StoredKindAndValue) { + assert(numNumericValuesInPathElement(kind) == 3 && + "Path element kind does not require three values"); + } + PathElement(GenericSignature *sig) : storage((reinterpret_cast(sig) >> 3)), storedKind(StoredGenericSignature) {} @@ -358,8 +369,10 @@ class ConstraintLocator : public llvm::FoldingSetNode { /// Retrieve a path element for an argument/parameter comparison in a /// function application. - static PathElement getApplyArgToParam(unsigned argIdx, unsigned paramIdx) { - return PathElement(ApplyArgToParam, argIdx, paramIdx); + static PathElement getApplyArgToParam(unsigned argIdx, unsigned paramIdx, + ParameterTypeFlags flags) { + unsigned rawFlags = flags.toRaw(); + return PathElement(ApplyArgToParam, argIdx, paramIdx, rawFlags); } /// Retrieve a path element for a generic argument referred to by @@ -432,24 +445,25 @@ class ConstraintLocator : public llvm::FoldingSetNode { unsigned getValue() const { unsigned numValues = numNumericValuesInPathElement(getKind()); assert(numValues > 0 && "No value in path element!"); - - auto value = decodeStorage(storage).second; - if (numValues == 1) { - return value; - } - - return value >> 16; + auto extraValues = numValues - 1; + return decodeStorage(storage).second >> (extraValues * 16); } /// Retrieve the second value associated with this path element, /// if it has one. unsigned getValue2() const { unsigned numValues = numNumericValuesInPathElement(getKind()); - (void)numValues; - assert(numValues == 2 && "No second value in path element!"); + assert(numValues >= 2 && "No second value in path element!"); + auto extraValues = numValues - 2; + auto value = decodeStorage(storage).second >> (extraValues * 16); + return value & 0x00FFFF; + } + ParameterTypeFlags getParameterFlags() const { + assert(getKind() == ApplyArgToParam && "Is not a param application"); auto value = decodeStorage(storage).second; - return value & 0x00FFFF; + uint8_t rawFlags = value & 0xFF; + return ParameterTypeFlags::fromRaw(rawFlags); } /// Retrieve the declaration for a witness path element. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 4af4fc757d480..018244ea8b404 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -1140,13 +1140,16 @@ namespace { return finish(true, expr); } - // Let's try to figure out if `InOutExpr` is out of place early - // otherwise there is a risk of producing solutions which can't - // be later applied to AST and would result in the crash in some + // Let's try to figure out if `InOutExpr` or `VarargExpansionExpr` is out + // of place early. Otherwise there is a risk of producing solutions which + // can't be later applied to AST and would result in the crash in some // cases. Such expressions are only allowed in argument positions // of function/operator calls. - if (isa(expr)) { - // If this is an implicit `inout` expression we assume that + if (isa(expr) || isa(expr)) { + auto diag = isa(expr) + ? diag::extraneous_address_of + : diag::array_to_vararg_coercion_outside_arg_position; + // If this is an implicit `inout` or expansion expression we assume that // compiler knowns what it's doing. if (expr->isImplicit()) return finish(true, expr); @@ -1156,11 +1159,18 @@ namespace { return finish(true, expr); auto parents = ParentExpr->getParentMap(); - - auto result = parents.find(expr); - if (result != parents.end()) { - auto *parent = result->getSecond(); - + Expr *parent = nullptr; + if (isa(expr)) { + auto result = parents.find(expr); + if (result != parents.end()) { + parent = result->getSecond(); + } + } else if (isa(expr) && !ExprStack.empty()) { + // Use ExprStack to find the first parent. This accounts for the case + // where expressions were folded after ParentExpr was set + parent = ExprStack.back(); + } + if (parent) { if (isa(parent)) return finish(true, expr); @@ -1171,16 +1181,18 @@ namespace { isa(call->getSecond())) return finish(true, expr); - if (isa(call->getSecond())) { + if (isa(expr) && + isa(call->getSecond())) { TC.diagnose(expr->getStartLoc(), diag::cannot_pass_inout_arg_to_subscript); return finish(false, nullptr); - } + } else if (isa(call->getSecond())) + return finish(true, expr); } } } - TC.diagnose(expr->getStartLoc(), diag::extraneous_address_of); + TC.diagnose(expr->getStartLoc(), diag); return finish(false, nullptr); } diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index 7659247093928..6366410143310 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -375,7 +375,13 @@ static Expr *makeBinOp(TypeChecker &TC, Expr *Op, Expr *LHS, Expr *RHS, // Resolve the 'as' or 'is' expression. assert(!as->isFolded() && "already folded 'as' expr in sequence?!"); assert(RHS == as && "'as' with non-type RHS?!"); - as->setSubExpr(LHS); + as->setSubExpr(LHS); + auto coercion = dyn_cast(as); + if (coercion && coercion->includesVarargExpansion()) { + auto expansionExpr = + new (TC.Context) VarargExpansionExpr(coercion, false); + return makeResultExpr(expansionExpr); + } return makeResultExpr(as); } diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 0bed7736da541..24faca4ab8937 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -300,6 +300,61 @@ func rdar21784170() { (Array.init as (Double...) -> Array)(initial as (Double, Double)) // expected-error {{cannot convert value of type '(Double, Double)' to expected argument type 'Double'}} } +// Diagnose passing an array in lieu of variadic parameters +func variadic(_ x: Int...) {} +func variadicArrays(_ x: [Int]...) {} +func variadicAny(_ x: Any...) {} +struct HasVariadicSubscript { + subscript(_ x: Int...) -> Int { + get { 0 } + } +} +let foo = HasVariadicSubscript() + +let array = [1,2,3] +let arrayWithOtherEltType = ["hello", "world"] + +variadic(array) // expected-error {{cannot pass an array of type '[Int]' as variadic arguments of type 'Int'}} +// expected-note@-1 {{use 'as' to pass array elements as variadic arguments}} {{15-15= as Int...}} +variadic([1,2,3]) // expected-error {{cannot pass an array of type '[Int]' as variadic arguments of type 'Int'}} +// expected-note@-1 {{use 'as' to pass array elements as variadic arguments}} {{17-17= as Int...}} +// expected-note@-2 {{remove brackets to pass array elements directly}} {{10-11=}} {{16-17=}} +variadic([1,2,3,]) // expected-error {{cannot pass an array of type '[Int]' as variadic arguments of type 'Int'}} +// expected-note@-1 {{use 'as' to pass array elements as variadic arguments}} {{18-18= as Int...}} +// expected-note@-2 {{remove brackets to pass array elements directly}} {{10-11=}} {{16-17=}} {{17-18=}} +variadic(0, array, 4) // expected-error {{cannot pass an array of type '[Int]' as variadic arguments of type 'Int'}} +// expected-note@-1 {{use 'as' to pass array elements as variadic arguments}} +variadic(0, [1,2,3], 4) // expected-error {{cannot pass an array of type '[Int]' as variadic arguments of type 'Int'}} +// expected-note@-1 {{use 'as' to pass array elements as variadic arguments}} +// expected-note@-2 {{remove brackets to pass array elements directly}} {{13-14=}} {{19-20=}} +variadic(0, [1,2,3,], 4) // expected-error {{cannot pass an array of type '[Int]' as variadic arguments of type 'Int'}} +// expected-note@-1 {{use 'as' to pass array elements as variadic arguments}} +// expected-note@-2 {{remove brackets to pass array elements directly}} {{13-14=}} {{19-20=}} {{20-21=}} +variadic(arrayWithOtherEltType) // expected-error {{cannot convert value of type '[String]' to expected argument type 'Int'}} +variadic(1, arrayWithOtherEltType) // expected-error {{cannot convert value of type '[String]' to expected argument type 'Int'}} +variadic(["hello", "world"]) // expected-error 2 {{cannot convert value of type 'String' to expected element type 'Int'}} +// expected-error@-1 {{cannot pass an array of type '[Int]' as variadic arguments of type 'Int'}} +// expected-note@-2 {{use 'as' to pass array elements as variadic arguments}} +// expected-note@-3 {{remove brackets to pass array elements directly}} +variadic([1] + [2] as [Int]) // expected-error {{cannot pass an array of type '[Int]' as variadic arguments of type 'Int'}} +// expected-note@-1 {{use 'as' to pass array elements as variadic arguments}} {{10-10=(}} {{28-28=) as Int...}} + +foo[array] // expected-error {{cannot pass an array of type '[Int]' as variadic arguments of type 'Int'}} +// expected-note@-1 {{use 'as' to pass array elements as variadic arguments}} {{10-10= as Int...}} +foo[[1,2,3]] // expected-error {{cannot pass an array of type '[Int]' as variadic arguments of type 'Int'}} +// expected-note@-1 {{use 'as' to pass array elements as variadic arguments}} {{12-12= as Int...}} +// expected-note@-2 {{remove brackets to pass array elements directly}} {{5-6=}} {{11-12=}} +foo[0, [1,2,3], 4] // expected-error {{cannot pass an array of type '[Int]' as variadic arguments of type 'Int'}} +// expected-note@-1 {{use 'as' to pass array elements as variadic arguments}} +// expected-note@-2 {{remove brackets to pass array elements directly}} {{8-9=}} {{14-15=}} + +variadicAny(array) +variadicAny([1,2,3]) +variadicArrays(array) +variadicArrays([1,2,3]) +variadicArrays(arrayWithOtherEltType) // expected-error {{cannot convert value of type '[String]' to expected argument type '[Int]'}} +variadicArrays(1,2,3) // expected-error {{cannot convert value of type 'Int' to expected argument type '[Int]'}} + // BOGUS: unexpected trailing closure func expect(_: T) -> (U.Type) -> Int { return { a in 0 } } func expect(_: T, _: Int = 1) -> (U.Type) -> String { return { a in "String" } } diff --git a/test/Interpreter/varargs.swift b/test/Interpreter/varargs.swift index 1fe36891de6a0..df9569fcf550a 100644 --- a/test/Interpreter/varargs.swift +++ b/test/Interpreter/varargs.swift @@ -16,3 +16,54 @@ vf(a as NSArray) // CHECK: OK vf(s as NSString) // CHECK: OK + +func f(_ x: Int...) { + print(x as Any..., separator: ",") +} + +func f(_ x: [Int]...) { + print(x as Any..., separator: ",") +} + +let closure: ((Int...) -> Void) = { + (args: Int...) in + print(args as Any..., separator: ",") +} + +f(1,2,3) // CHECK: 1,2,3 +f([1,2,3] as Int...) // CHECK: 1,2,3 +closure([1,2,3] as Int...) // CHECK: 1,2,3 +let x = [1,2,3].map { $0 + 1 } +f(x as Int...) // CHECK: 2,3,4 +f([x, x] as [Int]...) // CHECK: [2, 3, 4],[2, 3, 4] + +func overloaded(x: [Int]) { print("array") } +func overloaded(x: Int...) { print("varargs") } +overloaded(x: x as Int...) // CHECK: varargs +overloaded(x: x) // CHECK: array + +struct HasVariadicSubscript { + subscript(bar: Int...) -> Int { + get { + print(bar as Any..., separator: ",") + return 42 + } + } +} +_ = HasVariadicSubscript()[[9,8,7] as Int...] // CHECK: 9,8,7 + +func triangle(_ x: Int...) { + if x.isEmpty { + return + } + print(x as Any..., separator: ",") + triangle(x.dropLast() as Int...) +} + +triangle(1,2,3,4,5) +// CHECK: 1,2,3,4,5 +// CHECK: 1,2,3,4 +// CHECK: 1,2,3 +// CHECK: 1,2 +// CHECK: 1 + diff --git a/test/expr/cast/array_coerce_to_varargs.swift b/test/expr/cast/array_coerce_to_varargs.swift new file mode 100644 index 0000000000000..e0b1e5e2908fa --- /dev/null +++ b/test/expr/cast/array_coerce_to_varargs.swift @@ -0,0 +1,74 @@ +// RUN: %target-typecheck-verify-swift + +func f(_ x: Int...) {} +func f2(x: String, y: Int..., z: String) {} +func f3(_ x: String...) {} +struct HasVariadicSubscript { + subscript(bar: Int...) -> Int { + get { 42 } + } +} +let closure: ((Int...) -> Void) = { + (args: Int...) in +} +let x: [Int] = [1,2,3] + [4] +let y: [Int]! = [1,2,3] + +f([1,2,3] as Int ...) // expected-error {{expected expression after operator}} +f(x as! Int...) // expected-error {{coercion to variadic arguments using 'as!' is not allowed; did you mean to use 'as'?}} +f(x as? Int...) // expected-error {{coercion to variadic arguments using 'as?' is not allowed; did you mean to use 'as'?}} + +_ = 0 as Int...5 +_ = 0 as Int ... 5 + +f(1,2,3) +f([1,2,3] as Int...) +closure([1,2,3] as Int...) +f(x as Int...) +f((x.map { $0 + 1 }) as Int...) +f([] as Int...) +f(y as Int...) + +f2(x: "Hello,", y: 1,2,3, z: "world!") +f2(x: "Hello again,", y: [1,2,3] as Int..., z: "world!") +f2(x: "Hello yet again,", y: x as Int..., z: "world!") + +f3(["\(1)", "\(2)"] as String...) + +func overloaded(x: [Int]) {} +func overloaded(x: Int...) {} +overloaded(x: x as Int...) +overloaded(x: x) + +f([1,2,3] as Int..., 3, 4) // expected-error {{array elements coerced to variadic arguments cannot be used alongside additional variadic arguments}} +f(1, 2, [1,2,3] as Int...) // expected-error {{array elements coerced to variadic arguments cannot be used alongside additional variadic arguments}} +f(1, 2, [1,2,3] as Int..., 3, 4) // expected-error {{array elements coerced to variadic arguments cannot be used alongside additional variadic arguments}} +f([1,2,3] as Int..., 3, 4, [1,2,3] as Int...) // expected-error {{array elements coerced to variadic arguments cannot be used alongside additional variadic arguments}} +f([1,2,3] as Int..., [1,2,3] as Int...) // expected-error {{array elements coerced to variadic arguments cannot be used alongside additional variadic arguments}} + +f(1 as Int...) // expected-error {{cannot convert value of type 'Int' to type '[Int]' in coercion}} + +class A {} +class B: A {} +protocol P {} +struct S: P {} +struct S2 {} +func takesA(_ x: A...) {} +func takesP(_ x: P...) {} +takesA([A(), A()] as A...) +takesA([B(), B()] as A...) +takesA([B(), B()] as B...) +takesA([S2()] as A...) // expected-error {{cannot convert value of type 'S2' to expected element type 'A'}} +takesP([S(), S(), S()] as P...) +takesP([S2()] as P...) // expected-error {{'[S2]' is not convertible to '[P]'; did you mean to use 'as!' to force downcast?}} {{8-8=(}} {{14-14= as! [P])}} + +f(([1,2,3] as Int...) as Int...) // expected-error {{coercion to variadic arguments is only allowed in an argument position}} +let y = [1,2,3] as Int... // expected-error {{coercion to variadic arguments is only allowed in an argument position}} +x as Int... // expected-error {{coercion to variadic arguments is only allowed in an argument position}} +(x as Int...) + x // expected-error {{coercion to variadic arguments is only allowed in an argument position}} + +func takesArray(_ x: [Int]) {} +takesArray([1,2,3] as Int...) // expected-error {{cannot invoke 'takesArray' with an argument list of type '(Int...)'}} +// expected-note@-1 {{expected an argument list of type '([Int])'}} +takesArray(x as Int...) // expected-error {{cannot invoke 'takesArray' with an argument list of type '(Int...)'}} +// expected-note@-1 {{expected an argument list of type '([Int])'}}