diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 74ec049c0320d..722b4979df801 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -6016,6 +6016,7 @@ class FuncDecl : public AbstractFunctionDecl { bool isConsuming() const { return getSelfAccessKind() == SelfAccessKind::__Consuming; } + bool isCallAsFunctionMethod() const; SelfAccessKind getSelfAccessKind() const; diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 29526cc1032fb..60d0eec59de44 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -35,6 +35,7 @@ IDENTIFIER(buildBlock) IDENTIFIER(buildDo) IDENTIFIER(buildEither) IDENTIFIER(buildIf) +IDENTIFIER(callAsFunction) IDENTIFIER(Change) IDENTIFIER_WITH_NAME(code_, "_code") IDENTIFIER(CodingKeys) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 43d699db2e6b1..95564d1741617 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6929,6 +6929,10 @@ SelfAccessKind FuncDecl::getSelfAccessKind() const { SelfAccessKind::NonMutating); } +bool FuncDecl::isCallAsFunctionMethod() const { + return getName() == getASTContext().Id_callAsFunction && isInstanceMember(); +} + ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc, OptionalTypeKind Failability, SourceLoc FailabilityLoc, diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index a16ccdf9d861e..d333de41a41d0 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1069,9 +1069,12 @@ namespace { Expr *finishApply(ApplyExpr *apply, Type openedType, ConstraintLocatorBuilder locator); - // Resolve @dynamicCallable applications. - Expr *finishApplyDynamicCallable(const Solution &solution, ApplyExpr *apply, - ConstraintLocatorBuilder locator); + // Resolve `@dynamicCallable` applications. + Expr *finishApplyDynamicCallable(ApplyExpr *apply, + SelectedOverload selected, + FuncDecl *method, + AnyFunctionType *methodType, + ConstraintLocatorBuilder applyFunctionLoc); private: /// Simplify the given type by substituting all occurrences of @@ -6748,11 +6751,62 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, return literal; } -// Resolve @dynamicCallable applications. +// Returns true if the given method and method type are a valid +// `@dynamicCallable` required `func dynamicallyCall` method. +static bool isValidDynamicCallableMethod(FuncDecl *method, + AnyFunctionType *methodType) { + auto &ctx = method->getASTContext(); + if (method->getName() != ctx.Id_dynamicallyCall) + return false; + if (methodType->getParams().size() != 1) + return false; + auto argumentLabel = methodType->getParams()[0].getLabel(); + if (argumentLabel != ctx.Id_withArguments && + argumentLabel != ctx.Id_withKeywordArguments) + return false; + return true; +} + +// Resolve `callAsFunction` method applications. +static Expr *finishApplyCallAsFunctionMethod( + ExprRewriter &rewriter, ApplyExpr *apply, SelectedOverload selected, + AnyFunctionType *openedMethodType, + ConstraintLocatorBuilder applyFunctionLoc) { + auto &cs = rewriter.cs; + auto *fn = apply->getFn(); + auto choice = selected.choice; + // Create direct reference to `callAsFunction` method. + bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; + auto *declRef = rewriter.buildMemberRef( + fn, selected.openedFullType, /*dotLoc*/ SourceLoc(), choice, + DeclNameLoc(fn->getEndLoc()), selected.openedType, applyFunctionLoc, + applyFunctionLoc, /*implicit*/ true, choice.getFunctionRefKind(), + AccessSemantics::Ordinary, isDynamic); + if (!declRef) + return nullptr; + declRef->setImplicit(apply->isImplicit()); + apply->setFn(declRef); + // Coerce argument to input type of the `callAsFunction` method. + SmallVector argLabelsScratch; + auto *arg = rewriter.coerceCallArguments( + apply->getArg(), openedMethodType, apply, + apply->getArgumentLabels(argLabelsScratch), apply->hasTrailingClosure(), + applyFunctionLoc); + if (!arg) + return nullptr; + apply->setArg(arg); + cs.setType(apply, openedMethodType->getResult()); + cs.cacheExprTypes(apply); + return apply; +} + +// Resolve `@dynamicCallable` applications. Expr * -ExprRewriter::finishApplyDynamicCallable(const Solution &solution, - ApplyExpr *apply, - ConstraintLocatorBuilder locator) { +ExprRewriter::finishApplyDynamicCallable(ApplyExpr *apply, + SelectedOverload selected, + FuncDecl *method, + AnyFunctionType *methodType, + ConstraintLocatorBuilder loc) { auto &ctx = cs.getASTContext(); auto *fn = apply->getFn(); @@ -6761,27 +6815,16 @@ ExprRewriter::finishApplyDynamicCallable(const Solution &solution, arg = TupleExpr::createImplicit(ctx, parenExpr->getSubExpr(), {}); // Get resolved `dynamicallyCall` method and verify it. - auto loc = locator.withPathElement(ConstraintLocator::ApplyFunction); - auto selected = solution.getOverloadChoice(cs.getConstraintLocator(loc)); - auto *method = dyn_cast(selected.choice.getDecl()); - auto methodType = simplifyType(selected.openedType)->castTo(); - assert(method->getName() == ctx.Id_dynamicallyCall && - "Expected 'dynamicallyCall' method"); + assert(isValidDynamicCallableMethod(method, methodType)); auto params = methodType->getParams(); - assert(params.size() == 1 && - "Expected 'dynamicallyCall' method with one parameter"); auto argumentType = params[0].getParameterType(); - auto argumentLabel = params[0].getLabel(); - assert((argumentLabel == ctx.Id_withArguments || - argumentLabel == ctx.Id_withKeywordArguments) && - "Expected 'dynamicallyCall' method argument label 'withArguments' or " - "'withKeywordArguments'"); // Determine which method was resolved: a `withArguments` method or a // `withKeywordArguments` method. + auto argumentLabel = methodType->getParams()[0].getLabel(); bool useKwargsMethod = argumentLabel == ctx.Id_withKeywordArguments; - // Construct expression referencing the `dynamicallyCall` method. + // Construct expression referencing the `dynamicallyCall` method. bool isDynamic = selected.choice.getKind() == OverloadChoiceKind::DeclViaDynamic; auto member = buildMemberRef(fn, selected.openedFullType, @@ -6988,6 +7031,24 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, llvm_unreachable("Unhandled DeclTypeCheckingSemantics in switch."); }; + // Resolve `callAsFunction` and `@dynamicCallable` applications. + auto applyFunctionLoc = + locator.withPathElement(ConstraintLocator::ApplyFunction); + if (auto selected = solution.getOverloadChoiceIfAvailable( + cs.getConstraintLocator(applyFunctionLoc))) { + auto *method = dyn_cast(selected->choice.getDecl()); + auto methodType = + simplifyType(selected->openedType)->getAs(); + if (method && methodType) { + if (method->isCallAsFunctionMethod()) + return finishApplyCallAsFunctionMethod( + *this, apply, *selected, methodType, applyFunctionLoc); + if (methodType && isValidDynamicCallableMethod(method, methodType)) + return finishApplyDynamicCallable( + apply, *selected, method, methodType, applyFunctionLoc); + } + } + // The function is always an rvalue. fn = cs.coerceToRValue(fn); @@ -7088,57 +7149,51 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, } // We have a type constructor. - if (auto metaTy = cs.getType(fn)->getAs()) { - auto ty = metaTy->getInstanceType(); - - // If we're "constructing" a tuple type, it's simply a conversion. - if (auto tupleTy = ty->getAs()) { - // FIXME: Need an AST to represent this properly. - return coerceToType(apply->getArg(), tupleTy, locator); - } - - // We're constructing a value of nominal type. Look for the constructor or - // enum element to use. - auto ctorLocator = cs.getConstraintLocator( - locator.withPathElement(ConstraintLocator::ApplyFunction) - .withPathElement(ConstraintLocator::ConstructorMember)); - auto selected = solution.getOverloadChoiceIfAvailable(ctorLocator); - if (!selected) { - assert(ty->hasError() || ty->hasUnresolvedType()); - cs.setType(apply, ty); - return apply; - } - - assert(ty->getNominalOrBoundGenericNominal() || ty->is() || - ty->isExistentialType() || ty->is()); - - // We have the constructor. - auto choice = selected->choice; - - // Consider the constructor decl reference expr 'implicit', but the - // constructor call expr itself has the apply's 'implicitness'. - bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; - Expr *declRef = buildMemberRef(fn, selected->openedFullType, - /*dotLoc=*/SourceLoc(), choice, - DeclNameLoc(fn->getEndLoc()), - selected->openedType, locator, ctorLocator, - /*Implicit=*/true, - choice.getFunctionRefKind(), - AccessSemantics::Ordinary, isDynamic); - if (!declRef) - return nullptr; - declRef->setImplicit(apply->isImplicit()); - apply->setFn(declRef); + auto metaTy = cs.getType(fn)->castTo(); + auto ty = metaTy->getInstanceType(); - // Tail-recur to actually call the constructor. - return finishApply(apply, openedType, locator); + // If we're "constructing" a tuple type, it's simply a conversion. + if (auto tupleTy = ty->getAs()) { + // FIXME: Need an AST to represent this properly. + return coerceToType(apply->getArg(), tupleTy, locator); } - // Handle @dynamicCallable applications. - // At this point, all other ApplyExpr cases have been handled. - return finishApplyDynamicCallable(solution, apply, locator); -} + // We're constructing a value of nominal type. Look for the constructor or + // enum element to use. + auto ctorLocator = cs.getConstraintLocator( + locator.withPathElement(ConstraintLocator::ApplyFunction) + .withPathElement(ConstraintLocator::ConstructorMember)); + auto selected = solution.getOverloadChoiceIfAvailable(ctorLocator); + if (!selected) { + assert(ty->hasError() || ty->hasUnresolvedType()); + cs.setType(apply, ty); + return apply; + } + assert(ty->getNominalOrBoundGenericNominal() || ty->is() || + ty->isExistentialType() || ty->is()); + + // We have the constructor. + auto choice = selected->choice; + + // Consider the constructor decl reference expr 'implicit', but the + // constructor call expr itself has the apply's 'implicitness'. + bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; + Expr *declRef = buildMemberRef(fn, selected->openedFullType, + /*dotLoc=*/SourceLoc(), choice, + DeclNameLoc(fn->getEndLoc()), + selected->openedType, locator, ctorLocator, + /*Implicit=*/true, + choice.getFunctionRefKind(), + AccessSemantics::Ordinary, isDynamic); + if (!declRef) + return nullptr; + declRef->setImplicit(apply->isImplicit()); + apply->setFn(declRef); + + // Tail-recur to actually call the constructor. + return finishApply(apply, openedType, locator); +} // Return the precedence-yielding parent of 'expr', along with the index of // 'expr' as the child of that parent. The precedence-yielding parent is the diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 0aeaeff17eb35..88908bc92a9e2 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -4462,9 +4462,19 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { !fnType->is() && !fnType->is()) { auto arg = callExpr->getArg(); + auto isDynamicCallable = + CS.DynamicCallableCache[fnType->getCanonicalType()].isValid(); + + // Note: Consider caching `hasCallAsFunctionMethods` in `NominalTypeDecl`. + auto *nominal = fnType->getAnyNominal(); + auto hasCallAsFunctionMethods = nominal && + llvm::any_of(nominal->getMembers(), [](Decl *member) { + auto funcDecl = dyn_cast(member); + return funcDecl && funcDecl->isCallAsFunctionMethod(); + }); // Diagnose @dynamicCallable errors. - if (CS.DynamicCallableCache[fnType->getCanonicalType()].isValid()) { + if (isDynamicCallable) { auto dynamicCallableMethods = CS.DynamicCallableCache[fnType->getCanonicalType()]; @@ -4520,7 +4530,8 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { } } - return true; + if (!isDynamicCallable && !hasCallAsFunctionMethods) + return true; } bool hasTrailingClosure = callArgHasTrailingClosure(callExpr->getArg()); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 5d3d171e06bd7..edb77a93e79ef 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5987,6 +5987,7 @@ ConstraintSystem::simplifyApplicableFnConstraint( Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { + auto &ctx = getASTContext(); // By construction, the left hand side is a type that looks like the // following: $T1 -> $T2. @@ -6011,6 +6012,16 @@ ConstraintSystem::simplifyApplicableFnConstraint( } } + // Before stripping lvalue-ness and optional types, save the original second + // type for handling `func callAsFunction` and `@dynamicCallable` + // applications. This supports the following cases: + // - Generating constraints for `mutating func callAsFunction`. The nominal + // type (`type2`) should be an lvalue type. + // - Extending `Optional` itself with `func callAsFunction` or + // `@dynamicCallable` functionality. Optional types are stripped below if + // `shouldAttemptFixes()` is true. + auto origLValueType2 = + getFixedTypeRecursive(type2, flags, /*wantRValue=*/false); // Drill down to the concrete type on the right hand side. type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true); auto desugar2 = type2->getDesugaredType(); @@ -6080,9 +6091,34 @@ ConstraintSystem::simplifyApplicableFnConstraint( ConstraintLocatorBuilder outerLocator = getConstraintLocator(anchor, parts, locator.getSummaryFlags()); - // Before stripping optional types, save original type for handling - // @dynamicCallable applications. This supports the fringe case where - // `Optional` itself is extended with @dynamicCallable functionality. + // Handle applications of types with `callAsFunction` methods. + // Do this before stripping optional types below, when `shouldAttemptFixes()` + // is true. + auto hasCallAsFunctionMethods = + desugar2->mayHaveMembers() && + llvm::any_of(lookupMember(desugar2, DeclName(ctx.Id_callAsFunction)), + [](LookupResultEntry entry) { + return isa(entry.getValueDecl()); + }); + if (hasCallAsFunctionMethods) { + auto memberLoc = getConstraintLocator( + outerLocator.withPathElement(ConstraintLocator::Member)); + // Add a `callAsFunction` member constraint, binding the member type to a + // type variable. + auto memberTy = createTypeVariable(memberLoc, /*options=*/0); + // TODO: Revisit this if `static func callAsFunction` is to be supported. + // Static member constraint requires `FunctionRefKind::DoubleApply`. + addValueMemberConstraint(origLValueType2, DeclName(ctx.Id_callAsFunction), + memberTy, DC, FunctionRefKind::SingleApply, + /*outerAlternatives*/ {}, locator); + // Add new applicable function constraint based on the member type + // variable. + addConstraint(ConstraintKind::ApplicableFunction, func1, memberTy, + locator); + return SolutionKind::Solved; + } + + // Record the second type before unwrapping optionals. auto origType2 = desugar2; unsigned unwrapCount = 0; if (shouldAttemptFixes()) { @@ -6305,6 +6341,13 @@ getDynamicCallableMethods(Type type, ConstraintSystem &CS, return result; } +// TODO: Refactor/simplify this function. +// - It should perform less duplicate work with its caller +// `ConstraintSystem::simplifyApplicableFnConstraint`. +// - It should generate a member constraint instead of manually forming an +// overload set for `func dynamicallyCall` candidates. +// - It should support `mutating func dynamicallyCall`. This should fall out of +// using member constraints with an lvalue base type. ConstraintSystem::SolutionKind ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint( Type type1, diff --git a/test/Sema/Inputs/call_as_function_other_module.swift b/test/Sema/Inputs/call_as_function_other_module.swift new file mode 100644 index 0000000000000..740b85618fe55 --- /dev/null +++ b/test/Sema/Inputs/call_as_function_other_module.swift @@ -0,0 +1,11 @@ +public protocol Layer { + func callAsFunction(_ input: Float) -> Float +} + +public struct Dense { + public init() {} + + public func callAsFunction(_ input: Float) -> Float { + return input * 2 + } +} diff --git a/test/Sema/call_as_function_cross_module.swift b/test/Sema/call_as_function_cross_module.swift new file mode 100644 index 0000000000000..e88ec62e5d2fd --- /dev/null +++ b/test/Sema/call_as_function_cross_module.swift @@ -0,0 +1,14 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -primary-file %S/Inputs/call_as_function_other_module.swift -emit-module-path %t/call_as_function_other_module.swiftmodule +// RUN: %target-swift-frontend -typecheck -I %t -primary-file %s -verify + +import call_as_function_other_module + +func testLayer(_ layer: L) -> Float { + return layer(1) +} + +func testDense() -> Float { + let dense = Dense() + return dense(1) +} diff --git a/test/Sema/call_as_function_generic.swift b/test/Sema/call_as_function_generic.swift new file mode 100644 index 0000000000000..e86406180d86d --- /dev/null +++ b/test/Sema/call_as_function_generic.swift @@ -0,0 +1,43 @@ +// RUN: %target-typecheck-verify-swift + +protocol P0 { + func callAsFunction(x: Self) +} + +struct ConcreteType { + func callAsFunction(_ x: T, _ y: U) -> (T, U) { + return (x, y) + } + + func callAsFunction(_ fn: @escaping (T) -> U) -> (T) -> U { + return fn + } +} + +let concrete = ConcreteType() +_ = concrete(1, 3.0) +_ = concrete(concrete, concrete.callAsFunction as ([Int], Float) -> ([Int], Float)) + +func generic(_ x: T, _ y: U) { + _ = concrete(x, x) + _ = concrete(x, y) +} + +struct GenericType { + let collection: T + func callAsFunction(_ x: U) -> Bool where U == T.Element, U : Equatable { + return collection.contains(x) + } +} + +// Test conditional conformance. +extension GenericType where T.Element : Numeric { + func callAsFunction(initialValue: T.Element) -> T.Element { + return collection.reduce(initialValue, +) + } +} + +let genericString = GenericType<[String]>(collection: ["Hello", "world", "!"]) +_ = genericString("Hello") +let genericInt = GenericType>(collection: [1, 2, 3]) +_ = genericInt(initialValue: 1) diff --git a/test/Sema/call_as_function_optional.swift b/test/Sema/call_as_function_optional.swift new file mode 100644 index 0000000000000..f8c342e1c5850 --- /dev/null +++ b/test/Sema/call_as_function_optional.swift @@ -0,0 +1,19 @@ +// RUN: %target-typecheck-verify-swift + +// Test extending `Optional` with `func callAsFunction`. +// `Optional` is an edge case since constraint simplification has special +// `Optional` stripping logic. + +extension Optional { + func callAsFunction() -> Optional { + return self + } + func callAsFunction(_ fn: (Int) -> Void) -> Optional { + return self + } +} +func testOptional(_ x: T?) { + _ = x()()() + // Test trailing closure syntax. + _ = x { _ in } ({ _ in }) +} diff --git a/test/Sema/call_as_function_protocol.swift b/test/Sema/call_as_function_protocol.swift new file mode 100644 index 0000000000000..1497c615451bb --- /dev/null +++ b/test/Sema/call_as_function_protocol.swift @@ -0,0 +1,61 @@ +// RUN: %target-typecheck-verify-swift + +protocol P0 { + // expected-note @+1 {{protocol requires function 'callAsFunction()' with type '() -> Missing'; do you want to add a stub?}} + func callAsFunction() -> Self +} +func testProtocol(_ x: P0) { + _ = x() +} +func testGeneric(_ x: T) { + _ = x() +} + +protocol P1 { + func callAsFunction() -> Self +} +extension P1 { + // expected-note @+1 {{found this candidate}} + func callAsFunction() -> Self { + return self + } +} +protocol P2 {} +extension P2 { + // expected-note @+1 {{found this candidate}} + func callAsFunction(x: Int, y: Int) -> Int { + return x + y + } +} + +// expected-error @+1 {{type 'Missing' does not conform to protocol 'P0'}} +struct Missing : P0 {} +struct S0 : P0 { + @discardableResult + func callAsFunction() -> S0 { return self } +} +let s0 = S0() +s0() + +struct S1 : P1 { + func callAsFunction() -> S1 { return self } +} + +let s1 = S1() +_ = s1()() + +struct Conforming : P0 & P1 & P2 {} +let conforming = Conforming() +_ = conforming(x: 1, y: 2) +_ = conforming().callAsFunction(x:y:)(1, 2) +_ = conforming.callAsFunction(x:y:) +_ = conforming.callAsFunction // expected-error {{ambiguous use of 'callAsFunction'}} + +protocol P3 {} +extension P3 { + func callAsFunction() -> Self { return self } +} +struct S3 : P3 {} + +let s3 = S3() +_ = s3()() diff --git a/test/Sema/call_as_function_simple.swift b/test/Sema/call_as_function_simple.swift new file mode 100644 index 0000000000000..0c1253dd2ac17 --- /dev/null +++ b/test/Sema/call_as_function_simple.swift @@ -0,0 +1,198 @@ +// RUN: %target-typecheck-verify-swift + +struct SimpleCallable { + func callAsFunction(_ x: Float) -> Float { + return x + } +} + +// Simple tests. + +let foo = SimpleCallable() +_ = foo(1) +_ = foo(foo(1)) + +// TODO(SR-11378): Improve this error to match the error using a direct `callAsFunction` member reference. +// expected-error @+2 {{cannot call value of non-function type 'SimpleCallable'}} +// expected-error @+1 {{cannot invoke 'foo' with an argument list of type '(Int, Int)'}} +_ = foo(1, 1) +// expected-error @+1 {{cannot convert value of type 'SimpleCallable' to specified type '(Float) -> Float'}} +let _: (Float) -> Float = foo + +// Test direct `callAsFunction` member references. + +_ = foo.callAsFunction(1) +_ = [1, 2, 3].map(foo.callAsFunction) +_ = foo.callAsFunction(foo(1)) +_ = foo(foo.callAsFunction(1)) +let _: (Float) -> Float = foo.callAsFunction + +func callable() -> SimpleCallable { + return SimpleCallable() +} +extension SimpleCallable { + var foo: SimpleCallable { + return self + } + func bar() -> SimpleCallable { + return self + } +} + +_ = foo.foo(1) +_ = foo.bar()(1) +_ = callable()(1) +_ = [1, 2, 3].map(foo.foo.callAsFunction) +_ = [1, 2, 3].map(foo.bar().callAsFunction) +_ = [1, 2, 3].map(callable().callAsFunction) + +struct MultipleArgsCallable { + func callAsFunction(x: Int, y: Float) -> [Int] { + return [x] + } +} + +let bar = MultipleArgsCallable() +_ = bar(x: 1, y: 1) +_ = bar.callAsFunction(x: 1, y: 1) +_ = bar(x: bar.callAsFunction(x: 1, y: 1)[0], y: 1) +_ = bar.callAsFunction(x: bar(x: 1, y: 1)[0], y: 1) +_ = bar(1, 1) // expected-error {{missing argument labels 'x:y:' in call}} + +struct Extended {} +extension Extended { + @discardableResult + func callAsFunction() -> Extended { + return self + } +} +var extended = Extended() +extended()().callAsFunction()() + +struct TakesTrailingClosure { + func callAsFunction(_ fn: () -> Void) { + fn() + } + func callAsFunction(_ x: Int, label y: Float, _ fn: (Int, Float) -> Void) { + fn(x, y) + } +} +var takesTrailingClosure = TakesTrailingClosure() +takesTrailingClosure { print("Hi") } +takesTrailingClosure() { print("Hi") } +takesTrailingClosure(1, label: 2) { _ = Float($0) + $1 } + +struct OptionalCallable { + func callAsFunction() -> OptionalCallable? { + return self + } +} +var optional = OptionalCallable() +_ = optional()?.callAsFunction()?() + +struct Variadic { + func callAsFunction(_ args: Int...) -> [Int] { + return args + } +} +var variadic = Variadic() +_ = variadic() +_ = variadic(1, 2, 3) + +struct Mutating { + var x: Int + mutating func callAsFunction() { + x += 1 + } +} +func testMutating(_ x: Mutating, _ y: inout Mutating) { + // TODO(SR-11378): Improve this error to match the error using a direct `callAsFunction` member reference. + // expected-error @+2 {{cannot call value of non-function type 'Mutating'}} + // expected-error @+1 {{cannot invoke 'x' with no arguments}} + _ = x() + // expected-error @+1 {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} + _ = x.callAsFunction() + _ = y() + _ = y.callAsFunction() +} + +struct Inout { + func callAsFunction(_ x: inout Int) { + x += 5 + } +} +func testInout(_ x: Inout, _ arg: inout Int) { + x(&arg) + x.callAsFunction(&arg) + // expected-error @+1 {{passing value of type 'Int' to an inout parameter requires explicit '&'}} + x(arg) + // expected-error @+1 {{passing value of type 'Int' to an inout parameter requires explicit '&'}} + x.callAsFunction(arg) +} + +struct Autoclosure { + func callAsFunction(_ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String) { + if condition() { + print(message()) + } + } +} +func testAutoclosure(_ x: Autoclosure) { + x(true, "Failure") + x({ true }(), { "Failure" }()) +} + +struct Throwing { + func callAsFunction() throws -> Throwing { + return self + } + func callAsFunction(_ f: () throws -> ()) rethrows { + try f() + } +} +struct DummyError : Error {} +var throwing = Throwing() +_ = try throwing() +_ = try throwing { throw DummyError() } + +enum BinaryOperation { + case add, subtract, multiply, divide +} +extension BinaryOperation { + func callAsFunction(_ lhs: Float, _ rhs: Float) -> Float { + switch self { + case .add: return lhs + rhs + case .subtract: return lhs - rhs + case .multiply: return lhs * rhs + case .divide: return lhs / rhs + } + } +} +_ = BinaryOperation.add(1, 2) + +class BaseClass { + func callAsFunction() -> Self { + return self + } +} +class SubClass : BaseClass { + override func callAsFunction() -> Self { + return self + } +} + +func testIUO(a: SimpleCallable!, b: MultipleArgsCallable!, c: Extended!, + d: OptionalCallable!, e: Variadic!, f: inout Mutating!, + g: Inout!, inoutInt: inout Int, h: Throwing!) { + _ = a(1) + _ = b(x: 1, y: 1) + _ = c() + _ = d()?.callAsFunction()?() + _ = e() + _ = e(1, 2, 3) + _ = f() + _ = g(&inoutInt) + _ = try? h() + _ = try? h { throw DummyError() } +}