Skip to content

[swift2objc] Support closure#3207

Open
Cairo09 wants to merge 22 commits intodart-lang:mainfrom
Cairo09:support-closure
Open

[swift2objc] Support closure#3207
Cairo09 wants to merge 22 commits intodart-lang:mainfrom
Cairo09:support-closure

Conversation

@Cairo09
Copy link
Copy Markdown
Contributor

@Cairo09 Cairo09 commented Mar 6, 2026

Fixes: #1669

Changes made:

  1. added closure parsing as a suffix parselet by supporting 'text: ->' in _suffixParsets, so any parsed type followed by -> ReturnType is converted into ClosureType
  2. reused tuple parsing output as planned- parameterside parsing first produces either voidType, a single type, or TupleType then _closureParametersFromType converts that to closure parameter list that is () to [], (A, B)to [A, B] tuple elements to many params, single to one param (Int) or Int to [Int]
  3. added _parseTypeExpressionUntilArrow so parser must stop parameter side parsing before ->
  4. -> is in _suffixParsets, but async/ or throws before -> are handled in _maybeParseSuffixTypeExpression by _parseClosureModifiers
  5. _sameAs and aliasedType are used so thunking happens only when closure signatures differ
  6. Added ClosureType in AST with fields for parameters, return type, and isEscaping, isSendable, isAsync, isThrowing
    7.Added visitClosureType to AST
  7. Preserved @escaping , @sendable in parsing through _attributeParselet
    9.Added closure effect parsing (for async, throws) using _parseClosureModifiers
  8. Added closure adaptation in function transformation so wrapper methods generate closure thunks only when original and transformed closure signatures differ.

Tests added:

  1. checks parsing of plain closures
    2.attributed closures parsing
    3.nested,optional closures, empty parameter closures parsing
    4.checks closure flag preservation
    5.method level closure param and return support
  2. adapter behavior tests- thunk only when needed, sendable propagation, no @escaping on closure literals, wrap or unwrap direction correctness
    added e2e test

PR Checklist

  • I’ve reviewed the contributor guide and applied the relevant portions to this PR.
  • I've run dart tool/ci.dart --all locally and resolved all issues identified. This ensures the PR is formatted, has no lint errors, and ran all code generators. This applies to the packages part of the toplevel pubspec.yaml workspace.
  • All existing and new tests are passing. I added new tests to check the change I am making.
  • The PR is actually solving the issue. PRs that don't solve the issue will be closed. Please be respectful of the maintainers' time. If it's not clear what the issue is, feel free to ask questions on the GitHub issue before submitting a PR.
  • I have updated CHANGELOG.md for the relevant packages. (Not needed for small changes such as doc typos).
  • I have updated the pubspec package version if necessary.

@Cairo09
Copy link
Copy Markdown
Contributor Author

Cairo09 commented Mar 6, 2026

I added : to splittables because in the e2e integration test for closure where there is (callback:
it split as ":(" so it produced an error of Malformed parameter list

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 8, 2026

PR Health

Breaking changes ✔️
Package Change Current Version New Version Needed Version Looking good?

This check can be disabled by tagging the PR with skip-breaking-check.

Changelog Entry ✔️
Package Changed Files

Changes to files need to be accounted for in their respective changelogs.

This check can be disabled by tagging the PR with skip-changelog-check.

API leaks ✔️

The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.

Package Leaked API symbol Leaking sources

This check can be disabled by tagging the PR with skip-leaking-check.

TokenList fragments,
) => _parseTypeExpression(context, symbolgraph, fragments);

(ReferredType, TokenList) _parseTypeExpression(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of separating parseType and _parseTypeExpression?

return parselet(context, symbolgraph, token, fragments.slice(1));
}

(ReferredType, TokenList) _parseTypeExpressionUntilArrow(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems hacky. Ideally types should be self delimiting. That is, I'd expect parseType to already stop when it's done parsing the type. What happens if you just try to use parseType?

) {
if (fragments.isEmpty) return (null, fragments);

final (isAsync, isThrowing, afterEffects) = _parseClosureModifiers(fragments);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole chunk violates the parslet design. Why do you need it?

shouldWrapPrimitives: originalFunction.throws,
);

final (wrapperResult, type) = switch ((
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't all this logic live in maybeWrapValue?

);

final (unwrappedParamValue, unwrappedType) = maybeUnwrapValue(
final (unwrappedParamValue, unwrappedType) = switch ((
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't all this logic live in maybeUnwrapValue?

@@ -0,0 +1,21 @@
import Foundation

public func applyGlobal(callback: (Int) -> Int, value: Int) -> Int {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs way more test cases. You're only testing (Int) -> Int. Also add tests for:

  • Zero args
  • Multiple args
  • Tuple returns
  • Void returns
  • Non-trivial arg and return types (ie types that themselves need to be transformed)
  • nested closures (ie closures that take closures as args, and return closures)
  • escaping
  • sendable
  • async
  • throwing
  • inout params
  • labeled params, (_ abc: String) -> Int

If any of those are going to be a lot of work to support, still write the test, but comment it out with a TODO. I'll file a bug for any such missing features, and we'll get to it later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[swift2objc] Support closures

2 participants