-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
Prevent assigning class name to variable. #13404
Conversation
396474f
to
03638bd
Compare
Add additional test, it does: |
Transitioning this to |
This is just a quick general comment for the moment, not addressing the specifics of the PR. It seems to me, from the existing implementation, that classes were intended to to be first-class objects. This is in keeping with the language in general.
|
Appreciated. Main question is whether or not
Interesting point, that statement is based on Bram's comment "It's not an object and we don't have a type that is a class" in #12006 (comment); Bram closed that issue with "Once something like a typedef has been implemented please check this again. Closing for now." Maybe the distinction is that it's not a type because you can't do
although internally there is a vim object of type VAR_CLASS. And you can do
But there are several breadcrumbs about a class type. |
I am actually assigning classes to a const variables in my code. This is my use case:
As you mention, that might be:
but that is not implemented yet, afaik. If the class assignment is removed, one is forced to use imported classes with a prefix, I think. Or is there another way? I personally find it very convenient to be able to assign non-prefixed aliases to imported names; it makes my code much easier to read and write. |
@errael Can you elaborate on your proposed workaround? I have tried this: vim9script
# lib.vim
export class Context
this.text: string
public this.index: number = 0
static def MakeContext(text: string, index = 0): any
return Context.new(text, index)
enddef
def new(this.text, this.index = v:none)
enddef
endclass vim9script
import './lib.vim'
const MakeContext = lib.Context.MakeContext
def Test_MakeContext()
var ctx = MakeContext("X", 42)
assert_equal(v:t_object, type(ctx))
assert_equal("X", ctx.text) # <== E715
assert_equal(42, ctx.index) # <== E715
enddef
Test_MakeContext() Sourcing the latter script results in |
Ok, I can make it work like this: vim9script
# lib.vim
export class Context
this.text: string
public this.index: number = 0
def new(this.text, this.index = v:none)
enddef
endclass
export def NewContext(text: string, index = 0): Context
return Context.new(text, index)
enddef vim9script
import './lib.vim'
const NewContext = lib.NewContext
def Test_MakeContext()
var ctx = NewContext("X", 42)
assert_equal(v:t_object, type(ctx))
assert_equal("X", ctx.text)
assert_equal(42, ctx.index)
enddef
Test_MakeContext() It is still the case, though, that if I want to define a function that takes a
A bit cumbersome, but maybe not too bad, after all. Please consider the ergonomics of removing the assignment without providing a replacement. It feels a bit unnatural to me that I can assign anything but classes. |
Oh crap, how could I forget this important case, which I use all the time with functions. Without |
Right,
If
You're right, I don't think that's acceptable. But maybe
But it's beginning to look like |
I see. Clearly, something like
It's the proper way to do it, imo. If you are going to implement |
Cool. I'll put it back to "Draft PR", and reopen when |
Probably not now that I think you've missed assignments to parameters. vim9script
class A
endclass
def Foo(c: any)
echomsg typename(c)
enddef
Foo(A)
I can't understand the comment there, given that the help entry was added only a month earlier.
Indeed. I wouldn't like to see it sent out into the world in its current state even with assignment disabled without some sort of reasonable narrative. It looks a bit like class/singleton-metaclass pairs. I suppose something like that could fall out of the implementation but |
Ah, of course. Thanks. Need a function like
I found @lifepillar's distinction between
which outputs |
Implementation note: From what I've seen there's a dictionary of accessible names.
Maybe pre vim9class, tv was always a value. Now it can also be a type. |
src/evalvars.c
Outdated
@@ -1158,7 +1158,9 @@ ex_let(exarg_T *eap) | |||
// declaration, not the expression. | |||
SOURCING_LNUM = cur_lnum; | |||
|
|||
if (!eap->skip && eval_res != FAIL) | |||
if (!eap->skip && eval_res != FAIL && rettv.v_type == VAR_CLASS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use list assignment to assign multiple variables (B is a class in the statement below):
[a, b] = [10, B]
To handle this case, this check should be moved to the ex_let_one() function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To handle this case, this check should be moved to the ex_let_one() function
Thanks, it was still on my list.
5038c9e
to
4f7b273
Compare
Pushed a tweak to allow non-value items in |
37255b1
to
c306ad6
Compare
This may not be needed. The builtins check their args. This commit adds uniformity to checking.
efcf8cd
to
16d88af
Compare
@lifepillar With
There are still things that are a hassle or unexpected. Which make this a workaround.
|
16d88af
to
2905c4b
Compare
Rebase with |
Why was this PR closed? Is this not needed anymore? |
I closed it in favor of yegappan#2 which is identical to this PR. |
I have updated PR #13441 to address this issue. |
Thanks. Glad I didn't merge 5 minutes earlier :) |
@lifepillar @yegappan @dkearns please review.
Code reviewers, please consider where the errors are generated. Is there a better place to check? Are all the cases caught?
Error is
E1393: Cannot use a class as a variable or value
notes on changes
Prevent assignment
class
ortypealias
.dict
/list
:var l = [1, C]
,var d = {x: C}
.NOTE: currently non-value items can be added to a list (while #13421 is being sorted out)
NOTE: a builtin must not return a non-value:, bare, in
list
, indict
Here's an overview of the code changes made (so far).
There is a separate commit for
instanceof()
experimentation.There is a separate commit for uniform builtin argument checking.
TODO
Fixed tests as needed, but have around a dozen or so tests to write based on debug scripts. Plan to add tests after initial review (particularly of the error message).
What now?
This PR is very conservative. Some of the checks could be backed out.
The advantage of aggressive checks is uniformity, which should be less
confusing.
This would ease some problems with builtins like
instanceof()
This still wouldn't allow the use of the non-value.
Need to prevent non-value from being the result of an expression;
protects against usage like aList[0].new().
Notes on why the change
With this PR,
Test_call_constructor_from_legacy()
failed, the test has beenreplaced with
Test_construct_object_from_legacy()
which shows an alternateway to do the same thing.
There's the following TODO item:
This PR "give[s] an error now".
The important question: does preventing this take away an important capability?
When I first saw this TODO, I wondered why not just let it be, and take care
of it later. I played with it, #12961, and noted that it was very limited with
common stuff not working. I think I didn't read the original issue, #12006,
carefully; just reading it now, Bram's argument is
If it's left in, it leaves a lot of technical debt to get it fully working.
Some uses for this, can be achieved with something like