From 932f10853a189bd3dccedff773e88b9560faad6f Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Wed, 24 Apr 2019 00:54:53 -0700 Subject: [PATCH 1/9] Introduce callables. Introduce callables: values that can be called like functions. Methods named `func callFunction` act as call-syntax delegate methods. ``` struct Adder { var base: Int func callFunction(_ x: Int) -> Int { return x + base } } var adder = Adder(base: 3) adder(10) // desugars to `adder.callFunction(10)` ``` --- include/swift/AST/Decl.h | 3 + include/swift/AST/KnownIdentifiers.def | 1 + lib/Sema/CSApply.cpp | 56 ++++++ lib/Sema/CSDiag.cpp | 15 +- lib/Sema/CSSimplify.cpp | 54 +++++ .../Inputs/call_method_other_module.swift | 11 ++ test/Sema/call_method_cross_module.swift | 14 ++ test/Sema/call_method_generic.swift | 43 ++++ test/Sema/call_method_protocol.swift | 61 ++++++ test/Sema/call_method_simple.swift | 184 ++++++++++++++++++ 10 files changed, 440 insertions(+), 2 deletions(-) create mode 100644 test/Sema/Inputs/call_method_other_module.swift create mode 100644 test/Sema/call_method_cross_module.swift create mode 100644 test/Sema/call_method_generic.swift create mode 100644 test/Sema/call_method_protocol.swift create mode 100644 test/Sema/call_method_simple.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 4f6d153e293f3..3b353c4696955 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5958,6 +5958,9 @@ class FuncDecl : public AbstractFunctionDecl { bool isConsuming() const { return getSelfAccessKind() == SelfAccessKind::__Consuming; } + bool isCallable() const { + return getName().str() == "call" && isInstanceMember(); + } SelfAccessKind getSelfAccessKind() const { return static_cast(Bits.FuncDecl.SelfAccess); diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index f482b21be7b8f..7b7ec715a6027 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -31,6 +31,7 @@ IDENTIFIER(Any) IDENTIFIER(ArrayLiteralElement) IDENTIFIER(atIndexedSubscript) IDENTIFIER_(bridgeToObjectiveC) +IDENTIFIER(call) IDENTIFIER_WITH_NAME(code_, "_code") IDENTIFIER(CodingKeys) IDENTIFIER(combine) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 5775c6350d431..6b741e5521689 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7067,6 +7067,9 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, llvm_unreachable("Unhandled DeclTypeCheckingSemantics in switch."); }; + // Save the original potentially lvalue function for rewriting call method + // applications. + auto *originalFn = fn; // The function is always an rvalue. fn = cs.coerceToRValue(fn); @@ -7213,6 +7216,59 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, return finishApply(apply, openedType, locator); } + // Handle call method applications. + auto &ctx = cs.getASTContext(); + + TupleExpr *arg = dyn_cast(apply->getArg()); + if (auto parenExpr = dyn_cast(apply->getArg())) + arg = TupleExpr::createImplicit(ctx, parenExpr->getSubExpr(), {}); + + // Get resolved call method and verify it. + auto loc = locator.withPathElement(ConstraintLocator::ApplyFunction); + auto selected = solution.getOverloadChoice(cs.getConstraintLocator(loc)); + auto choice = selected.choice; + auto *callMethod = dyn_cast(selected.choice.getDecl()); + if (callMethod && callMethod->isCallable()) { + auto methodType = + simplifyType(selected.openedType)->castTo(); + auto selfParam = callMethod->getImplicitSelfDecl(); + // Diagnose `mutating` method call on immutable value. + // FIXME(TF-444): This logic for `mutating` method calls incorrectly rejects + // IUOs. Performing this ad-hoc check using `originalFn` feels hacky; + // rewrite if possible. + if (!cs.getType(originalFn)->hasLValueType() && selfParam->isInOut()) { + AssignmentFailure failure(originalFn, cs, originalFn->getLoc(), + diag::cannot_pass_rvalue_mutating_subelement, + diag::cannot_pass_rvalue_mutating); + failure.diagnose(); + return nullptr; + } + // Create direct reference to call method. + bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; + Expr *declRef = buildMemberRef(originalFn, selected.openedFullType, + /*dotLoc=*/SourceLoc(), choice, + DeclNameLoc(fn->getEndLoc()), + selected.openedType, loc, loc, + /*Implicit=*/true, + choice.getFunctionRefKind(), + AccessSemantics::Ordinary, isDynamic); + if (!declRef) + return nullptr; + declRef->setImplicit(apply->isImplicit()); + apply->setFn(declRef); + // Coerce argument to input type of call method. + SmallVector argLabelsScratch; + auto *arg = coerceCallArguments(apply->getArg(), methodType, apply, + apply->getArgumentLabels(argLabelsScratch), + apply->hasTrailingClosure(), loc); + if (!arg) + return nullptr; + apply->setArg(arg); + cs.setType(apply, methodType->getResult()); + cs.cacheExprTypes(apply); + return apply; + } + // Handle @dynamicCallable applications. // At this point, all other ApplyExpr cases have been handled. return finishApplyDynamicCallable(cs, solution, apply, locator); diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index e587423975f82..19f43e05dddef 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -4796,9 +4796,19 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { !fnType->is() && !fnType->is()) { auto arg = callExpr->getArg(); + auto isDynamicCallable = + CS.DynamicCallableCache[fnType->getCanonicalType()].isValid(); + + // TODO: Consider caching? + auto *nominal = fnType->getAnyNominal(); + auto hasCallMethods = nominal && + llvm::any_of(nominal->getMembers(), [](Decl *member) { + auto funcDecl = dyn_cast(member); + return funcDecl && funcDecl->isCallable(); + }); // Diagnose @dynamicCallable errors. - if (CS.DynamicCallableCache[fnType->getCanonicalType()].isValid()) { + if (isDynamicCallable) { auto dynamicCallableMethods = CS.DynamicCallableCache[fnType->getCanonicalType()]; @@ -4854,7 +4864,8 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { } } - return true; + if (!isDynamicCallable && !hasCallMethods) + return true; } bool hasTrailingClosure = callArgHasTrailingClosure(callExpr->getArg()); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index b55fc9650efa7..4729561a2b1ed 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5788,6 +5788,60 @@ ConstraintSystem::simplifyApplicableFnConstraint( return simplified; } + // Handle applications of types with call methods. + if (desugar2->mayHaveMembers()) { + auto &ctx = getASTContext(); + // Get all call methods of the nominal type. + // TODO: Consider caching? + SmallVector callMethods; + auto candidates = lookupMember(desugar2, DeclName(ctx.Id_call)); + for (auto entry : candidates) { + auto callMethod = dyn_cast(entry.getValueDecl()); + if (!callMethod) + continue; + callMethods.push_back(callMethod); + } + + // Handle call methods calls. + if (!callMethods.empty()) { + // Create a type variable for the call method. + auto loc = getConstraintLocator(locator); + auto tv = createTypeVariable(loc, TVO_CanBindToLValue); + + // Record the call method overload set. + SmallVector choices; + for (auto candidate : callMethods) { + TC.validateDecl(candidate); + if (candidate->isInvalid()) continue; + choices.push_back( + OverloadChoice(type2, candidate, FunctionRefKind::SingleApply)); + } + if (choices.empty()) return SolutionKind::Error; + addOverloadSet(tv, choices, DC, loc); + + // Create type variables for each parameter type. + SmallVector tvParams; + for (unsigned i : range(func1->getNumParams())) { + auto param = func1->getParams()[i]; + auto paramType = param.getPlainType(); + + auto *tvParam = createTypeVariable(loc, TVO_CanBindToNoEscape); + auto locatorBuilder = + locator.withPathElement(LocatorPathElt::getTupleElement(i)); + addConstraint(ConstraintKind::ArgumentConversion, paramType, + tvParam, locatorBuilder); + tvParams.push_back(AnyFunctionType::Param( + tvParam, Identifier(), param.getParameterFlags())); + } + // Create target function type and an applicable function constraint. + AnyFunctionType *funcType = + FunctionType::get(tvParams, func1->getResult()); + addConstraint(ConstraintKind::ApplicableFunction, funcType, tv, locator); + + return SolutionKind::Solved; + } + } + // Handle applications of @dynamicCallable types. return simplifyDynamicCallableApplicableFnConstraint(type1, origType2, subflags, locator); diff --git a/test/Sema/Inputs/call_method_other_module.swift b/test/Sema/Inputs/call_method_other_module.swift new file mode 100644 index 0000000000000..6109ffc7f972e --- /dev/null +++ b/test/Sema/Inputs/call_method_other_module.swift @@ -0,0 +1,11 @@ +public protocol Layer { + func call(_ input: Float) -> Float +} + +public struct Dense { + public init() {} + + public func call(_ input: Float) -> Float { + return input * 2 + } +} diff --git a/test/Sema/call_method_cross_module.swift b/test/Sema/call_method_cross_module.swift new file mode 100644 index 0000000000000..d97628cf20b88 --- /dev/null +++ b/test/Sema/call_method_cross_module.swift @@ -0,0 +1,14 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -primary-file %S/Inputs/call_method_other_module.swift -emit-module-path %t/call_method_other_module.swiftmodule +// RUN: %target-swift-frontend -typecheck -I %t -primary-file %s -verify + +import call_method_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_method_generic.swift b/test/Sema/call_method_generic.swift new file mode 100644 index 0000000000000..b07e57d2012d8 --- /dev/null +++ b/test/Sema/call_method_generic.swift @@ -0,0 +1,43 @@ +// RUN: %target-typecheck-verify-swift + +protocol P0 { + func call(x: Self) +} + +struct ConcreteType { + func call(_ x: T, _ y: U) -> (T, U) { + return (x, y) + } + + func call(_ fn: @escaping (T) -> U) -> (T) -> U { + return fn + } +} + +let concrete = ConcreteType() +_ = concrete(1, 3.0) +_ = concrete(concrete, concrete.call as ([Int], Float) -> ([Int], Float)) + +func generic(_ x: T, _ y: U) { + _ = concrete(x, x) + _ = concrete(x, y) +} + +struct GenericType { + let collection: T + func call(_ x: U) -> Bool where U == T.Element, U : Equatable { + return collection.contains(x) + } +} + +// Test conditional conformance. +extension GenericType where T.Element : Numeric { + func call(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_method_protocol.swift b/test/Sema/call_method_protocol.swift new file mode 100644 index 0000000000000..da7efbbf53e27 --- /dev/null +++ b/test/Sema/call_method_protocol.swift @@ -0,0 +1,61 @@ +// RUN: %target-typecheck-verify-swift + +protocol P0 { + // expected-note @+1 {{protocol requires function 'call()' with type '() -> Missing'; do you want to add a stub?}} + func call() -> Self +} +func testProtocol(_ x: P0) { + _ = x() +} +func testGeneric(_ x: T) { + _ = x() +} + +protocol P1 { + func call() -> Self +} +extension P1 { + // expected-note @+1 {{found this candidate}} + func call() -> Self { + return self + } +} +protocol P2 {} +extension P2 { + // expected-note @+1 {{found this candidate}} + func call(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 call() -> S0 { return self } +} +let s0 = S0() +s0() + +struct S1 : P1 { + func call() -> S1 { return self } +} + +let s1 = S1() +_ = s1()() + +struct Conforming : P0 & P1 & P2 {} +let conforming = Conforming() +_ = conforming(x: 1, y: 2) +_ = conforming().call(x:y:)(1, 2) +_ = conforming.call(x:y:) +_ = conforming.call // expected-error {{ambiguous use of 'call'}} + +protocol P3 {} +extension P3 { + func call() -> Self { return self } +} +struct S3 : P3 {} + +let s3 = S3() +_ = s3()() diff --git a/test/Sema/call_method_simple.swift b/test/Sema/call_method_simple.swift new file mode 100644 index 0000000000000..dea1f055d996a --- /dev/null +++ b/test/Sema/call_method_simple.swift @@ -0,0 +1,184 @@ +// RUN: %target-typecheck-verify-swift + +struct SimpleCallable { + func call(_ x: Float) -> Float { + return x + } +} + +// Simple tests. + +let foo = SimpleCallable() +_ = foo(1) +_ = foo(foo(1)) + +// TODO: Improve this error to match the error using a direct `call` 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 `call` member references. + +_ = foo.call(1) +_ = [1, 2, 3].map(foo.call) +_ = foo.call(foo(1)) +_ = foo(foo.call(1)) +let _: (Float) -> Float = foo.call + +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.call) +_ = [1, 2, 3].map(foo.bar().call) +_ = [1, 2, 3].map(callable().call) + +struct MultipleArgsCallable { + func call(x: Int, y: Float) -> [Int] { + return [x] + } +} + +let bar = MultipleArgsCallable() +_ = bar(x: 1, y: 1) +_ = bar.call(x: 1, y: 1) +_ = bar(x: bar.call(x: 1, y: 1)[0], y: 1) +_ = bar.call(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 call() -> Extended { + return self + } +} +var extended = Extended() +extended()().call()() + +struct OptionalCallable { + func call() -> OptionalCallable? { + return self + } +} +var optional = OptionalCallable() +_ = optional()?.call()?() + +struct Variadic { + func call(_ args: Int...) -> [Int] { + return args + } +} +var variadic = Variadic() +_ = variadic() +_ = variadic(1, 2, 3) + +struct Mutating { + var x: Int + mutating func call() { + x += 1 + } +} +func testMutating(_ x: Mutating, _ y: inout Mutating) { + _ = x() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} + _ = x.call() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} + _ = y() + _ = y.call() +} + +struct Inout { + func call(_ x: inout Int) { + x += 5 + } +} +func testInout(_ x: Inout, _ arg: inout Int) { + x(&arg) + x.call(&arg) + // TODO: Improve this error to match the error using a direct `call` member reference. + // expected-error @+2 {{cannot invoke 'x' with an argument list of type '(Int)'}} + // expected-error @+1 {{cannot call value of non-function type 'Inout'}} + x(arg) + // expected-error @+1 {{passing value of type 'Int' to an inout parameter requires explicit '&'}} + x.call(arg) +} + +struct Autoclosure { + func call(_ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String) { + if condition() { + print(message()) + } + } +} +func testAutoclosure(_ x: Autoclosure) { + x(true, "Failure") + x({ true }(), { "Failure" }()) +} + +struct Throwing { + func call() throws -> Throwing { + return self + } + func call(_ 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 call(_ 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 call() -> Self { + return self + } +} +class SubClass : BaseClass { + override func call() -> 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()?.call()?() + _ = e() + _ = e(1, 2, 3) + // FIXME(TF-444): `mutating func call` and IUO doesn't work. + // _ = f() + _ = g(&inoutInt) + _ = try? h() + _ = try? h { throw DummyError() } +} From 4cce21fa7fd849577aaf4db02e083025ecaff5bb Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Fri, 17 May 2019 04:20:30 -0700 Subject: [PATCH 2/9] Implement SE-0253 review feedback. Use `callFunction` instead of `call` as the call-syntax delegate method name. Add trailing closure tests. --- include/swift/AST/Decl.h | 4 +- include/swift/AST/KnownIdentifiers.def | 2 +- lib/Sema/CSApply.cpp | 2 +- lib/Sema/CSDiag.cpp | 2 +- lib/Sema/CSSimplify.cpp | 2 +- .../Inputs/call_method_other_module.swift | 4 +- test/Sema/call_method_generic.swift | 12 +-- test/Sema/call_method_protocol.swift | 22 +++--- test/Sema/call_method_simple.swift | 77 +++++++++++-------- 9 files changed, 70 insertions(+), 57 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 3b353c4696955..224577c5a2bd8 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5958,8 +5958,8 @@ class FuncDecl : public AbstractFunctionDecl { bool isConsuming() const { return getSelfAccessKind() == SelfAccessKind::__Consuming; } - bool isCallable() const { - return getName().str() == "call" && isInstanceMember(); + bool isCallFunction() const { + return getName().str() == "callFunction" && isInstanceMember(); } SelfAccessKind getSelfAccessKind() const { diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 7b7ec715a6027..4b4e961e8cd88 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -31,7 +31,7 @@ IDENTIFIER(Any) IDENTIFIER(ArrayLiteralElement) IDENTIFIER(atIndexedSubscript) IDENTIFIER_(bridgeToObjectiveC) -IDENTIFIER(call) +IDENTIFIER(callFunction) IDENTIFIER_WITH_NAME(code_, "_code") IDENTIFIER(CodingKeys) IDENTIFIER(combine) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 6b741e5521689..acd7c6cd8dd9c 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7228,7 +7228,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, auto selected = solution.getOverloadChoice(cs.getConstraintLocator(loc)); auto choice = selected.choice; auto *callMethod = dyn_cast(selected.choice.getDecl()); - if (callMethod && callMethod->isCallable()) { + if (callMethod && callMethod->isCallFunction()) { auto methodType = simplifyType(selected.openedType)->castTo(); auto selfParam = callMethod->getImplicitSelfDecl(); diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 19f43e05dddef..4bb3915d9957d 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -4804,7 +4804,7 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { auto hasCallMethods = nominal && llvm::any_of(nominal->getMembers(), [](Decl *member) { auto funcDecl = dyn_cast(member); - return funcDecl && funcDecl->isCallable(); + return funcDecl && funcDecl->isCallFunction(); }); // Diagnose @dynamicCallable errors. diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 4729561a2b1ed..5b5b68ba32e50 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5794,7 +5794,7 @@ ConstraintSystem::simplifyApplicableFnConstraint( // Get all call methods of the nominal type. // TODO: Consider caching? SmallVector callMethods; - auto candidates = lookupMember(desugar2, DeclName(ctx.Id_call)); + auto candidates = lookupMember(desugar2, DeclName(ctx.Id_callFunction)); for (auto entry : candidates) { auto callMethod = dyn_cast(entry.getValueDecl()); if (!callMethod) diff --git a/test/Sema/Inputs/call_method_other_module.swift b/test/Sema/Inputs/call_method_other_module.swift index 6109ffc7f972e..a17ce53801d17 100644 --- a/test/Sema/Inputs/call_method_other_module.swift +++ b/test/Sema/Inputs/call_method_other_module.swift @@ -1,11 +1,11 @@ public protocol Layer { - func call(_ input: Float) -> Float + func callFunction(_ input: Float) -> Float } public struct Dense { public init() {} - public func call(_ input: Float) -> Float { + public func callFunction(_ input: Float) -> Float { return input * 2 } } diff --git a/test/Sema/call_method_generic.swift b/test/Sema/call_method_generic.swift index b07e57d2012d8..7391222f343c6 100644 --- a/test/Sema/call_method_generic.swift +++ b/test/Sema/call_method_generic.swift @@ -1,22 +1,22 @@ // RUN: %target-typecheck-verify-swift protocol P0 { - func call(x: Self) + func callFunction(x: Self) } struct ConcreteType { - func call(_ x: T, _ y: U) -> (T, U) { + func callFunction(_ x: T, _ y: U) -> (T, U) { return (x, y) } - func call(_ fn: @escaping (T) -> U) -> (T) -> U { + func callFunction(_ fn: @escaping (T) -> U) -> (T) -> U { return fn } } let concrete = ConcreteType() _ = concrete(1, 3.0) -_ = concrete(concrete, concrete.call as ([Int], Float) -> ([Int], Float)) +_ = concrete(concrete, concrete.callFunction as ([Int], Float) -> ([Int], Float)) func generic(_ x: T, _ y: U) { _ = concrete(x, x) @@ -25,14 +25,14 @@ func generic(_ x: T, _ y: U) { struct GenericType { let collection: T - func call(_ x: U) -> Bool where U == T.Element, U : Equatable { + func callFunction(_ x: U) -> Bool where U == T.Element, U : Equatable { return collection.contains(x) } } // Test conditional conformance. extension GenericType where T.Element : Numeric { - func call(initialValue: T.Element) -> T.Element { + func callFunction(initialValue: T.Element) -> T.Element { return collection.reduce(initialValue, +) } } diff --git a/test/Sema/call_method_protocol.swift b/test/Sema/call_method_protocol.swift index da7efbbf53e27..70b08cd9fb2cb 100644 --- a/test/Sema/call_method_protocol.swift +++ b/test/Sema/call_method_protocol.swift @@ -1,8 +1,8 @@ // RUN: %target-typecheck-verify-swift protocol P0 { - // expected-note @+1 {{protocol requires function 'call()' with type '() -> Missing'; do you want to add a stub?}} - func call() -> Self + // expected-note @+1 {{protocol requires function 'callFunction()' with type '() -> Missing'; do you want to add a stub?}} + func callFunction() -> Self } func testProtocol(_ x: P0) { _ = x() @@ -12,18 +12,18 @@ func testGeneric(_ x: T) { } protocol P1 { - func call() -> Self + func callFunction() -> Self } extension P1 { // expected-note @+1 {{found this candidate}} - func call() -> Self { + func callFunction() -> Self { return self } } protocol P2 {} extension P2 { // expected-note @+1 {{found this candidate}} - func call(x: Int, y: Int) -> Int { + func callFunction(x: Int, y: Int) -> Int { return x + y } } @@ -32,13 +32,13 @@ extension P2 { struct Missing : P0 {} struct S0 : P0 { @discardableResult - func call() -> S0 { return self } + func callFunction() -> S0 { return self } } let s0 = S0() s0() struct S1 : P1 { - func call() -> S1 { return self } + func callFunction() -> S1 { return self } } let s1 = S1() @@ -47,13 +47,13 @@ _ = s1()() struct Conforming : P0 & P1 & P2 {} let conforming = Conforming() _ = conforming(x: 1, y: 2) -_ = conforming().call(x:y:)(1, 2) -_ = conforming.call(x:y:) -_ = conforming.call // expected-error {{ambiguous use of 'call'}} +_ = conforming().callFunction(x:y:)(1, 2) +_ = conforming.callFunction(x:y:) +_ = conforming.callFunction // expected-error {{ambiguous use of 'callFunction'}} protocol P3 {} extension P3 { - func call() -> Self { return self } + func callFunction() -> Self { return self } } struct S3 : P3 {} diff --git a/test/Sema/call_method_simple.swift b/test/Sema/call_method_simple.swift index dea1f055d996a..fe43efe75acd6 100644 --- a/test/Sema/call_method_simple.swift +++ b/test/Sema/call_method_simple.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift struct SimpleCallable { - func call(_ x: Float) -> Float { + func callFunction(_ x: Float) -> Float { return x } } @@ -21,11 +21,11 @@ let _: (Float) -> Float = foo // Test direct `call` member references. -_ = foo.call(1) -_ = [1, 2, 3].map(foo.call) -_ = foo.call(foo(1)) -_ = foo(foo.call(1)) -let _: (Float) -> Float = foo.call +_ = foo.callFunction(1) +_ = [1, 2, 3].map(foo.callFunction) +_ = foo.callFunction(foo(1)) +_ = foo(foo.callFunction(1)) +let _: (Float) -> Float = foo.callFunction func callable() -> SimpleCallable { return SimpleCallable() @@ -42,43 +42,56 @@ extension SimpleCallable { _ = foo.foo(1) _ = foo.bar()(1) _ = callable()(1) -_ = [1, 2, 3].map(foo.foo.call) -_ = [1, 2, 3].map(foo.bar().call) -_ = [1, 2, 3].map(callable().call) +_ = [1, 2, 3].map(foo.foo.callFunction) +_ = [1, 2, 3].map(foo.bar().callFunction) +_ = [1, 2, 3].map(callable().callFunction) struct MultipleArgsCallable { - func call(x: Int, y: Float) -> [Int] { + func callFunction(x: Int, y: Float) -> [Int] { return [x] } } let bar = MultipleArgsCallable() _ = bar(x: 1, y: 1) -_ = bar.call(x: 1, y: 1) -_ = bar(x: bar.call(x: 1, y: 1)[0], y: 1) -_ = bar.call(x: bar(x: 1, y: 1)[0], y: 1) +_ = bar.callFunction(x: 1, y: 1) +_ = bar(x: bar.callFunction(x: 1, y: 1)[0], y: 1) +_ = bar.callFunction(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 call() -> Extended { + func callFunction() -> Extended { return self } } var extended = Extended() -extended()().call()() +extended()().callFunction()() + +struct TakesTrailingClosure { + func callFunction(_ fn: () -> Void) { + fn() + } + func callFunction(_ 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 call() -> OptionalCallable? { + func callFunction() -> OptionalCallable? { return self } } var optional = OptionalCallable() -_ = optional()?.call()?() +_ = optional()?.callFunction()?() struct Variadic { - func call(_ args: Int...) -> [Int] { + func callFunction(_ args: Int...) -> [Int] { return args } } @@ -88,35 +101,35 @@ _ = variadic(1, 2, 3) struct Mutating { var x: Int - mutating func call() { + mutating func callFunction() { x += 1 } } func testMutating(_ x: Mutating, _ y: inout Mutating) { _ = x() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} - _ = x.call() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} + _ = x.callFunction() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} _ = y() - _ = y.call() + _ = y.callFunction() } struct Inout { - func call(_ x: inout Int) { + func callFunction(_ x: inout Int) { x += 5 } } func testInout(_ x: Inout, _ arg: inout Int) { x(&arg) - x.call(&arg) + x.callFunction(&arg) // TODO: Improve this error to match the error using a direct `call` member reference. // expected-error @+2 {{cannot invoke 'x' with an argument list of type '(Int)'}} // expected-error @+1 {{cannot call value of non-function type 'Inout'}} x(arg) // expected-error @+1 {{passing value of type 'Int' to an inout parameter requires explicit '&'}} - x.call(arg) + x.callFunction(arg) } struct Autoclosure { - func call(_ condition: @autoclosure () -> Bool, + func callFunction(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String) { if condition() { print(message()) @@ -129,10 +142,10 @@ func testAutoclosure(_ x: Autoclosure) { } struct Throwing { - func call() throws -> Throwing { + func callFunction() throws -> Throwing { return self } - func call(_ f: () throws -> ()) rethrows { + func callFunction(_ f: () throws -> ()) rethrows { try f() } } @@ -145,7 +158,7 @@ enum BinaryOperation { case add, subtract, multiply, divide } extension BinaryOperation { - func call(_ lhs: Float, _ rhs: Float) -> Float { + func callFunction(_ lhs: Float, _ rhs: Float) -> Float { switch self { case .add: return lhs + rhs case .subtract: return lhs - rhs @@ -157,12 +170,12 @@ extension BinaryOperation { _ = BinaryOperation.add(1, 2) class BaseClass { - func call() -> Self { + func callFunction() -> Self { return self } } class SubClass : BaseClass { - override func call() -> Self { + override func callFunction() -> Self { return self } } @@ -173,10 +186,10 @@ func testIUO(a: SimpleCallable!, b: MultipleArgsCallable!, c: Extended!, _ = a(1) _ = b(x: 1, y: 1) _ = c() - _ = d()?.call()?() + _ = d()?.callFunction()?() _ = e() _ = e(1, 2, 3) - // FIXME(TF-444): `mutating func call` and IUO doesn't work. + // FIXME(TF-444): `mutating func callFunction` and IUO doesn't work. // _ = f() _ = g(&inoutInt) _ = try? h() From 518bda2c5d29bca905d2e4911bb60bf2e1a2e210 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 23 May 2019 10:55:05 -0700 Subject: [PATCH 3/9] Implement SE-0253 review feedback. Use `callAsFunction` as the call-syntax delegate method name. --- include/swift/AST/Decl.h | 4 +- include/swift/AST/KnownIdentifiers.def | 2 +- lib/Sema/CSApply.cpp | 14 ++-- lib/Sema/CSDiag.cpp | 8 +- lib/Sema/CSSimplify.cpp | 14 ++-- .../call_as_function_other_module.swift | 11 +++ .../Inputs/call_method_other_module.swift | 11 --- ...ft => call_as_function_cross_module.swift} | 4 +- ...c.swift => call_as_function_generic.swift} | 12 +-- ....swift => call_as_function_protocol.swift} | 22 +++--- ...le.swift => call_as_function_simple.swift} | 74 +++++++++---------- 11 files changed, 88 insertions(+), 88 deletions(-) create mode 100644 test/Sema/Inputs/call_as_function_other_module.swift delete mode 100644 test/Sema/Inputs/call_method_other_module.swift rename test/Sema/{call_method_cross_module.swift => call_as_function_cross_module.swift} (68%) rename test/Sema/{call_method_generic.swift => call_as_function_generic.swift} (64%) rename test/Sema/{call_method_protocol.swift => call_as_function_protocol.swift} (55%) rename test/Sema/{call_method_simple.swift => call_as_function_simple.swift} (67%) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 224577c5a2bd8..77981cd4e1b31 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5958,8 +5958,8 @@ class FuncDecl : public AbstractFunctionDecl { bool isConsuming() const { return getSelfAccessKind() == SelfAccessKind::__Consuming; } - bool isCallFunction() const { - return getName().str() == "callFunction" && isInstanceMember(); + bool isCallAsFunctionMethod() const { + return getName().str() == "callAsFunction" && isInstanceMember(); } SelfAccessKind getSelfAccessKind() const { diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 4b4e961e8cd88..57d06859e8b2f 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -31,7 +31,7 @@ IDENTIFIER(Any) IDENTIFIER(ArrayLiteralElement) IDENTIFIER(atIndexedSubscript) IDENTIFIER_(bridgeToObjectiveC) -IDENTIFIER(callFunction) +IDENTIFIER(callAsFunction) IDENTIFIER_WITH_NAME(code_, "_code") IDENTIFIER(CodingKeys) IDENTIFIER(combine) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index e8272b59cfdbe..3c013df2405b4 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7051,8 +7051,8 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, llvm_unreachable("Unhandled DeclTypeCheckingSemantics in switch."); }; - // Save the original potentially lvalue function for rewriting call method - // applications. + // Save the original potentially lvalue function for rewriting + // `callAsFunction` method applications. auto *originalFn = fn; // The function is always an rvalue. fn = cs.coerceToRValue(fn); @@ -7200,19 +7200,19 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, return finishApply(apply, openedType, locator); } - // Handle call method applications. + // Handle `callAsFunction` method applications. auto &ctx = cs.getASTContext(); TupleExpr *arg = dyn_cast(apply->getArg()); if (auto parenExpr = dyn_cast(apply->getArg())) arg = TupleExpr::createImplicit(ctx, parenExpr->getSubExpr(), {}); - // Get resolved call method and verify it. + // Get resolved `callAsFunction` method and verify it. auto loc = locator.withPathElement(ConstraintLocator::ApplyFunction); auto selected = solution.getOverloadChoice(cs.getConstraintLocator(loc)); auto choice = selected.choice; auto *callMethod = dyn_cast(selected.choice.getDecl()); - if (callMethod && callMethod->isCallFunction()) { + if (callMethod && callMethod->isCallAsFunctionMethod()) { auto methodType = simplifyType(selected.openedType)->castTo(); auto selfParam = callMethod->getImplicitSelfDecl(); @@ -7227,7 +7227,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, failure.diagnose(); return nullptr; } - // Create direct reference to call method. + // Create direct reference to `callAsFunction` method. bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; Expr *declRef = buildMemberRef(originalFn, selected.openedFullType, /*dotLoc=*/SourceLoc(), choice, @@ -7240,7 +7240,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, return nullptr; declRef->setImplicit(apply->isImplicit()); apply->setFn(declRef); - // Coerce argument to input type of call method. + // Coerce argument to input type of the `callAsFunction` method. SmallVector argLabelsScratch; auto *arg = coerceCallArguments(apply->getArg(), methodType, apply, apply->getArgumentLabels(argLabelsScratch), diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 4aa8c71835e6a..1c0f9b2923a77 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -4797,12 +4797,12 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { auto isDynamicCallable = CS.DynamicCallableCache[fnType->getCanonicalType()].isValid(); - // TODO: Consider caching? + // Note: Consider caching `hasCallAsFunctionMethods` in `NominalTypeDecl`. auto *nominal = fnType->getAnyNominal(); - auto hasCallMethods = nominal && + auto hasCallAsFunctionMethods = nominal && llvm::any_of(nominal->getMembers(), [](Decl *member) { auto funcDecl = dyn_cast(member); - return funcDecl && funcDecl->isCallFunction(); + return funcDecl && funcDecl->isCallAsFunctionMethod(); }); // Diagnose @dynamicCallable errors. @@ -4862,7 +4862,7 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) { } } - if (!isDynamicCallable && !hasCallMethods) + if (!isDynamicCallable && !hasCallAsFunctionMethods) return true; } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 9d91133716d53..02d5aefa63ade 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5833,13 +5833,13 @@ ConstraintSystem::simplifyApplicableFnConstraint( return simplified; } - // Handle applications of types with call methods. + // Handle applications of types with `callAsFunction` methods. if (desugar2->mayHaveMembers()) { auto &ctx = getASTContext(); - // Get all call methods of the nominal type. - // TODO: Consider caching? + // Get all `callAsFunction` methods of the nominal type. + // Note: Consider caching `callAsFunction` methods. SmallVector callMethods; - auto candidates = lookupMember(desugar2, DeclName(ctx.Id_callFunction)); + auto candidates = lookupMember(desugar2, DeclName(ctx.Id_callAsFunction)); for (auto entry : candidates) { auto callMethod = dyn_cast(entry.getValueDecl()); if (!callMethod) @@ -5847,13 +5847,13 @@ ConstraintSystem::simplifyApplicableFnConstraint( callMethods.push_back(callMethod); } - // Handle call methods calls. + // Handle `callAsFunction` methods calls. if (!callMethods.empty()) { - // Create a type variable for the call method. + // Create a type variable for the `callAsFunction` method. auto loc = getConstraintLocator(locator); auto tv = createTypeVariable(loc, TVO_CanBindToLValue); - // Record the call method overload set. + // Record the `callAsFunction` method overload set. SmallVector choices; for (auto candidate : callMethods) { TC.validateDecl(candidate); 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/Inputs/call_method_other_module.swift b/test/Sema/Inputs/call_method_other_module.swift deleted file mode 100644 index a17ce53801d17..0000000000000 --- a/test/Sema/Inputs/call_method_other_module.swift +++ /dev/null @@ -1,11 +0,0 @@ -public protocol Layer { - func callFunction(_ input: Float) -> Float -} - -public struct Dense { - public init() {} - - public func callFunction(_ input: Float) -> Float { - return input * 2 - } -} diff --git a/test/Sema/call_method_cross_module.swift b/test/Sema/call_as_function_cross_module.swift similarity index 68% rename from test/Sema/call_method_cross_module.swift rename to test/Sema/call_as_function_cross_module.swift index d97628cf20b88..e88ec62e5d2fd 100644 --- a/test/Sema/call_method_cross_module.swift +++ b/test/Sema/call_as_function_cross_module.swift @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -primary-file %S/Inputs/call_method_other_module.swift -emit-module-path %t/call_method_other_module.swiftmodule +// 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_method_other_module +import call_as_function_other_module func testLayer(_ layer: L) -> Float { return layer(1) diff --git a/test/Sema/call_method_generic.swift b/test/Sema/call_as_function_generic.swift similarity index 64% rename from test/Sema/call_method_generic.swift rename to test/Sema/call_as_function_generic.swift index 7391222f343c6..e86406180d86d 100644 --- a/test/Sema/call_method_generic.swift +++ b/test/Sema/call_as_function_generic.swift @@ -1,22 +1,22 @@ // RUN: %target-typecheck-verify-swift protocol P0 { - func callFunction(x: Self) + func callAsFunction(x: Self) } struct ConcreteType { - func callFunction(_ x: T, _ y: U) -> (T, U) { + func callAsFunction(_ x: T, _ y: U) -> (T, U) { return (x, y) } - func callFunction(_ fn: @escaping (T) -> U) -> (T) -> U { + func callAsFunction(_ fn: @escaping (T) -> U) -> (T) -> U { return fn } } let concrete = ConcreteType() _ = concrete(1, 3.0) -_ = concrete(concrete, concrete.callFunction as ([Int], Float) -> ([Int], Float)) +_ = concrete(concrete, concrete.callAsFunction as ([Int], Float) -> ([Int], Float)) func generic(_ x: T, _ y: U) { _ = concrete(x, x) @@ -25,14 +25,14 @@ func generic(_ x: T, _ y: U) { struct GenericType { let collection: T - func callFunction(_ x: U) -> Bool where U == T.Element, U : Equatable { + 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 callFunction(initialValue: T.Element) -> T.Element { + func callAsFunction(initialValue: T.Element) -> T.Element { return collection.reduce(initialValue, +) } } diff --git a/test/Sema/call_method_protocol.swift b/test/Sema/call_as_function_protocol.swift similarity index 55% rename from test/Sema/call_method_protocol.swift rename to test/Sema/call_as_function_protocol.swift index 70b08cd9fb2cb..1497c615451bb 100644 --- a/test/Sema/call_method_protocol.swift +++ b/test/Sema/call_as_function_protocol.swift @@ -1,8 +1,8 @@ // RUN: %target-typecheck-verify-swift protocol P0 { - // expected-note @+1 {{protocol requires function 'callFunction()' with type '() -> Missing'; do you want to add a stub?}} - func callFunction() -> Self + // 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() @@ -12,18 +12,18 @@ func testGeneric(_ x: T) { } protocol P1 { - func callFunction() -> Self + func callAsFunction() -> Self } extension P1 { // expected-note @+1 {{found this candidate}} - func callFunction() -> Self { + func callAsFunction() -> Self { return self } } protocol P2 {} extension P2 { // expected-note @+1 {{found this candidate}} - func callFunction(x: Int, y: Int) -> Int { + func callAsFunction(x: Int, y: Int) -> Int { return x + y } } @@ -32,13 +32,13 @@ extension P2 { struct Missing : P0 {} struct S0 : P0 { @discardableResult - func callFunction() -> S0 { return self } + func callAsFunction() -> S0 { return self } } let s0 = S0() s0() struct S1 : P1 { - func callFunction() -> S1 { return self } + func callAsFunction() -> S1 { return self } } let s1 = S1() @@ -47,13 +47,13 @@ _ = s1()() struct Conforming : P0 & P1 & P2 {} let conforming = Conforming() _ = conforming(x: 1, y: 2) -_ = conforming().callFunction(x:y:)(1, 2) -_ = conforming.callFunction(x:y:) -_ = conforming.callFunction // expected-error {{ambiguous use of 'callFunction'}} +_ = conforming().callAsFunction(x:y:)(1, 2) +_ = conforming.callAsFunction(x:y:) +_ = conforming.callAsFunction // expected-error {{ambiguous use of 'callAsFunction'}} protocol P3 {} extension P3 { - func callFunction() -> Self { return self } + func callAsFunction() -> Self { return self } } struct S3 : P3 {} diff --git a/test/Sema/call_method_simple.swift b/test/Sema/call_as_function_simple.swift similarity index 67% rename from test/Sema/call_method_simple.swift rename to test/Sema/call_as_function_simple.swift index fe43efe75acd6..fbc05a88809fe 100644 --- a/test/Sema/call_method_simple.swift +++ b/test/Sema/call_as_function_simple.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift struct SimpleCallable { - func callFunction(_ x: Float) -> Float { + func callAsFunction(_ x: Float) -> Float { return x } } @@ -12,20 +12,20 @@ let foo = SimpleCallable() _ = foo(1) _ = foo(foo(1)) -// TODO: Improve this error to match the error using a direct `call` member reference. +// TODO: 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 `call` member references. +// Test direct `callAsFunction` member references. -_ = foo.callFunction(1) -_ = [1, 2, 3].map(foo.callFunction) -_ = foo.callFunction(foo(1)) -_ = foo(foo.callFunction(1)) -let _: (Float) -> Float = foo.callFunction +_ = 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() @@ -42,38 +42,38 @@ extension SimpleCallable { _ = foo.foo(1) _ = foo.bar()(1) _ = callable()(1) -_ = [1, 2, 3].map(foo.foo.callFunction) -_ = [1, 2, 3].map(foo.bar().callFunction) -_ = [1, 2, 3].map(callable().callFunction) +_ = [1, 2, 3].map(foo.foo.callAsFunction) +_ = [1, 2, 3].map(foo.bar().callAsFunction) +_ = [1, 2, 3].map(callable().callAsFunction) struct MultipleArgsCallable { - func callFunction(x: Int, y: Float) -> [Int] { + func callAsFunction(x: Int, y: Float) -> [Int] { return [x] } } let bar = MultipleArgsCallable() _ = bar(x: 1, y: 1) -_ = bar.callFunction(x: 1, y: 1) -_ = bar(x: bar.callFunction(x: 1, y: 1)[0], y: 1) -_ = bar.callFunction(x: bar(x: 1, y: 1)[0], 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 callFunction() -> Extended { + func callAsFunction() -> Extended { return self } } var extended = Extended() -extended()().callFunction()() +extended()().callAsFunction()() struct TakesTrailingClosure { - func callFunction(_ fn: () -> Void) { + func callAsFunction(_ fn: () -> Void) { fn() } - func callFunction(_ x: Int, label y: Float, _ fn: (Int, Float) -> Void) { + func callAsFunction(_ x: Int, label y: Float, _ fn: (Int, Float) -> Void) { fn(x, y) } } @@ -83,15 +83,15 @@ takesTrailingClosure() { print("Hi") } takesTrailingClosure(1, label: 2) { _ = Float($0) + $1 } struct OptionalCallable { - func callFunction() -> OptionalCallable? { + func callAsFunction() -> OptionalCallable? { return self } } var optional = OptionalCallable() -_ = optional()?.callFunction()?() +_ = optional()?.callAsFunction()?() struct Variadic { - func callFunction(_ args: Int...) -> [Int] { + func callAsFunction(_ args: Int...) -> [Int] { return args } } @@ -101,35 +101,35 @@ _ = variadic(1, 2, 3) struct Mutating { var x: Int - mutating func callFunction() { + mutating func callAsFunction() { x += 1 } } func testMutating(_ x: Mutating, _ y: inout Mutating) { _ = x() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} - _ = x.callFunction() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} + _ = x.callAsFunction() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} _ = y() - _ = y.callFunction() + _ = y.callAsFunction() } struct Inout { - func callFunction(_ x: inout Int) { + func callAsFunction(_ x: inout Int) { x += 5 } } func testInout(_ x: Inout, _ arg: inout Int) { x(&arg) - x.callFunction(&arg) - // TODO: Improve this error to match the error using a direct `call` member reference. + x.callAsFunction(&arg) + // TODO: Improve this error to match the error using a direct `callAsFunction` member reference. // expected-error @+2 {{cannot invoke 'x' with an argument list of type '(Int)'}} // expected-error @+1 {{cannot call value of non-function type 'Inout'}} x(arg) // expected-error @+1 {{passing value of type 'Int' to an inout parameter requires explicit '&'}} - x.callFunction(arg) + x.callAsFunction(arg) } struct Autoclosure { - func callFunction(_ condition: @autoclosure () -> Bool, + func callAsFunction(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String) { if condition() { print(message()) @@ -142,10 +142,10 @@ func testAutoclosure(_ x: Autoclosure) { } struct Throwing { - func callFunction() throws -> Throwing { + func callAsFunction() throws -> Throwing { return self } - func callFunction(_ f: () throws -> ()) rethrows { + func callAsFunction(_ f: () throws -> ()) rethrows { try f() } } @@ -158,7 +158,7 @@ enum BinaryOperation { case add, subtract, multiply, divide } extension BinaryOperation { - func callFunction(_ lhs: Float, _ rhs: Float) -> Float { + func callAsFunction(_ lhs: Float, _ rhs: Float) -> Float { switch self { case .add: return lhs + rhs case .subtract: return lhs - rhs @@ -170,12 +170,12 @@ extension BinaryOperation { _ = BinaryOperation.add(1, 2) class BaseClass { - func callFunction() -> Self { + func callAsFunction() -> Self { return self } } class SubClass : BaseClass { - override func callFunction() -> Self { + override func callAsFunction() -> Self { return self } } @@ -186,10 +186,10 @@ func testIUO(a: SimpleCallable!, b: MultipleArgsCallable!, c: Extended!, _ = a(1) _ = b(x: 1, y: 1) _ = c() - _ = d()?.callFunction()?() + _ = d()?.callAsFunction()?() _ = e() _ = e(1, 2, 3) - // FIXME(TF-444): `mutating func callFunction` and IUO doesn't work. + // FIXME(TF-444): `mutating func callAsFunction` and IUO doesn't work. // _ = f() _ = g(&inoutInt) _ = try? h() From 9695a26dc7c4f7b32dcf48d81d2c5b7b93496717 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 23 May 2019 11:53:12 -0700 Subject: [PATCH 4/9] Address review feedback. --- include/swift/AST/Decl.h | 4 +--- lib/AST/Decl.cpp | 4 ++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 77981cd4e1b31..e71b4f6082f20 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5958,9 +5958,7 @@ class FuncDecl : public AbstractFunctionDecl { bool isConsuming() const { return getSelfAccessKind() == SelfAccessKind::__Consuming; } - bool isCallAsFunctionMethod() const { - return getName().str() == "callAsFunction" && isInstanceMember(); - } + bool isCallAsFunctionMethod() const; SelfAccessKind getSelfAccessKind() const { return static_cast(Bits.FuncDecl.SelfAccess); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 635d24cabde91..44d65de63cc41 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6666,6 +6666,10 @@ bool FuncDecl::isBinaryOperator() const { !params->get(1)->isVariadic(); } +bool FuncDecl::isCallAsFunctionMethod() const { + return getName() == getASTContext().Id_callAsFunction && isInstanceMember(); +} + ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc, OptionalTypeKind Failability, SourceLoc FailabilityLoc, From e21383689ef2c301376612154b104ff160cceec8 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 27 Jun 2019 16:57:40 -0700 Subject: [PATCH 5/9] Fix `mutating func callAsFunction` + IUO. - Reorganize `ExprRewriter::finishApply` to check `callAsFunction` and `@dynamicCallable` cases before coercing the function expression to an rvalue. - Enable `mutating callAsFunction` + IUO test. --- lib/Sema/CSApply.cpp | 239 ++++++++++++------------ test/Sema/call_as_function_simple.swift | 3 +- 2 files changed, 122 insertions(+), 120 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 2b472f68b96c4..d851507fa4b74 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6792,11 +6792,70 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, return literal; } -// Resolve @dynamicCallable applications. -static Expr *finishApplyDynamicCallable(ConstraintSystem &cs, - const Solution &solution, - ApplyExpr *apply, - ConstraintLocatorBuilder locator) { +// Returns true if the given method and method type are a valid +// `@dynamicCallable` required `func dynamicallyCall` method. +static bool isValidDynamicallyCallMethod(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; + auto *method = dyn_cast(choice.getDecl()); + auto selfParam = method->getImplicitSelfDecl(); + + // Diagnose `mutating` method call on immutable value. + if (!cs.getType(fn)->hasLValueType() && selfParam->isInOut()) { + AssignmentFailure failure(fn, cs, fn->getLoc(), + diag::cannot_pass_rvalue_mutating_subelement, + diag::cannot_pass_rvalue_mutating); + failure.diagnose(); + return nullptr; + } + // 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. +static Expr *finishApplyDynamicCallable(ConstraintSystem &cs, ApplyExpr *apply, + FuncDecl *method, + AnyFunctionType *methodType) { auto &ctx = cs.getASTContext(); auto *fn = apply->getFn(); @@ -6805,22 +6864,11 @@ static Expr *finishApplyDynamicCallable(ConstraintSystem &cs, 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 = selected.openedType->castTo(); - assert(method->getName() == ctx.Id_dynamicallyCall && - "Expected 'dynamicallyCall' method"); - assert(methodType->getParams().size() == 1 && - "Expected 'dynamicallyCall' method with one parameter"); - auto argumentLabel = methodType->getParams()[0].getLabel(); - assert((argumentLabel == ctx.Id_withArguments || - argumentLabel == ctx.Id_withKeywordArguments) && - "Expected 'dynamicallyCall' method argument label 'withArguments' or " - "'withKeywordArguments'"); + assert(isValidDynamicallyCallMethod(method, methodType)); // 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. @@ -7005,9 +7053,23 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, llvm_unreachable("Unhandled DeclTypeCheckingSemantics in switch."); }; - // Save the original potentially lvalue function for rewriting - // `callAsFunction` method applications. - auto *originalFn = fn; + // 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 && isValidDynamicallyCallMethod(method, methodType)) + return finishApplyDynamicCallable(cs, apply, method, methodType); + } + } + // The function is always an rvalue. fn = cs.coerceToRValue(fn); @@ -7108,110 +7170,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 `callAsFunction` method applications. - auto &ctx = cs.getASTContext(); - - TupleExpr *arg = dyn_cast(apply->getArg()); - if (auto parenExpr = dyn_cast(apply->getArg())) - arg = TupleExpr::createImplicit(ctx, parenExpr->getSubExpr(), {}); - - // Get resolved `callAsFunction` method and verify it. - auto loc = locator.withPathElement(ConstraintLocator::ApplyFunction); - auto selected = solution.getOverloadChoice(cs.getConstraintLocator(loc)); - auto choice = selected.choice; - auto *callMethod = dyn_cast(selected.choice.getDecl()); - if (callMethod && callMethod->isCallAsFunctionMethod()) { - auto methodType = - simplifyType(selected.openedType)->castTo(); - auto selfParam = callMethod->getImplicitSelfDecl(); - // Diagnose `mutating` method call on immutable value. - // FIXME(TF-444): This logic for `mutating` method calls incorrectly rejects - // IUOs. Performing this ad-hoc check using `originalFn` feels hacky; - // rewrite if possible. - if (!cs.getType(originalFn)->hasLValueType() && selfParam->isInOut()) { - AssignmentFailure failure(originalFn, cs, originalFn->getLoc(), - diag::cannot_pass_rvalue_mutating_subelement, - diag::cannot_pass_rvalue_mutating); - failure.diagnose(); - return nullptr; - } - // Create direct reference to `callAsFunction` method. - bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; - Expr *declRef = buildMemberRef(originalFn, selected.openedFullType, - /*dotLoc=*/SourceLoc(), choice, - DeclNameLoc(fn->getEndLoc()), - selected.openedType, loc, loc, - /*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 = coerceCallArguments(apply->getArg(), methodType, apply, - apply->getArgumentLabels(argLabelsScratch), - apply->hasTrailingClosure(), loc); - if (!arg) - return nullptr; - apply->setArg(arg); - cs.setType(apply, methodType->getResult()); - cs.cacheExprTypes(apply); + // 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; } - // Handle @dynamicCallable applications. - // At this point, all other ApplyExpr cases have been handled. - return finishApplyDynamicCallable(cs, solution, apply, locator); -} + 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/test/Sema/call_as_function_simple.swift b/test/Sema/call_as_function_simple.swift index fbc05a88809fe..7fa3d09ae4acd 100644 --- a/test/Sema/call_as_function_simple.swift +++ b/test/Sema/call_as_function_simple.swift @@ -189,8 +189,7 @@ func testIUO(a: SimpleCallable!, b: MultipleArgsCallable!, c: Extended!, _ = d()?.callAsFunction()?() _ = e() _ = e(1, 2, 3) - // FIXME(TF-444): `mutating func callAsFunction` and IUO doesn't work. - // _ = f() + _ = f() _ = g(&inoutInt) _ = try? h() _ = try? h { throw DummyError() } From 8dcd1c68b2acbf6fffc664b853b168a851584ea5 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Sat, 10 Aug 2019 23:02:52 -0700 Subject: [PATCH 6/9] Clean up CSSimplify and CSApply for `func callAsFunction`. - CSSimplify: get type variable for `func callAsFunction` member by generating a member constraint (with the potentially-lvalue base `type2`) rather than manually constructing an overload set. - CSApply: remove hack for diagnosing invalid `mutating callAsFunction` calls. - Add todo comments for improving `ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint`. --- lib/Sema/CSApply.cpp | 11 --- lib/Sema/CSSimplify.cpp | 102 ++++++++++-------------- test/Sema/call_as_function_simple.swift | 12 +-- 3 files changed, 51 insertions(+), 74 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index f1d2b233ba352..5489ee2feb9cb 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6775,17 +6775,6 @@ static Expr *finishApplyCallAsFunctionMethod( auto &cs = rewriter.cs; auto *fn = apply->getFn(); auto choice = selected.choice; - auto *method = dyn_cast(choice.getDecl()); - auto selfParam = method->getImplicitSelfDecl(); - - // Diagnose `mutating` method call on immutable value. - if (!cs.getType(fn)->hasLValueType() && selfParam->isInOut()) { - AssignmentFailure failure(fn, cs, fn->getLoc(), - diag::cannot_pass_rvalue_mutating_subelement, - diag::cannot_pass_rvalue_mutating); - failure.diagnose(); - return nullptr; - } // Create direct reference to `callAsFunction` method. bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic; auto *declRef = rewriter.buildMemberRef( diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 6db5e138d6630..cc43a141f9634 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,15 @@ ConstraintSystem::simplifyApplicableFnConstraint( } } + // Before stripping lvalue-ness and optional types, save original 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 *origType2 = type2->getDesugaredType(); // Drill down to the concrete type on the right hand side. type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true); auto desugar2 = type2->getDesugaredType(); @@ -6080,10 +6090,33 @@ 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. - auto origType2 = desugar2; + // 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, TVO_CanBindToLValue | + TVO_CanBindToNoEscape | + TVO_CanBindToInOut); + addValueMemberConstraint(origType2, 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; + } + unsigned unwrapCount = 0; if (shouldAttemptFixes()) { // If we have an optional type, try forcing it to see if that @@ -6165,60 +6198,6 @@ ConstraintSystem::simplifyApplicableFnConstraint( return simplified; } - // Handle applications of types with `callAsFunction` methods. - if (desugar2->mayHaveMembers()) { - auto &ctx = getASTContext(); - // Get all `callAsFunction` methods of the nominal type. - // Note: Consider caching `callAsFunction` methods. - SmallVector callMethods; - auto candidates = lookupMember(desugar2, DeclName(ctx.Id_callAsFunction)); - for (auto entry : candidates) { - auto callMethod = dyn_cast(entry.getValueDecl()); - if (!callMethod) - continue; - callMethods.push_back(callMethod); - } - - // Handle `callAsFunction` methods calls. - if (!callMethods.empty()) { - // Create a type variable for the `callAsFunction` method. - auto loc = getConstraintLocator(locator); - auto tv = createTypeVariable(loc, TVO_CanBindToLValue); - - // Record the `callAsFunction` method overload set. - SmallVector choices; - for (auto candidate : callMethods) { - TC.validateDecl(candidate); - if (candidate->isInvalid()) continue; - choices.push_back( - OverloadChoice(type2, candidate, FunctionRefKind::SingleApply)); - } - if (choices.empty()) return SolutionKind::Error; - addOverloadSet(tv, choices, DC, loc); - - // Create type variables for each parameter type. - SmallVector tvParams; - for (unsigned i : range(func1->getNumParams())) { - auto param = func1->getParams()[i]; - auto paramType = param.getPlainType(); - - auto *tvParam = createTypeVariable(loc, TVO_CanBindToNoEscape); - auto locatorBuilder = - locator.withPathElement(LocatorPathElt::getTupleElement(i)); - addConstraint(ConstraintKind::ArgumentConversion, paramType, - tvParam, locatorBuilder); - tvParams.push_back(AnyFunctionType::Param( - tvParam, Identifier(), param.getParameterFlags())); - } - // Create target function type and an applicable function constraint. - AnyFunctionType *funcType = - FunctionType::get(tvParams, func1->getResult()); - addConstraint(ConstraintKind::ApplicableFunction, funcType, tv, locator); - - return SolutionKind::Solved; - } - } - // Handle applications of @dynamicCallable types. return simplifyDynamicCallableApplicableFnConstraint(type1, origType2, subflags, locator); @@ -6359,6 +6338,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/call_as_function_simple.swift b/test/Sema/call_as_function_simple.swift index 7fa3d09ae4acd..4c3e8d733fa8b 100644 --- a/test/Sema/call_as_function_simple.swift +++ b/test/Sema/call_as_function_simple.swift @@ -106,8 +106,12 @@ struct Mutating { } } func testMutating(_ x: Mutating, _ y: inout Mutating) { - _ = x() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} - _ = x.callAsFunction() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} + // TODO: 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() } @@ -120,9 +124,7 @@ struct Inout { func testInout(_ x: Inout, _ arg: inout Int) { x(&arg) x.callAsFunction(&arg) - // TODO: Improve this error to match the error using a direct `callAsFunction` member reference. - // expected-error @+2 {{cannot invoke 'x' with an argument list of type '(Int)'}} - // expected-error @+1 {{cannot call value of non-function type 'Inout'}} + // 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) From 4cb7ebf8a0345a0e7f9041a0be41990e68c7cd14 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Sat, 10 Aug 2019 23:06:31 -0700 Subject: [PATCH 7/9] Add Optional `callAsFunction` test. --- test/Sema/call_as_function_optional.swift | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 test/Sema/call_as_function_optional.swift 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 }) +} From cebbd6335dab7e0bbf3e5c0dff9c445588e01f81 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Sat, 24 Aug 2019 00:19:28 -0700 Subject: [PATCH 8/9] Address review comments, fix test. - Rename `isValidDynamicallyCallMethod` to `isValidDynamicCallableMethod`. - Add todo hint regarding `static func callAsFunction`, if it is to be supported. - Restore `var origType2` before unwrapping optionality. - This fixes `expr/delayed-ident/static_var.swift`. --- lib/Sema/CSApply.cpp | 6 +++--- lib/Sema/CSSimplify.cpp | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 5489ee2feb9cb..d333de41a41d0 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6753,7 +6753,7 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, // Returns true if the given method and method type are a valid // `@dynamicCallable` required `func dynamicallyCall` method. -static bool isValidDynamicallyCallMethod(FuncDecl *method, +static bool isValidDynamicCallableMethod(FuncDecl *method, AnyFunctionType *methodType) { auto &ctx = method->getASTContext(); if (method->getName() != ctx.Id_dynamicallyCall) @@ -6815,7 +6815,7 @@ ExprRewriter::finishApplyDynamicCallable(ApplyExpr *apply, arg = TupleExpr::createImplicit(ctx, parenExpr->getSubExpr(), {}); // Get resolved `dynamicallyCall` method and verify it. - assert(isValidDynamicallyCallMethod(method, methodType)); + assert(isValidDynamicCallableMethod(method, methodType)); auto params = methodType->getParams(); auto argumentType = params[0].getParameterType(); @@ -7043,7 +7043,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType, if (method->isCallAsFunctionMethod()) return finishApplyCallAsFunctionMethod( *this, apply, *selected, methodType, applyFunctionLoc); - if (methodType && isValidDynamicallyCallMethod(method, methodType)) + if (methodType && isValidDynamicCallableMethod(method, methodType)) return finishApplyDynamicCallable( apply, *selected, method, methodType, applyFunctionLoc); } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index cc43a141f9634..edb77a93e79ef 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -6012,15 +6012,16 @@ ConstraintSystem::simplifyApplicableFnConstraint( } } - // Before stripping lvalue-ness and optional types, save original type for - // handling `func callAsFunction` and `@dynamicCallable` applications. - // This supports the following cases: + // 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 *origType2 = type2->getDesugaredType(); + 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(); @@ -6104,10 +6105,10 @@ ConstraintSystem::simplifyApplicableFnConstraint( outerLocator.withPathElement(ConstraintLocator::Member)); // Add a `callAsFunction` member constraint, binding the member type to a // type variable. - auto memberTy = createTypeVariable(memberLoc, TVO_CanBindToLValue | - TVO_CanBindToNoEscape | - TVO_CanBindToInOut); - addValueMemberConstraint(origType2, DeclName(ctx.Id_callAsFunction), + 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 @@ -6117,6 +6118,8 @@ ConstraintSystem::simplifyApplicableFnConstraint( return SolutionKind::Solved; } + // Record the second type before unwrapping optionals. + auto origType2 = desugar2; unsigned unwrapCount = 0; if (shouldAttemptFixes()) { // If we have an optional type, try forcing it to see if that From 0733cc05ec9471e6e6076ef7ba5d7b1f025db03c Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Mon, 26 Aug 2019 21:08:42 -0700 Subject: [PATCH 9/9] Reference SR-11378 in todo comments: improve `callAsFunction` diagnostics. --- test/Sema/call_as_function_simple.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Sema/call_as_function_simple.swift b/test/Sema/call_as_function_simple.swift index 4c3e8d733fa8b..0c1253dd2ac17 100644 --- a/test/Sema/call_as_function_simple.swift +++ b/test/Sema/call_as_function_simple.swift @@ -12,7 +12,7 @@ let foo = SimpleCallable() _ = foo(1) _ = foo(foo(1)) -// TODO: Improve this error to match the error using a direct `callAsFunction` member reference. +// 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) @@ -106,7 +106,7 @@ struct Mutating { } } func testMutating(_ x: Mutating, _ y: inout Mutating) { - // TODO: Improve this error to match the error using a direct `callAsFunction` member reference. + // 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()