-
Notifications
You must be signed in to change notification settings - Fork 10.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement @dynamicCallable
.
#20305
Implement @dynamicCallable
.
#20305
Conversation
41f9f15
to
2b358e3
Compare
The implementation has been revised to match the proposal.
Additionally, I decided to support multiple
Please look at the test file to see what's supported/unsupported. |
- Implement dynamically callable types as proposed in SE-0216. - Dynamic calls are resolved based on call-site syntax. - Use the `withArguments:` method if it's defined and there are no keyword arguments. - Otherwise, use the `withKeywordArguments:` method. - Support multiple `dynamicallyCall` methods. - This enables two scenarios: - Overloaded `dynamicallyCall` methods on a single `@dynamicCallable` type. - Multiple `dynamicallyCall` methods from a `@dynamicCallable` superclass or from `@dynamicCallable` protocols. - Add `DynamicCallableApplicableFunction` constraint. This, used with an overload set, is necessary to support multiple `dynamicallyCall` methods.
2b358e3
to
e651ed8
Compare
Awesome Dan! |
e2e426a
to
803dbfb
Compare
@@ -4432,6 +4432,9 @@ 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a test exercising this fringe case:
extension Optional : KeywordCallableProtocol {}
func testExtensions() {
let x: Int? = 3
// Test `Optional` extension.
print(x())
print(x(label: 1, 2))
}
If origType2
is not used and optional types are stripped, the test fails:
/Users/dan/swift-build/swift/test/attr/attr_dynamic_callable.swift:249:10: error: unexpected error produced: cannot call value of non-function type 'Int?'
print(x())
^
/Users/dan/swift-build/swift/test/attr/attr_dynamic_callable.swift:250:10: error: unexpected error produced: cannot call value of non-function type 'Int?'
print(x(label: 1, 2))
^
@swift-ci Please smoke test |
@swift-ci build toolchain |
I added some implementation notes to the PR description. @rudkx I wonder if you (or other code owners) have the time to please review this PR? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure the code fits in 80 columns, but it otherwise looks ok to me.
@rudkx do you mind if Dan merges this? We can continue to improve it in master.
lib/Sema/CSApply.cpp
Outdated
/*dotLoc=*/SourceLoc(), choice, | ||
DeclNameLoc(fn->getEndLoc()), | ||
selected->openedType, locator, ctorLocator, | ||
/*Implicit=*/true, choice.getFunctionRefKind(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fit in 80 columns.
lib/Sema/CSSimplify.cpp
Outdated
auto cand = cast<FuncDecl>(choice.getDecl()); | ||
return !isValidDynamicCallableMethod(cand, decl, CS.TC, hasKeywordArgs); | ||
}; | ||
candidates.erase(std::remove_if(candidates.begin(), candidates.end(), filter), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very minor, but:
This line was actually at 80 columns long, actually (i.e. the last character was on column 80).
I wonder if the 80 column convention is:
- The last character of a line can be on column 80 (or before). I've been following this.
- The last character of a line can be on column 79 (or before). I suppose this makes the line "fit within 80 columns".
Anyhow, I changed this line and other lines that end on column 80 so that they end on column 79.
lib/Sema/CSSimplify.cpp
Outdated
lookupDynamicCallableMethods(type, CS, locator, ctx.Id_withArguments, | ||
/*hasKeywordArgs*/ false); | ||
methods.keywordArgumentsMethods = | ||
lookupDynamicCallableMethods(type, CS, locator, ctx.Id_withKeywordArguments, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too
Yes it's fine to merge. I did a first pass over it yesterday and intended on taking a closer look today but haven't had a chance. I think the risk is very small that it will cause issues with code that isn't using the new feature. It looks like the source compatibility suite is kind of back into shape today so it would be good to let a run of that complete before hitting merge. |
@swift-ci Please test source compatibility |
- Fit lines within 80 columns (addresses feedback by @lattner). - Rename `DictionaryLiteral` to `KeyValuePairs`.
@swift-ci Please smoke test |
@swift-ci Please test source compatibility |
It seems the source compatibility suite failed due to a CI error: |
e64808e
to
06e2b0d
Compare
Fix indentation and tweak comments.
06e2b0d
to
14e28ac
Compare
All smoke/source compatibility tests passed! |
@swift-ci Please smoke test |
@swift-ci Please test source compatibility |
To reviewers: I'll leave merging the PR to you. After you've reviewed and feel confident. |
Thank you Dan!!! Please update the ChangeLog.md file if you haven't already! |
Yay! I'm happy to land this PR after first opening it in June. 😛Thanks for your patience! I'll update the changelog soon. EDIT 2: I plan to write a blog post documenting my experience implementing |
// If the right-hand side is not a function type, it must be a valid | ||
// @dynamicCallable type. Attempt to get valid `dynamicallyCall` methods. | ||
auto methods = getDynamicCallableMethods(desugar2, *this, locator); | ||
if (!methods.isValid()) return SolutionKind::Error; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@slavapestov: you gave this feedback on the old implementation PR:
Also you should not emit diagnostics during constraint simplification because we can explore different parts of the solution space as we visit disjunctions, and discard invalid paths.
I made sure not to emit diagnostics here, during simplification. Instead, I return SolutionKind::Error
and added diagnostic logic in CSDiag.cpp
.
FYI: I'll try to push two extra PRs before the Swift 5 cutoff:
|
This PR introduces the
@dynamicCallable
attribute, which enables nominal types to be "callable" via a simple syntactic sugar.This is the implementation of SE-0216. Read the proposal for more details.
@dynamicCallable
is a follow-up to SE-0195 "Dynamic Member Lookup".Forum discussion regarding
@dynamicCallable
:Proposal review and acceptance rationale:
Implementation notes:
@dynamicCallable
implementation has been revamped according to proposal clarifications.@dynamicCallable
type implements thewithArguments:
method and it is called with no keyword arguments, use thewithArguments:
method.withKeywordArguments:
method.@dynamicCallable
types with multipledynamicallyCall
methods are supported.dynamicallyCall
methods if it inherits from a@dynamicCallable
superclass or conforms to@dynamicCallable
protocols, or if it itself defines overloadeddynamicallyCall
methods.dynamicallyCall
methods, the implementation creates a disjunction constraint over alldynamicallyCall
methods, and pairs it with aDynamicCallableApplicableFunction
constraint.ApplicableFunction
constraint, but encountered a crash involving a parameter label count mismatch inconstraints::matchCallArguments
.x(1, 2, 3)
) but thedynamicallyCall
method defines only one parameter (e.g. one parameter inx.dynamicallyCall(withArguments: [Int]
).CallExpr
and the parameter count of the function it calls don't match).DynamicallyCallableApplicableFunction
constraint kind.simplifyDynamicCallableApplicableFnConstraint
has custom logic for checking parameter types and doesn't produce the crash.test/attr/attr_dynamic_callable.swift
to see what functionality is supported/unsupported.