Skip to content
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

[SR-2616] 'lazy' keyword results in "Type of expression is ambiguous without more context" #45221

Closed
swift-ci opened this issue Sep 12, 2016 · 8 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-2616
Radar rdar://problem/28313602
Original Reporter rmann (JIRA User)
Type Bug
Status Resolved
Resolution Done
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee @slavapestov
Priority Medium

md5: d87c612724b08f8b9285ef4e117bd00d

is duplicated by:

  • SR-3561 can't use initializer with default argument to define a lazy var
  • SR-4023 Type checker can't figure out constructor with default parameter in lazy property

relates to:

  • SR-2329 self unavailable in closures to initialize lazy properties unless type specified

Issue Description:

The following code results in "Type of expression is ambiguous without more context" when the lazy keyword is used. Without 'lazy', it compiles fine. Xcode 8 GM seed, Swift 3

class
myClass
{
  lazy var myQ = DispatchQueue(label: "Op", attributes: .concurrent)
                 ^Type of expression is ambiguous without more context
}

Jacob Bandes-Storch pointed this out on the list:

This problem seems to occur when using an initializer with some default argument values. The following code also exhibits the issue:

struct Bar {
  init(x: Int, y: Int? = 42) { }
}

class Foo {
  lazy var myQ = Bar(x: 3)
}
@belkadan
Copy link
Contributor

Mysterious. @rudkx, any ideas?

@rudkx
Copy link
Member

rudkx commented Sep 15, 2016

The diagnostic is lousy but this is an unanticipated type checking failure which is why.

The type checker builds a slightly different constraint system in the lazy case and we fail on it. It's going to take some debugging to understand why.

@slavapestov
Copy link
Member

I've debugging this before, because it is the root cause of some compiler_crashers.

The problem is that if the lazy property does not declare a type, we type check it twice, once to infer the type, and the second time as part of the body of the getter method. The second time often fails.

We need to stop synthesizing a getter body in Sema for lazy properties altogether, and emit the getter entirely in SILGen instead. We already have the ability to do that for global variables, and lazy properties are not much different.

@jtbandes
Copy link
Collaborator

Another easy way of seeing this issue (or a similar issue?) is:

class Foo {
    let bar = 42
    lazy var baz = { self.bar }()
}

The above does not compile: error: use of unresolved identifier 'self'. However, changing lazy var baz to lazy var baz: Int, it suddenly works just fine.

@jtbandes
Copy link
Collaborator

Just curious...

We need to stop synthesizing a getter body in Sema for lazy properties altogether, and emit the getter entirely in SILGen instead.

The body still needs to be typechecked at some point. What goes wrong when typechecking as part of a getter method / how would moving it to SILGen avoid this? (I'm curious but I don't know where this currently happens for global variables. Are you talking about something in here? https://github.com/apple/swift/blob/99de1fd10b13cbfd9b2f89d308c64cfe8c99e280/lib/SIL/SILGlobalVariable.cpp#L72)

@slavapestov
Copy link
Member

> The body still needs to be typechecked at some point.

Right. Sema will type check the initializer expression, and either use that to set the VarDecl's type, or validate it against the VarDecl's type.

> What goes wrong when typechecking as part of a getter method

The problem is the double typechecking of the same Expr. Sema doesn't really support this correctly, even though CSDiag pretends.

> how would moving it to SILGen avoid this?

A lazy getter is basically

if hasValue {
  return _value
}
_value = <initializer expression>
return _value

Instead of synthesizing an AST containing a possibly-typechecked initializer expression in the middle, we would have SILGen emit the scaffolding directly.

Global variables don't have an AST for the getter body – SILGen emits the getter directly. Otherwise, they would have the same problem.

@slavapestov
Copy link
Member

The 'static initializer' thing for SILGlobalVariable is something different. This is where the initializer expression produces something sufficiently simple (eg, an integer literal or something) that instead of executing code to construct the value, we can just embed it directly in the program binary.

@slavapestov
Copy link
Member

#9916

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself
Projects
None yet
Development

No branches or pull requests

5 participants