-
Notifications
You must be signed in to change notification settings - Fork 653
Add this expandos #860
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
Add this expandos #860
Conversation
Doesn't do control flow inference or respect type tags yet. Plus there are couple of known bugs.
Just like the other places with no type annotation in Typescript. I may eventually add a Type property to these, but only if the results are unacceptable. For getting started they are fine.
Note that the baselines *still* show a difference for some auto-typed results--Strada says `this.p: any` while Corsa says `this.p: string`, even though both behave as that it's a string when used. I debugged a test to convince myself that indeed both Strada and Corsa believe the example property is a string, though.
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.
Pull Request Overview
This PR introduces support for this
expandos (class-only this.property
assignments) by adding a unified classification for JS assignment declarations, updating the parser reparser, binder, and checker, and refreshing test baselines to reflect the new behavior.
- Added
JSDeclarationKind
andGetAssignmentDeclarationKind
inutilities.go
to classify assignment patterns. - Implemented
bindThisPropertyAssignment
in the binder and enriched the checker with expando tracking and widened-type logic. - Refactored the parser reparser to use the new declaration kind and updated numerous baselines for symbols, types, and errors.
Reviewed Changes
Copilot reviewed 521 out of 521 changed files in this pull request and generated no comments.
Show a summary per file
File | Description |
---|---|
testdata/baselines/reference/submodule/compiler/argumentsReferenceInConstructor3_Js.symbols.diff | Update symbols diff for this.foo and this.bar expandos. |
testdata/baselines/reference/submodule/compiler/argumentsReferenceInConstructor3_Js.symbols | Refresh symbols output for constructor 3 case. |
testdata/baselines/reference/submodule/compiler/argumentsReferenceInConstructor3_Js.errors.txt | Adjust error count after this-property assignment support. |
testdata/baselines/reference/submodule/compiler/argumentsReferenceInConstructor2_Js.types | Update types for this["arguments"] expando. |
testdata/baselines/reference/submodule/compiler/argumentsReferenceInConstructor2_Js.symbols.diff | Refresh symbols diff for bracket-access expandos. |
testdata/baselines/reference/submodule/compiler/argumentsReferenceInConstructor2_Js.symbols | Update symbols for constructor 2 expandos. |
testdata/baselines/reference/submodule/compiler/argumentsReferenceInConstructor1_Js.types | Update types for this.arguments expando. |
testdata/baselines/reference/submodule/compiler/argumentsReferenceInConstructor1_Js.symbols.diff | Refresh symbols diff for constructor 1 expandos. |
testdata/baselines/reference/submodule/compiler/argumentsReferenceInConstructor1_Js.symbols | Update symbols for constructor 1 expandos. |
testdata/baselines/reference/submodule/compiler/argumentsReferenceInConstructor1_Js.errors.txt | Remove now-obsolete error on this.arguments . |
testdata/baselines/reference/submodule/compiler/ambientPropertyDeclarationInJs.symbols.diff | Adjust baseline for ambient this.prop declarations. |
testdata/baselines/reference/submodule/compiler/ambientPropertyDeclarationInJs.symbols | Refresh ambient property symbols with dual declarations. |
testdata/baselines/reference/submodule/compiler/allowJscheckJsTypeParameterNoCrash.types | Update types baseline for method data2 . |
testdata/baselines/reference/submodule/compiler/allowJscheckJsTypeParameterNoCrash.symbols.diff | Refresh symbols diff for data2 in vextend . |
testdata/baselines/reference/submodule/compiler/allowJscheckJsTypeParameterNoCrash.symbols | Update symbol locations for data2 declarations. |
internal/parser/reparser.go | Remove old jsDeclarationKind , use GetAssignmentDeclarationKind . |
internal/checker/types.go | Added isConstructorDeclaredProperty to TypeAliasLinks . |
internal/checker/checker.go | Extended checker with expando-kind caching and widened-type logic. |
internal/binder/binder.go | Introduced bindThisPropertyAssignment for this.property . |
internal/ast/utilities.go | Added JSDeclarationKind enum and GetAssignmentDeclarationKind . |
Comments suppressed due to low confidence (3)
internal/parser/reparser.go:5
- The import of
core
is unused in this file after removinggetAssignmentDeclarationKind
. Consider removing it to keep imports tidy.
"github.com/microsoft/typescript-go/internal/core"
internal/checker/types.go:158
- The
isConstructorDeclaredProperty
field inTypeAliasLinks
is never referenced. Either remove it or implement its intended use to avoid dead code.
isConstructorDeclaredProperty bool
internal/binder/binder.go:1058
- The handler for
KindFunctionDeclaration
/KindFunctionExpression
is left empty (commented// !!! constructor functions
). This likely skips binding for constructor functions. Add proper handling or clarify why it's omitted.
case ast.KindFunctionDeclaration, ast.KindFunctionExpression:
Hopefully this isn't very expensive, because BinaryExpression is one of the top nodes we allocate. |
This seems odd; I guess it's a problem in our type printer that it doesn't back out on auto types? |
I guess we'll find out, but also that's one reason why I'm going to send it as a separate PR. Edit: It bumps the size from 88 to 96, and hovering over Later edit: Oh, of course, 64 bits=8 bytes. Pointers are just huge these days. |
An alternate idea: have the reparser emit JSExpando nodes for assignments. Then BinaryExpression doesn't have to be a declaration either. The problem with this is that, while The more I talk about it, the more I like this idea. It lets me delete a lot of BinaryExpression special-casing in the checker too. |
After some noodling, I, uh, just asked o4-mini to do the math for me. Since the 16 extra bits for the current approach are 1/5 the size of the trimmed down BinaryExpression and JSExpando, as long as assignments are less than 1/5 of BinaryExpressions, the dupe approach is cheaper. Here's the entire conversation: sandersnIf I have a BinaryExpression with size=80, I have to emit a duplicate JSExpando, also size=80, whenever the BinaryExpression is an assignment. Alternatively, I could make all BinaryExpressions have size=96, with no duplicates. What's the ratio of assignments that would make the duplicate JSExpando approach cheaper? GitHub CopilotThe math is pretty straightforward. Let α = fraction of Binaries that are assignments. • With the “duplicate‐JSExpando” approach everyone stays at 80 bytes, but assignments pay +80: • With the 96 byte “blow‐up‐everyone” approach you pay 96 always. You want So if fewer than 20% of your BinaryExpressions are assignments, the duplicate‐JSExpando (80+80) approach is smaller on average. At ≥20% assignments it’s cheaper to just make all of them 96 bytes. |
I got most of the way through implementing this and realised that ExportAssignment is quite hacky to add a type annotation to; normally we resolve all the way to the target of the alias for type checking, skipping the annotation entirely. Since there's only one place so far that I have to check for a cast-as-proxy-for-annotation, I think a surface-level hack in a JS-related location is better than an obscure hack in a common code path. |
internal/binder/binder.go
Outdated
} | ||
case ast.KindSourceFile, ast.KindModuleDeclaration: | ||
// top-level this.property as assignment to globals is no longer supported | ||
break |
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.
This break isn't needed.
This PR adds this-property assignment declarations, aka
this
expandos. They currently only work in classes -- I haven't added any constructor function support yet, and Corsa no longer allows assignment to globalthis
at the toplevel of a script.The implementation rewrites a significant chunk of getWidenedTypeForAssignmentDeclaration. It should be easier to read and do less duplicative work -- but it's not perfect. And I know that I'll likely have to add handling for object-literal and class expandos later.
Because this change adds control-flow typing of this-property assignments in JS classes, it exposes the fact that Corsa's baselines always write auto types as their calculated type. Strada writes them as
any
most (all?) of the time.I again faked type annotations on binary expressions as a cast. This works, but nowhere near everywhere it's supposed to. I'm going to send a separate PR to add a Type field to all the nodes that JS would like to reparse type annotations onto from JSDoc (BinaryExpression, PropertyAssignment, ExportAssignment). I'll have to change getWidenedTypeForAssignmentDeclaration's check for type annotations on BinaryExpressions when that PR goes in.
Edit: I tried this and found that it involves at least as much hacky code to add a Type field, and is likely slower because it grows the size of BinaryExpression.