-
Notifications
You must be signed in to change notification settings - Fork 424
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
nil-checking and conditionals #13639
Comments
In a separate (off-line) discussion, @bradcray indicated a preference for something along the lines of 1a/1b. |
Note that in 1b, I would not expect to allow a if const ref c = current {
current = nil;
fnExpectingNotNil(c); // error that isn't checked at runtime or compile-time!
} |
I kinda like 1b, and kinda don't. I like that it tries to compose existing concepts (introduction of a new symbol via
What about a |
Actually I don't think this kind of code has much use outside of checking for not-
a |
That's a good point. I think you're arguably saying that the patterns I might write in C using the feature are more or less what we're trying to support here anyway (?). I think this makes me tend to like 1b pretty well. 1a's of course not bad either depending on how we feel about |
Yes, that's what I'm saying. Or, at least that's the case in the compiler, where we write things like
I'm not personally that in to |
I'd like to mention an example from our Futures module: inline proc isValid(): bool {
return ((classRef != nil) && classRef.valid);
} where |
I propose to go forward with 1a and/or 1b: if [let|const|var] c = EXPR {
// 'c' is non-nilable here
// the 'var' form allows assigning non-nilable values to 'c'
}
// allow this in while-loops, too
while [let|const|var] c = EXPR {
.........
} I do not have a preference for which of My preference for this form is based on its robustness and clarity:
if myC.someField != nil {
myFunction(); // did myFunction set someField on the myC instance?
myC2.someField = nil; // does myC == myC2 or not?
myC.someField.someMethod(); // should the compiler allow this?
}
proc myFunction() {
myArray[idx].someField = nil;
}
One example where this form shines is in linked-list processing: var current = mylist.head;
while const c = current {
.......
current = c.next; // this would be hard to make legal under 2a/b
} |
|
I talked to some users who also preferred the 1a/1b options over the 2a/2b options (without any strong preference between the options). |
Implements Strategy (1b) in chapel-lang#13639. Allows the following syntax: ```chpl if const X = someExpression() { // must be a class .... // X is guaranteed to be non-nil // X is a borrow, except it is unmanaged if someExpression() is } ``` as well as the following variants: * `var X` instead of `const X` makes X modifiable within the then-block * `then` keyword instead of '{}` and else-clause are allowed as usual TODOs * [ ] standard paratest * [ ] gasnet paratest * [ ] add tests for correct and incorrect usage * [ ] add parser-generated files Signed-off-by: Vassily Litvinov <vasslitvinov@users.noreply.github.com>
Implements Strategy (1b) in chapel-lang#13639. Allows the following syntax: ```chpl if const X = someExpression() { // must be a class .... // X is guaranteed to be non-nil // X is a borrow, except it is unmanaged if someExpression() is } ``` as well as the following variants: * `var X` instead of `const X` makes X modifiable within the then-block * `then` keyword instead of '{}` and else-clause are allowed as usual TODOs * [ ] standard paratest * [ ] gasnet paratest * [ ] add tests for correct and incorrect usage * [ ] add parser-generated files Signed-off-by: Vassily Litvinov <vasslitvinov@users.noreply.github.com>
Enable the "if-var" construct for conditionals Implements Strategy (1b) in #13639. Allows the following syntax: ```chpl if const X = someExpression() { // must be a class .... // X is guaranteed to be non-nil // X is a borrow, except it is unmanaged if someExpression() is } ``` as well as the following variants: * `var X` instead of `const X` makes X modifiable within the then-block * `then` keyword instead of `{}` and else-clauses are allowed as usual Initially this PR contained applications of this construct in a couple of places in the modules. I took them out for now and will add them back in later by reverting 391973e r: @mppf
An interesting aspect of 1a/1b is what memory management the let-variable has. In this code: if const c = EXPR {
// then-block
} we can give However, if EXPR is PR #17047, which implements (1b), makes |
This matches where my thinking went upon reading about the |
Belatedly, I'm realizing that the behavior you've proposed and implemented matches what we'd do for normal variable initializations from de-nil'ed class expressions, since: class C {
}
var myO = new owned C?();
var myS = new shared C?();
var myB = new borrowed C?();
var myU = new unmanaged C?();
writeln(myO.type:string);
writeln(myS.type:string);
writeln(myB.type:string);
writeln(myU.type:string);
const myO2 = myO!;
const myS2 = myS!;
const myB2 = myB!;
const myU2 = myU!;
writeln(myO2.type:string);
writeln(myS2.type:string);
writeln(myB2.type:string);
writeln(myU2.type:string); results in: owned C?
shared C?
borrowed C?
unmanaged C?
borrowed C
borrowed C
borrowed C
unmanaged C This makes me even more confident that your implementation is correct. Tagging @mppf for his experience and expertise with using and implementing nilable classes. |
@bradcray indeed we should look at why we made a special case for unmanaged for My initial implementation had everything be a borrow uniformly. Alas, then I could not make the changes that I temporarily reverted in 391973e. Here is the issue: We use proc _domain.add_arr(x:unmanaged BaseArr) {
if const ahead = _arrs_head {
x.next = ahead; // x.next is unmanaged, cannot assign a borrow into it
ahead.prev = x;
}
_arrs_head = x;
} |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
To support the case where the programmer wants to retain the memory management being tested, I propose a variant on the let-var construct that specifies the type explicitly, for example: if var myNonNilable: owned = someExpr() {
// myNonNilable took over the ownership of the result of someExpr()
} |
I was going to propose the same, but had been waiting until we had a compelling use case. |
This follows the suit of chapel-lang#17047 and the design (1b) in chapel-lang#13639. It reuses some code added in chapel-lang#17047. Instead of lowering the while-var construct in normalize, as chapel-lang#17047 did for if-var, the lowering is done during parsing. This is because for a while-loop the generated AST contains TWO copies of the conditional, rather than one. Signed-off-by: Vassily Litvinov <vasslitvinov@users.noreply.github.com>
This follows the suit of chapel-lang#17047 and the design (1b) in chapel-lang#13639. This reuses some code added in chapel-lang#17047. Instead of lowering the while-var construct in normalize, as chapel-lang#17047 did for if-var, the lowering is done during parsing. This is because for a while-loop the generated AST contains TWO copies of the conditional, rather than one. The ASTs for while-loops that do not use the while-var construct are unaffected by this change. Signed-off-by: Vassily Litvinov <vasslitvinov@users.noreply.github.com>
Enable the "while-var" construct for while-loops This follows the suit of #17047 and the design (1b) in #13639. This reuses some code added in #17047. Instead of lowering the while-var construct in normalize, as #17047 did for if-var, the lowering is done during parsing. This is because for a while-loop the generated AST contains TWO copies of the conditional, rather than one. The ASTs for while-loops that do not use the while-var construct are unaffected by this change. r: @lydia-duncan
On the question of
if let
vs. inferring non-nilness based upon conditionals, we have discussed this a bit already #12614 (comment) #12614 (comment). In this issue I'd like to focus more on that question specifically.Here is the example we will discuss, starting with :
I know of 4 strategies for improvement within this kind of conditional pattern:
1a. If-let, following Swift
E.g.
Pros:
c
vscurrent
c
vscurrent
Cons:
let
syntax was on the way out in Chapel...1b. If-var variant (from #12614 (comment))
E.g.
Pros:
c
vscurrent
c
vscurrent
Cons:
2a. inference-based with shadow variable
Pros:
Cons:
c
vscurrent
c
vscurrent
2b. inference-based with allowed coercions
Pros:
Cons:
current.type
somewhere will give MyClass? but we know it's not nil hereThe text was updated successfully, but these errors were encountered: