Skip to content

Perform function conversions when converting between autoclosures #17339

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6865,10 +6865,18 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
isInDefaultArgumentContext = (initalizerCtx->getInitializerKind() ==
InitializerKind::DefaultArgument);
auto toEI = toFunc->getExtInfo();

auto fromFunc = fromType->getAs<FunctionType>();

// Coercion to an autoclosure type produces an implicit closure.
// The constraint solver only performs this conversion when the source
// type is not an autoclosure function type. That's a weird rule in
// some rules, but it's easy to follow here. Really we just shouldn't
// represent autoclosures as a bit on function types.
// FIXME: The type checker is more lenient, and allows @autoclosures to
// be subtypes of non-@autoclosures, which is bogus.
if (toFunc->isAutoClosure()) {
if (toFunc->isAutoClosure() &&
(!fromFunc || !fromFunc->isAutoClosure())) {
// The function type without @noescape if we are in the default argument
// context.
auto newToFuncType = toFunc;
Expand Down Expand Up @@ -6909,7 +6917,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,

// Coercion from one function type to another, this produces a
// FunctionConversionExpr in its full generality.
if (auto fromFunc = fromType->getAs<FunctionType>()) {
if (fromFunc) {
// If we have a ClosureExpr, then we can safely propagate the 'no escape'
// bit to the closure without invalidating prior analysis.
auto fromEI = fromFunc->getExtInfo();
Expand Down
14 changes: 14 additions & 0 deletions test/attr/attr_autoclosure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func migrateAC(@autoclosure _: () -> ()) { }
func migrateACE(@autoclosure(escaping) _: () -> ()) { }

func takesAutoclosure(_ fn: @autoclosure () -> Int) {}
func takesThrowingAutoclosure(_: @autoclosure () throws -> Int) {}

func callAutoclosureWithNoEscape(_ fn: () -> Int) {
takesAutoclosure(1+1) // ok
Expand All @@ -161,3 +162,16 @@ func callAutoclosureWithNoEscape_3(_ fn: @autoclosure () -> Int) {
func variadicAutoclosure(_ fn: @autoclosure () -> ()...) {
for _ in fn {}
}

// rdar://41219750
// These are all arguably invalid; the autoclosure should have to be called.
// But as long as we allow them, we shouldn't crash.
func passNonThrowingToNonThrowingAC(_ fn: @autoclosure () -> Int) {
takesAutoclosure(fn)
}
func passNonThrowingToThrowingAC(_ fn: @autoclosure () -> Int) {
takesThrowingAutoclosure(fn)
}
func passThrowingToThrowingAC(_ fn: @autoclosure () throws -> Int) {
takesThrowingAutoclosure(fn)
}