Skip to content

Add some diagnostics related to lifetime dependence and function types #74956

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 3 commits into from
Jul 10, 2024
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
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -7994,6 +7994,9 @@ ERROR(lifetime_dependence_cannot_infer_ambiguous_candidate, none,
())
ERROR(lifetime_dependence_immortal_conflict_name, none,
"conflict between the parameter name and immortal keyword", ())
ERROR(lifetime_dependence_function_type, none,
"lifetime dependencies on function types are not supported",
())

//===----------------------------------------------------------------------===//
// MARK: Sending
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3248,6 +3248,10 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
if (!matchFunctionIsolations(func1, func2, kind, flags, locator))
return getTypeMatchFailure(locator);

if (func1->getLifetimeDependenceInfo() != func2->getLifetimeDependenceInfo()) {
return getTypeMatchFailure(locator);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this works because the solver would use a "catch all" conversion fix for arguments but maybe we should add a tailored fix for this to indicate what really mismatched here instead of making developers compare types visually and guess?

Copy link
Contributor Author

@meg-gupta meg-gupta Jul 10, 2024

Choose a reason for hiding this comment

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

Are you suggesting to use a ContextualFailure? I didn't see precedent in CSSimplify where a ContextualFailure was used without attempting a ConstraintFix. Most type mismatches without ConstraintFix were flagged with the generic getTypeMatchFailure .
If it is okay to use a ContextualFailure here, I can do that as a follow on PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't really want to implement a ConstraintFix here. It can cause cascaded SIL diagnostics later on and maybe even more confusing to the user.

}

// To contextual type increase the score to avoid ambiguity when solver can
// find more than one viable binding different only in representation e.g.
// let _: (@convention(block) () -> Void)? = Bool.random() ? nil : {}
Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3961,6 +3961,12 @@ NeverNullType TypeResolver::resolveASTFunctionType(
}
}

if (auto *lifetimeRepr = dyn_cast_or_null<LifetimeDependentReturnTypeRepr>(
repr->getResultTypeRepr())) {
diagnoseInvalid(lifetimeRepr, lifetimeRepr->getLoc(),
diag::lifetime_dependence_function_type);
}

auto resultOptions = options.withoutContext();
resultOptions.setContext(TypeResolverContext::FunctionResult);
auto outputTy = resolveType(repr->getResultTypeRepr(), resultOptions);
Expand Down
47 changes: 47 additions & 0 deletions test/Sema/lifetime_dependence_functype.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NonescapableTypes
// REQUIRES: asserts

struct NC: ~Copyable {
var ne: NE {
NE()
}
}

struct NE: ~Escapable {
@_unsafeNonescapableResult
init() {}
}

func transfer(_ ne: NE) -> NE {
ne
}

func applyAnnotatedTransfer(ne: NE, transfer: (NE) -> dependsOn(0) NE) -> NE { // expected-error{{lifetime dependencies on function types are not supported}}
transfer(ne)
}

func applyTransfer(ne: NE, transfer: (NE) -> NE) -> NE {
transfer(ne)
}

func testTransfer(nc: consuming NC) {
let transferred = applyTransfer(ne: nc.ne, transfer: transfer) // expected-error{{cannot convert value of type '(NE) -> _inherit(0) NE' to expected argument type '(NE) -> NE'}}

_ = consume nc
_ = transfer(transferred)
}

func borrow(_ nc: borrowing NC) -> NE {
nc.ne
}

func applyBorrow(nc: borrowing NC, borrow: (borrowing NC) -> NE) -> NE {
borrow(nc)
}

func testBorrow(nc: consuming NC) {
let borrowed = applyBorrow(nc: nc, borrow: borrow) // expected-error{{cannot convert value of type '(borrowing NC) -> _scope(0) NE' to expected argument type '(borrowing NC) -> NE}}
_ = consume nc
_ = transfer(borrowed)
}