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

Fix fallthrough and default in new switch-true narrowing #55991

Merged
merged 7 commits into from
Oct 6, 2023

Conversation

jakebailey
Copy link
Member

@jakebailey jakebailey commented Oct 5, 2023

Fixes #55986

This fixes narrowing in fallthrough/default cases, narrowing the type properly against assumeTrue for the other cases. It now behaves like if statements do.

(We could totally also do switch (false) but that seems like a bad move.)

@typescript-bot typescript-bot added Author: Team For Milestone Bug PRs that fix a bug with a specific milestone labels Oct 5, 2023
@jakebailey
Copy link
Member Author

@typescript-bot test top200
@typescript-bot user test this
@typescript-bot perf test this
@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 5, 2023

Heya @jakebailey, I've started to run the diff-based user code test suite on this PR at 002973b. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 5, 2023

Heya @jakebailey, I've started to run the tarball bundle task on this PR at 002973b. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 5, 2023

Heya @jakebailey, I've started to run the regular perf test suite on this PR at 002973b. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 5, 2023

Heya @jakebailey, I've started to run the diff-based top-repos suite on this PR at 002973b. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 5, 2023

Hey @jakebailey, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/158091/artifacts?artifactName=tgz&fileId=665F4D484600BC510B8206632820E7752DB60A86FCEEF5C0FB3243633F6C5BC202&fileName=/typescript-5.3.0-insiders.20231005.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@5.3.0-pr-55991-5".;

@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the user test suite comparing main and refs/pull/55991/merge:

There were infrastructure failures potentially unrelated to your change:

  • 3 instances of "Package install failed"
  • 1 instance of "Unknown failure"

Otherwise...

Everything looks good!

@typescript-bot
Copy link
Collaborator

@jakebailey
The results of the perf run you requested are in!

Here they are:

Compiler

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Angular - node (v18.15.0, x64)
Memory used 295,081k (± 0.02%) 295,067k (± 0.01%) ~ 295,012k 295,134k p=0.471 n=6
Parse Time 2.63s (± 0.37%) 2.63s (± 0.65%) ~ 2.60s 2.65s p=1.000 n=6
Bind Time 0.84s (± 1.39%) 0.84s (± 1.23%) ~ 0.83s 0.85s p=0.799 n=6
Check Time 8.06s (± 0.24%) 8.06s (± 0.34%) ~ 8.04s 8.10s p=1.000 n=6
Emit Time 7.03s (± 0.41%) 7.05s (± 0.29%) ~ 7.01s 7.07s p=0.373 n=6
Total Time 18.57s (± 0.29%) 18.58s (± 0.17%) ~ 18.54s 18.63s p=0.687 n=6
Compiler-Unions - node (v18.15.0, x64)
Memory used 191,629k (± 1.22%) 193,560k (± 1.68%) ~ 190,577k 196,652k p=1.000 n=6
Parse Time 1.35s (± 0.56%) 1.34s (± 0.61%) ~ 1.33s 1.35s p=0.383 n=6
Bind Time 0.73s (± 0.00%) 0.73s (± 0.56%) ~ 0.73s 0.74s p=0.405 n=6
Check Time 9.21s (± 0.54%) 9.10s (± 0.26%) -0.10s (- 1.12%) 9.07s 9.13s p=0.005 n=6
Emit Time 2.64s (± 0.78%) 2.64s (± 0.62%) ~ 2.61s 2.66s p=0.868 n=6
Total Time 13.92s (± 0.51%) 13.81s (± 0.20%) -0.11s (- 0.79%) 13.77s 13.84s p=0.005 n=6
Monaco - node (v18.15.0, x64)
Memory used 347,297k (± 0.01%) 347,278k (± 0.00%) ~ 347,258k 347,294k p=0.297 n=6
Parse Time 2.46s (± 0.36%) 2.46s (± 0.40%) ~ 2.45s 2.48s p=0.931 n=6
Bind Time 0.94s (± 0.43%) 0.94s (± 0.43%) ~ 0.93s 0.94s p=1.000 n=6
Check Time 6.90s (± 0.43%) 6.92s (± 0.38%) ~ 6.90s 6.97s p=0.259 n=6
Emit Time 4.01s (± 0.19%) 4.02s (± 0.31%) ~ 4.00s 4.04s p=0.167 n=6
Total Time 14.30s (± 0.22%) 14.34s (± 0.18%) ~ 14.32s 14.39s p=0.086 n=6
TFS - node (v18.15.0, x64)
Memory used 302,544k (± 0.01%) 302,549k (± 0.01%) ~ 302,523k 302,579k p=0.872 n=6
Parse Time 2.00s (± 1.21%) 2.01s (± 0.63%) ~ 1.99s 2.02s p=0.318 n=6
Bind Time 1.00s (± 1.26%) 1.00s (± 0.75%) ~ 0.99s 1.01s p=0.676 n=6
Check Time 6.27s (± 0.36%) 6.26s (± 0.37%) ~ 6.23s 6.28s p=0.685 n=6
Emit Time 3.57s (± 0.34%) 3.57s (± 0.51%) ~ 3.55s 3.59s p=0.453 n=6
Total Time 12.84s (± 0.26%) 12.84s (± 0.17%) ~ 12.82s 12.88s p=0.747 n=6
material-ui - node (v18.15.0, x64)
Memory used 470,504k (± 0.01%) 470,482k (± 0.00%) ~ 470,447k 470,505k p=0.298 n=6
Parse Time 2.57s (± 0.59%) 2.58s (± 0.48%) ~ 2.56s 2.59s p=0.933 n=6
Bind Time 0.99s (± 0.76%) 0.99s (± 0.52%) ~ 0.99s 1.00s p=0.784 n=6
Check Time 16.59s (± 0.50%) 16.63s (± 0.31%) ~ 16.53s 16.66s p=0.627 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 20.16s (± 0.42%) 20.20s (± 0.28%) ~ 20.09s 20.24s p=0.630 n=6
xstate - node (v18.15.0, x64)
Memory used 512,636k (± 0.01%) 512,562k (± 0.01%) -73k (- 0.01%) 512,513k 512,609k p=0.045 n=6
Parse Time 3.28s (± 0.16%) 3.27s (± 0.41%) ~ 3.25s 3.29s p=0.150 n=6
Bind Time 1.55s (± 0.49%) 1.55s (± 0.26%) ~ 1.55s 1.56s p=0.389 n=6
Check Time 2.88s (± 0.81%) 2.89s (± 0.84%) ~ 2.86s 2.93s p=0.517 n=6
Emit Time 0.08s (± 4.99%) 0.08s (± 4.99%) ~ 0.08s 0.09s p=1.000 n=6
Total Time 7.78s (± 0.34%) 7.78s (± 0.51%) ~ 7.75s 7.86s p=1.000 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Angular - node (v18.15.0, x64)
  • Compiler-Unions - node (v18.15.0, x64)
  • Monaco - node (v18.15.0, x64)
  • TFS - node (v18.15.0, x64)
  • material-ui - node (v18.15.0, x64)
  • xstate - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

tsserver

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-UnionsTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,365ms (± 1.52%) 2,384ms (± 0.94%) ~ 2,355ms 2,411ms p=0.230 n=6
Req 2 - geterr 5,400ms (± 1.87%) 5,356ms (± 1.51%) ~ 5,293ms 5,469ms p=0.748 n=6
Req 3 - references 328ms (± 0.90%) 328ms (± 1.09%) ~ 325ms 335ms p=0.807 n=6
Req 4 - navto 275ms (± 0.74%) 279ms (± 1.48%) ~ 273ms 283ms p=0.102 n=6
Req 5 - completionInfo count 1,356 (± 0.00%) 1,356 (± 0.00%) ~ 1,356 1,356 p=1.000 n=6
Req 5 - completionInfo 86ms (± 9.96%) 79ms (± 8.26%) ~ 74ms 90ms p=0.162 n=6
CompilerTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,469ms (± 1.10%) 2,499ms (± 0.38%) ~ 2,487ms 2,513ms p=0.065 n=6
Req 2 - geterr 4,118ms (± 1.94%) 4,065ms (± 0.31%) ~ 4,049ms 4,081ms p=0.293 n=6
Req 3 - references 339ms (± 1.31%) 343ms (± 0.24%) ~ 342ms 344ms p=0.183 n=6
Req 4 - navto 284ms (± 0.29%) 286ms (± 0.62%) ~ 284ms 289ms p=0.177 n=6
Req 5 - completionInfo count 1,518 (± 0.00%) 1,518 (± 0.00%) ~ 1,518 1,518 p=1.000 n=6
Req 5 - completionInfo 81ms (± 7.72%) 87ms (± 0.59%) 🔻+6ms (+ 7.82%) 87ms 88ms p=0.021 n=6
xstateTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,593ms (± 0.67%) 2,606ms (± 0.33%) ~ 2,600ms 2,622ms p=0.630 n=6
Req 2 - geterr 1,658ms (± 1.14%) 1,706ms (± 2.69%) ~ 1,656ms 1,754ms p=0.066 n=6
Req 3 - references 110ms (± 7.82%) 116ms (± 9.20%) ~ 105ms 128ms p=0.677 n=6
Req 4 - navto 359ms (± 0.14%) 360ms (± 0.52%) ~ 358ms 363ms p=0.078 n=6
Req 5 - completionInfo count 2,071 (± 0.00%) 2,071 (± 0.00%) ~ 2,071 2,071 p=1.000 n=6
Req 5 - completionInfo 305ms (± 0.64%) 306ms (± 2.10%) ~ 295ms 311ms p=0.808 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • CompilerTSServer - node (v18.15.0, x64)
  • Compiler-UnionsTSServer - node (v18.15.0, x64)
  • xstateTSServer - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Startup

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
tsc-startup - node (v18.15.0, x64)
Execution time 152.38ms (± 0.18%) 152.32ms (± 0.18%) -0.06ms (- 0.04%) 151.12ms 154.49ms p=0.037 n=600
tsserver-startup - node (v18.15.0, x64)
Execution time 227.44ms (± 0.17%) 227.43ms (± 0.17%) ~ 226.10ms 233.66ms p=0.579 n=600
tsserverlibrary-startup - node (v18.15.0, x64)
Execution time 229.06ms (± 0.18%) 229.02ms (± 0.18%) ~ 227.40ms 236.86ms p=0.175 n=600
typescript-startup - node (v18.15.0, x64)
Execution time 228.40ms (± 0.16%) 228.29ms (± 0.18%) -0.11ms (- 0.05%) 226.71ms 234.32ms p=0.002 n=600
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • tsc-startup - node (v18.15.0, x64)
  • tsserver-startup - node (v18.15.0, x64)
  • tsserverlibrary-startup - node (v18.15.0, x64)
  • typescript-startup - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

Copy link
Member

@DanielRosenwasser DanielRosenwasser left a comment

Choose a reason for hiding this comment

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

Can we have a test of a default preceding a case?

Additionally, do we have any tests for case clauses narrowing subsequent case clauses?

type Shape =
    | { kind: "circle", radius: number }
    | { kind: "square", sideLength: number }

function wat(shape: Shape) {
    switch (true) {
        case shape.kind === "circle":
            return Math.PI * shape.radius ** 2;
        case shape.kind === "circle": // should error
    }

    if (shape.kind === "circle") {
        return Math.PI * shape.radius ** 2;
    }
    else if (shape.kind === "circle") {
        //         ~~~~
        // Property 'kind' does not exist on type 'never'.
    }
}

src/compiler/checker.ts Show resolved Hide resolved
src/compiler/checker.ts Show resolved Hide resolved
src/compiler/checker.ts Show resolved Hide resolved
src/compiler/checker.ts Show resolved Hide resolved
@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the top-repos suite comparing main and refs/pull/55991/merge:

Everything looks good!

@jakebailey
Copy link
Member Author

@Andarist feel free to tackle this if you know the right answer here

@fatcerberus
Copy link

don't think we have enough CFA to actually determine the negative case, so it just doesn't narrow at all

The more I think about this, the more I think this might be good behavior - at a high level, default is the catch-all case for when nothing else matches, so maybe it's better to encourage writing code there that works for any possible result, even the ones with existing cases. This makes it less fragile, e.g. if a case is added or removed later.1

Footnotes

  1. I suppose one could make the same argument for the else block in if..else if..else chain, but it's been my experience that chains of if..else if chains are typically comprehensive (and therefore want the else to be as narrow as possible), while a switch (true) very often isn't.

@jakebailey
Copy link
Member Author

I feel like I must be wrong and we do have CFA within switch cases, I just didn't look long enough last night to figure it out...

@jakebailey jakebailey marked this pull request as draft October 5, 2023 15:14
@jakebailey
Copy link
Member Author

I am surely wrong, we totally create branch labels for case blocks and everything.

@jakebailey
Copy link
Member Author

Additionally, do we have any tests for case clauses narrowing subsequent case clauses?

I don't think your code would error in the way you expect; e.g. see what we do for typeof now: Playground Link

@jakebailey
Copy link
Member Author

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 5, 2023

Heya @jakebailey, I've started to run the tarball bundle task on this PR at a98c233. You can monitor the build here.

@jakebailey
Copy link
Member Author

I'm still not confident but I'd appreciate people trying the playground from the most recent change and try and break it.

Clearly, this is not my area.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 5, 2023

Hey @jakebailey, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/158098/artifacts?artifactName=tgz&fileId=C8065AA1EA9E126D23B7F7C378E555CD3146D579927812786F817296AFFF710002&fileName=/typescript-5.3.0-insiders.20231005.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@5.3.0-pr-55991-17".;

@jakebailey
Copy link
Member Author

Notably it feels suspicious that I haven't explicitly handled fallthrough, so I bet there's another edge case I'm missing where there needs to be CFA from one case group into another?

Comment on lines 28044 to 28045
// In the default case, we need to assume that all of the other cases were false,
// but also that any of the other cases in this block are true.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// In the default case, we need to assume that all of the other cases were false,
// but also that any of the other cases in this block are true.
// In the default case, it's possible that we came from:
// 1. a set of fallthrough cases, where their conditions might be true
// 2. no case succeeding, in which case all conditions might be false

Copy link
Member

Choose a reason for hiding this comment

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

Actually...rereading it, is it the case that we gain no information from considering the fallthrough cases? Since you have to assume they could be both true and false?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, that's the edge case I'm guessing at in #55991 (comment), I need to write a test that can show that possibility.

Not to mention that you could have a broad case and then narrow within the case and return, leaving only part of the type to fallthough...

Copy link
Member Author

@jakebailey jakebailey Oct 5, 2023

Choose a reason for hiding this comment

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

In thinking harder, I realized that what this function should do is just "what do I know about this thing based on only the case itself?"; you don't need to worry about control flow, fallthrough, etc, here at all, because CFA will combine this info with the other cases later.

So in reality, the bug being fixed is "multiple cases were not handled" and "default case was not handled", along with the combo of the two. I think I need a test for the combo, still, but I think I handled it.

@jakebailey
Copy link
Member Author

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 5, 2023

Heya @jakebailey, I've started to run the tarball bundle task on this PR at 006f169. You can monitor the build here.

@jakebailey
Copy link
Member Author

Okay, another better version, but probably still not there. Please break it...

@jakebailey
Copy link
Member Author

Since it seems like it's working, I've rebased such that the fix and its test changes are all together for easier viewing.

@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the top-repos suite comparing main and refs/pull/55991/merge:

Something interesting changed - please have a look.

Details

jitsi/jitsi-meet

tsconfig.web.json

@jakebailey
Copy link
Member Author

jakebailey commented Oct 5, 2023

The above shows that this PR is still not fully ready: Playground Link

@RyanCavanaugh
Copy link
Member

The effect on this is bizarre:

interface IProps {
    one: boolean;
}

class Foo {
    mine: string = "";

    myMethod(x: IProps) {
        const { one } = x;
        switch (true) {
            case one:
                break;
            default:
                let x = this.mine;
        }
    }
}

@Andarist
Copy link
Contributor

Andarist commented Oct 5, 2023

I'm currently away and just peeking here. The Ryan's case above is broken because:

clausesType // never
hasDefaultClause // true
before // this
after // never
other // this

Since other is this then typeNotOther becomes never and the result is a union of that and the clausesType (so basically never | never). I don't believe this is specific anyhow to this type.

@jakebailey
Copy link
Member Author

I think I figured it out... Will update shortly. I'm sure it'll be wrong in some all new way, though 😄

@jakebailey
Copy link
Member Author

@typescript-bot test top200
@typescript-bot user test this
@typescript-bot perf test this
@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 6, 2023

Heya @jakebailey, I've started to run the tarball bundle task on this PR at 23f25c7. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 6, 2023

Heya @jakebailey, I've started to run the diff-based user code test suite on this PR at 23f25c7. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 6, 2023

Heya @jakebailey, I've started to run the regular perf test suite on this PR at 23f25c7. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 6, 2023

Heya @jakebailey, I've started to run the diff-based top-repos suite on this PR at 23f25c7. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 6, 2023

Hey @jakebailey, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/158115/artifacts?artifactName=tgz&fileId=8E5E1C8F173C716E657486EBC0A50616F75ADF915BE18B2E2122DB430244E82502&fileName=/typescript-5.3.0-insiders.20231006.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@5.3.0-pr-55991-50".;

@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the user test suite comparing main and refs/pull/55991/merge:

There were infrastructure failures potentially unrelated to your change:

  • 3 instances of "Package install failed"
  • 1 instance of "Unknown failure"

Otherwise...

Everything looks good!

@typescript-bot
Copy link
Collaborator

@jakebailey
The results of the perf run you requested are in!

Here they are:

Compiler

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Angular - node (v18.15.0, x64)
Memory used 295,077k (± 0.01%) 295,050k (± 0.01%) ~ 295,006k 295,107k p=0.261 n=6
Parse Time 2.62s (± 1.06%) 2.63s (± 0.44%) ~ 2.62s 2.65s p=0.683 n=6
Bind Time 0.84s (± 1.39%) 0.84s (± 0.65%) ~ 0.84s 0.85s p=0.152 n=6
Check Time 8.06s (± 0.30%) 8.08s (± 0.39%) ~ 8.04s 8.13s p=0.193 n=6
Emit Time 7.03s (± 0.33%) 7.05s (± 0.36%) ~ 7.02s 7.08s p=0.416 n=6
Total Time 18.55s (± 0.26%) 18.61s (± 0.21%) +0.05s (+ 0.29%) 18.57s 18.67s p=0.044 n=6
Compiler-Unions - node (v18.15.0, x64)
Memory used 191,691k (± 1.24%) 191,595k (± 1.24%) ~ 190,581k 196,449k p=0.066 n=6
Parse Time 1.35s (± 0.94%) 1.34s (± 0.87%) ~ 1.32s 1.35s p=0.122 n=6
Bind Time 0.73s (± 0.00%) 0.73s (± 0.00%) ~ 0.73s 0.73s p=1.000 n=6
Check Time 9.20s (± 0.65%) 9.12s (± 0.48%) -0.08s (- 0.83%) 9.08s 9.18s p=0.024 n=6
Emit Time 2.63s (± 0.48%) 2.63s (± 0.87%) ~ 2.60s 2.65s p=1.000 n=6
Total Time 13.91s (± 0.48%) 13.82s (± 0.47%) ~ 13.74s 13.90s p=0.064 n=6
Monaco - node (v18.15.0, x64)
Memory used 347,298k (± 0.00%) 347,307k (± 0.00%) ~ 347,283k 347,328k p=0.377 n=6
Parse Time 2.46s (± 0.57%) 2.45s (± 0.48%) ~ 2.44s 2.47s p=0.325 n=6
Bind Time 0.94s (± 0.43%) 0.94s (± 0.00%) ~ 0.94s 0.94s p=0.405 n=6
Check Time 6.91s (± 0.73%) 6.93s (± 0.58%) ~ 6.88s 6.99s p=0.572 n=6
Emit Time 4.01s (± 0.44%) 4.03s (± 0.70%) ~ 4.00s 4.08s p=0.618 n=6
Total Time 14.33s (± 0.28%) 14.35s (± 0.25%) ~ 14.30s 14.41s p=0.261 n=6
TFS - node (v18.15.0, x64)
Memory used 302,541k (± 0.01%) 302,549k (± 0.01%) ~ 302,530k 302,571k p=1.000 n=6
Parse Time 1.99s (± 0.74%) 2.00s (± 0.94%) ~ 1.98s 2.03s p=0.800 n=6
Bind Time 0.98s (± 4.57%) 1.00s (± 1.03%) ~ 0.99s 1.02s p=0.341 n=6
Check Time 6.25s (± 0.52%) 6.25s (± 0.51%) ~ 6.20s 6.28s p=1.000 n=6
Emit Time 3.57s (± 0.70%) 3.55s (± 0.60%) ~ 3.52s 3.57s p=0.133 n=6
Total Time 12.78s (± 0.37%) 12.80s (± 0.28%) ~ 12.74s 12.83s p=0.686 n=6
material-ui - node (v18.15.0, x64)
Memory used 470,494k (± 0.00%) 470,519k (± 0.02%) ~ 470,449k 470,665k p=0.689 n=6
Parse Time 2.58s (± 0.47%) 2.58s (± 0.71%) ~ 2.56s 2.61s p=0.684 n=6
Bind Time 1.00s (± 0.82%) 1.00s (± 0.82%) ~ 0.99s 1.01s p=1.000 n=6
Check Time 16.65s (± 0.31%) 16.59s (± 0.56%) ~ 16.47s 16.72s p=0.173 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 20.23s (± 0.23%) 20.17s (± 0.55%) ~ 20.01s 20.30s p=0.295 n=6
xstate - node (v18.15.0, x64)
Memory used 512,559k (± 0.01%) 512,575k (± 0.01%) ~ 512,531k 512,647k p=0.936 n=6
Parse Time 3.27s (± 0.30%) 3.26s (± 0.32%) ~ 3.25s 3.28s p=0.437 n=6
Bind Time 1.55s (± 0.49%) 1.55s (± 0.41%) ~ 1.54s 1.56s p=0.718 n=6
Check Time 2.86s (± 0.87%) 2.87s (± 0.71%) ~ 2.85s 2.91s p=0.808 n=6
Emit Time 0.08s (± 0.00%) 0.08s (± 0.00%) ~ 0.08s 0.08s p=1.000 n=6
Total Time 7.76s (± 0.19%) 7.75s (± 0.31%) ~ 7.73s 7.80s p=0.624 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Angular - node (v18.15.0, x64)
  • Compiler-Unions - node (v18.15.0, x64)
  • Monaco - node (v18.15.0, x64)
  • TFS - node (v18.15.0, x64)
  • material-ui - node (v18.15.0, x64)
  • xstate - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

tsserver

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-UnionsTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,383ms (± 1.51%) 2,384ms (± 1.18%) ~ 2,349ms 2,417ms p=1.000 n=6
Req 2 - geterr 5,343ms (± 1.67%) 5,346ms (± 1.77%) ~ 5,252ms 5,473ms p=0.810 n=6
Req 3 - references 330ms (± 1.49%) 329ms (± 1.21%) ~ 323ms 333ms p=1.000 n=6
Req 4 - navto 277ms (± 0.90%) 278ms (± 1.44%) ~ 271ms 280ms p=0.744 n=6
Req 5 - completionInfo count 1,356 (± 0.00%) 1,356 (± 0.00%) ~ 1,356 1,356 p=1.000 n=6
Req 5 - completionInfo 79ms (± 8.27%) 83ms (± 6.14%) ~ 79ms 90ms p=0.147 n=6
CompilerTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,472ms (± 1.37%) 2,472ms (± 1.37%) ~ 2,405ms 2,493ms p=0.630 n=6
Req 2 - geterr 4,137ms (± 1.79%) 4,137ms (± 2.14%) ~ 4,050ms 4,244ms p=0.810 n=6
Req 3 - references 336ms (± 1.01%) 336ms (± 1.10%) ~ 333ms 343ms p=0.682 n=6
Req 4 - navto 283ms (± 0.52%) 284ms (± 0.35%) ~ 283ms 285ms p=0.109 n=6
Req 5 - completionInfo count 1,518 (± 0.00%) 1,518 (± 0.00%) ~ 1,518 1,518 p=1.000 n=6
Req 5 - completionInfo 78ms (± 5.76%) 82ms (± 6.97%) ~ 76ms 87ms p=0.177 n=6
xstateTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,594ms (± 0.65%) 2,603ms (± 0.72%) ~ 2,568ms 2,620ms p=0.261 n=6
Req 2 - geterr 1,725ms (± 2.57%) 1,682ms (± 1.46%) ~ 1,648ms 1,712ms p=0.109 n=6
Req 3 - references 112ms (± 7.73%) 114ms (±10.35%) ~ 105ms 130ms p=0.936 n=6
Req 4 - navto 359ms (± 0.29%) 361ms (± 0.67%) ~ 358ms 364ms p=0.453 n=6
Req 5 - completionInfo count 2,073 (± 0.00%) 2,073 (± 0.00%) ~ 2,073 2,073 p=1.000 n=6
Req 5 - completionInfo 304ms (± 1.10%) 302ms (± 1.65%) ~ 295ms 308ms p=0.747 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • CompilerTSServer - node (v18.15.0, x64)
  • Compiler-UnionsTSServer - node (v18.15.0, x64)
  • xstateTSServer - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Startup

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
tsc-startup - node (v18.15.0, x64)
Execution time 152.34ms (± 0.18%) 152.22ms (± 0.18%) -0.12ms (- 0.08%) 151.14ms 155.43ms p=0.000 n=600
tsserver-startup - node (v18.15.0, x64)
Execution time 227.71ms (± 0.16%) 227.68ms (± 0.17%) ~ 226.40ms 232.31ms p=0.124 n=600
tsserverlibrary-startup - node (v18.15.0, x64)
Execution time 229.05ms (± 0.17%) 228.98ms (± 0.19%) ~ 227.30ms 236.41ms p=0.056 n=600
typescript-startup - node (v18.15.0, x64)
Execution time 229.09ms (± 0.17%) 228.98ms (± 0.18%) -0.11ms (- 0.05%) 227.30ms 234.49ms p=0.012 n=600
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • tsc-startup - node (v18.15.0, x64)
  • tsserver-startup - node (v18.15.0, x64)
  • tsserverlibrary-startup - node (v18.15.0, x64)
  • typescript-startup - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@jakebailey jakebailey marked this pull request as ready for review October 6, 2023 00:54
@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the top-repos suite comparing main and refs/pull/55991/merge:

Everything looks good!

@@ -28040,26 +28040,31 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function narrowTypeBySwitchOnTrue(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): Type {
const clauses = switchStatement.caseBlock.clauses.slice(clauseStart, clauseEnd);
const clausesType = narrowTypeForTrueClauses(type, clauses);
const defaultIndex = findIndex(switchStatement.caseBlock.clauses, clause => clause.kind === SyntaxKind.DefaultClause);
const hasDefaultClause = clauseStart === clauseEnd || (defaultIndex >= clauseStart && defaultIndex < clauseEnd);
Copy link
Member

Choose a reason for hiding this comment

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

Reading this in the below code was confusing for me because it's not about the switch having a default, it's about the given range including a default.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is just the terminology used above by the typeof narrowing. I can change it, but it will be inconsistent.

}

// Now, narrow based on the cases in this set.
const clauses = switchStatement.caseBlock.clauses.slice(clauseStart, clauseEnd);
Copy link
Member Author

Choose a reason for hiding this comment

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

I could theoretically try and make this directly iterate instead of slicing and mapping, if that's desirable. We already do a lot of slicing/mapping/etc in narrowing that isn't really needed, so this isn't new.

Copy link
Member Author

Choose a reason for hiding this comment

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

(see also narrowTypeBySwitchOnDiscriminant which just slices and doesn't try and be optimal)

@@ -28043,6 +28039,36 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return getUnionType(map(clauseWitnesses, text => text ? narrowTypeByTypeName(type, text) : neverType));
}

function narrowTypeBySwitchOnTrue(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): Type {
const defaultIndex = findIndex(switchStatement.caseBlock.clauses, clause => clause.kind === SyntaxKind.DefaultClause);
Copy link
Member

Choose a reason for hiding this comment

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

The only time switchStatement is used is to get caseBlock which is only used for clauses. Maybe we just pass that in or destructure that out?

Copy link
Member

Choose a reason for hiding this comment

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

I don't really know what clauseStart === clauseEnd means, but you could also avoid the findIndex by folding the walk into the first walk, and then you'd be able to avoid the range check, right?

You are basically walking across every switch/case per group of clauses (or per clause) which is N^2, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Like the other stuff, this is just me doing what the other switch case narrowings do. I can do whatever but it'll look different when it's possible that all of them could be improved.

Copy link
Member

Choose a reason for hiding this comment

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

Fair enough, we should come back to these at another PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, having now touched this, I do think there's a bunch to improve here and elsewhere. It's extremely likely that this just hasn't been touched in a while (when do we ever add new narrowings???)

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you want me to leave a TODO behind?

@jakebailey
Copy link
Member Author

jakebailey commented Oct 6, 2023

Just to note it, in talking to Daniel, it seems like there's some follow up stuff which would be nice to look at, e.g. if we just CFA'd between cases in switches, then it's likely that all of this code (and probably the other code for switch narrowing) would get to be deleted as we wouldn't have to re-encode that behavior into the checker (which we already do even before switch (true) for other narrowings, it's just that switch (true) is extra interesting).

(Note also that Playground Link matches.)

@jakebailey jakebailey merged commit 53a3d24 into microsoft:main Oct 6, 2023
19 checks passed
@jakebailey jakebailey deleted the fix-55986 branch October 6, 2023 23:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Milestone Bug PRs that fix a bug with a specific milestone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Narrowing switch(true) doesnt work for default case and is wrong for fallthrough cases in 5.3
6 participants