Skip to content

Fix switch statement exhaustiveness checking #34840

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

Merged
merged 3 commits into from
Nov 5, 2019
Merged

Fix switch statement exhaustiveness checking #34840

merged 3 commits into from
Nov 5, 2019

Conversation

ahejlsberg
Copy link
Member

Fixes #34661.

@ahejlsberg
Copy link
Member Author

@typescript-bot perf test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 30, 2019

Heya @ahejlsberg, I've started to run the perf test suite on this PR at 576e5f5. You can monitor the build here. It should now contribute to this PR's status checks.

@ahejlsberg
Copy link
Member Author

@typescript-bot perf test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 30, 2019

Heya @ahejlsberg, I've started to run the perf test suite on this PR at 576e5f5. You can monitor the build here. It should now contribute to this PR's status checks.

@ahejlsberg
Copy link
Member Author

@typescript-bot perf test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 30, 2019

Heya @ahejlsberg, I've started to run the perf test suite on this PR at 576e5f5. You can monitor the build here. It should now contribute to this PR's status checks.

@ahejlsberg
Copy link
Member Author

@typescript-bot perf test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 30, 2019

Heya @ahejlsberg, I've started to run the perf test suite on this PR at 576e5f5. You can monitor the build here. It should now contribute to this PR's status checks.

@rbuckton
Copy link
Contributor

@typescript-bot perf test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 30, 2019

Heya @rbuckton, I've started to run the perf test suite on this PR at 576e5f5. You can monitor the build here. It should now contribute to this PR's status checks.

Update: The results are in!

@microsoft microsoft deleted a comment from typescript-bot Oct 30, 2019
@typescript-bot
Copy link
Collaborator

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

Here they are:

Comparison Report - master..34840

Metric master 34840 Delta Best Worst
Angular - node (v10.16.3, x64)
Memory used 355,125k (± 0.03%) 356,833k (± 0.04%) +1,709k (+ 0.48%) 356,346k 356,994k
Parse Time 1.63s (± 0.65%) 1.62s (± 0.45%) -0.01s (- 0.31%) 1.61s 1.64s
Bind Time 0.87s (± 1.72%) 0.87s (± 0.94%) -0.00s (- 0.46%) 0.85s 0.89s
Check Time 4.56s (± 0.61%) 4.52s (± 0.28%) -0.04s (- 0.94%) 4.50s 4.56s
Emit Time 5.30s (± 0.58%) 5.25s (± 0.93%) -0.05s (- 1.00%) 5.18s 5.42s
Total Time 12.36s (± 0.41%) 12.26s (± 0.35%) -0.10s (- 0.82%) 12.19s 12.41s
Monaco - node (v10.16.3, x64)
Memory used 365,950k (± 0.01%) 366,004k (± 0.01%) +54k (+ 0.01%) 365,931k 366,073k
Parse Time 1.26s (± 0.46%) 1.26s (± 0.81%) +0.00s (+ 0.16%) 1.24s 1.29s
Bind Time 0.75s (± 0.53%) 0.75s (± 0.65%) +0.00s (+ 0.40%) 0.74s 0.76s
Check Time 4.65s (± 0.50%) 4.67s (± 0.73%) +0.02s (+ 0.41%) 4.59s 4.74s
Emit Time 2.94s (± 0.48%) 2.95s (± 0.73%) +0.01s (+ 0.37%) 2.92s 3.01s
Total Time 9.59s (± 0.26%) 9.63s (± 0.57%) +0.04s (+ 0.36%) 9.52s 9.77s
TFS - node (v10.16.3, x64)
Memory used 321,638k (± 0.03%) 321,651k (± 0.02%) +13k (+ 0.00%) 321,567k 321,792k
Parse Time 0.95s (± 0.58%) 0.96s (± 0.54%) +0.01s (+ 0.95%) 0.95s 0.97s
Bind Time 0.72s (± 1.63%) 0.72s (± 1.67%) +0.00s (+ 0.42%) 0.70s 0.75s
Check Time 4.10s (± 0.55%) 4.12s (± 0.52%) +0.02s (+ 0.49%) 4.07s 4.18s
Emit Time 3.05s (± 0.87%) 3.05s (± 0.72%) -0.00s (- 0.07%) 2.98s 3.10s
Total Time 8.82s (± 0.36%) 8.85s (± 0.38%) +0.03s (+ 0.32%) 8.79s 8.95s
Angular - node (v12.1.0, x64)
Memory used 330,765k (± 0.01%) 332,436k (± 0.03%) +1,671k (+ 0.51%) 332,235k 332,584k
Parse Time 1.56s (± 0.91%) 1.58s (± 0.57%) +0.01s (+ 0.90%) 1.55s 1.59s
Bind Time 0.84s (± 0.89%) 0.85s (± 0.68%) +0.01s (+ 1.19%) 0.84s 0.87s
Check Time 4.44s (± 0.58%) 4.45s (± 0.62%) +0.01s (+ 0.11%) 4.40s 4.52s
Emit Time 5.44s (± 0.64%) 5.47s (± 0.64%) +0.03s (+ 0.57%) 5.40s 5.54s
Total Time 12.29s (± 0.51%) 12.35s (± 0.48%) +0.06s (+ 0.50%) 12.21s 12.50s
Monaco - node (v12.1.0, x64)
Memory used 345,792k (± 0.02%) 345,708k (± 0.01%) -83k (- 0.02%) 345,636k 345,846k
Parse Time 1.23s (± 0.79%) 1.23s (± 1.02%) -0.00s (- 0.24%) 1.20s 1.25s
Bind Time 0.73s (± 0.65%) 0.73s (± 0.88%) +0.01s (+ 0.69%) 0.72s 0.75s
Check Time 4.49s (± 0.44%) 4.49s (± 0.32%) -0.00s (- 0.09%) 4.45s 4.52s
Emit Time 3.02s (± 0.58%) 3.02s (± 0.90%) +0.00s (+ 0.07%) 2.97s 3.08s
Total Time 9.46s (± 0.33%) 9.46s (± 0.39%) +0.00s (+ 0.01%) 9.40s 9.55s
TFS - node (v12.1.0, x64)
Memory used 304,041k (± 0.02%) 304,021k (± 0.02%) -21k (- 0.01%) 303,857k 304,206k
Parse Time 0.95s (± 0.31%) 0.95s (± 0.92%) +0.00s (+ 0.21%) 0.93s 0.97s
Bind Time 0.68s (± 0.53%) 0.68s (± 0.70%) -0.00s (- 0.15%) 0.67s 0.69s
Check Time 4.06s (± 0.56%) 4.06s (± 0.58%) +0.00s (+ 0.02%) 4.00s 4.13s
Emit Time 3.09s (± 0.61%) 3.08s (± 0.72%) -0.01s (- 0.39%) 3.03s 3.13s
Total Time 8.78s (± 0.41%) 8.77s (± 0.37%) -0.01s (- 0.11%) 8.72s 8.83s
Angular - node (v8.9.0, x64)
Memory used 349,964k (± 0.03%) 351,618k (± 0.01%) +1,654k (+ 0.47%) 351,514k 351,732k
Parse Time 2.10s (± 0.45%) 2.11s (± 0.35%) +0.01s (+ 0.43%) 2.10s 2.13s
Bind Time 0.91s (± 0.68%) 0.92s (± 0.54%) +0.01s (+ 0.77%) 0.91s 0.93s
Check Time 5.31s (± 0.77%) 5.32s (± 0.58%) +0.01s (+ 0.21%) 5.23s 5.38s
Emit Time 6.28s (± 1.08%) 6.27s (± 1.44%) -0.01s (- 0.11%) 6.04s 6.38s
Total Time 14.60s (± 0.67%) 14.62s (± 0.58%) +0.02s (+ 0.14%) 14.40s 14.73s
Monaco - node (v8.9.0, x64)
Memory used 363,805k (± 0.01%) 363,802k (± 0.01%) -3k (- 0.00%) 363,733k 363,885k
Parse Time 1.56s (± 0.38%) 1.56s (± 0.44%) 0.00s ( 0.00%) 1.55s 1.58s
Bind Time 0.93s (± 0.74%) 0.92s (± 0.57%) -0.01s (- 0.76%) 0.91s 0.93s
Check Time 5.55s (± 0.60%) 5.53s (± 0.58%) -0.03s (- 0.50%) 5.49s 5.64s
Emit Time 3.04s (± 0.83%) 3.03s (± 0.64%) -0.01s (- 0.39%) 3.00s 3.09s
Total Time 11.09s (± 0.32%) 11.04s (± 0.42%) -0.05s (- 0.46%) 10.98s 11.21s
TFS - node (v8.9.0, x64)
Memory used 320,538k (± 0.01%) 320,554k (± 0.01%) +16k (+ 0.00%) 320,416k 320,629k
Parse Time 1.28s (± 0.86%) 1.27s (± 0.53%) -0.00s (- 0.31%) 1.26s 1.29s
Bind Time 0.74s (± 0.81%) 0.74s (± 0.54%) +0.01s (+ 0.68%) 0.73s 0.75s
Check Time 4.72s (± 0.45%) 4.74s (± 0.64%) +0.02s (+ 0.34%) 4.67s 4.79s
Emit Time 3.21s (± 0.84%) 3.22s (± 0.34%) +0.01s (+ 0.40%) 3.20s 3.24s
Total Time 9.95s (± 0.38%) 9.97s (± 0.36%) +0.03s (+ 0.29%) 9.88s 10.03s
Angular - node (v8.9.0, x86)
Memory used 198,629k (± 0.02%) 199,596k (± 0.02%) +967k (+ 0.49%) 199,505k 199,693k
Parse Time 2.01s (± 0.55%) 2.04s (± 1.04%) +0.03s (+ 1.29%) 2.01s 2.11s
Bind Time 1.02s (± 0.74%) 1.02s (± 0.60%) -0.00s (- 0.39%) 1.01s 1.04s
Check Time 4.82s (± 0.83%) 4.81s (± 0.36%) -0.01s (- 0.12%) 4.79s 4.87s
Emit Time 6.06s (± 1.29%) 6.07s (± 0.92%) +0.01s (+ 0.16%) 5.99s 6.27s
Total Time 13.92s (± 0.66%) 13.94s (± 0.44%) +0.02s (+ 0.14%) 13.81s 14.09s
Monaco - node (v8.9.0, x86)
Memory used 203,761k (± 0.02%) 203,751k (± 0.02%) -9k (- 0.00%) 203,645k 203,815k
Parse Time 1.61s (± 0.70%) 1.60s (± 0.78%) -0.01s (- 0.37%) 1.58s 1.64s
Bind Time 0.75s (± 0.53%) 0.75s (± 0.66%) -0.00s (- 0.40%) 0.73s 0.75s
Check Time 5.34s (± 1.38%) 5.39s (± 0.95%) +0.05s (+ 0.94%) 5.26s 5.50s
Emit Time 2.93s (± 3.26%) 2.88s (± 0.81%) -0.05s (- 1.77%) 2.84s 2.94s
Total Time 10.63s (± 0.46%) 10.62s (± 0.43%) -0.01s (- 0.08%) 10.53s 10.74s
TFS - node (v8.9.0, x86)
Memory used 180,560k (± 0.02%) 180,536k (± 0.01%) -24k (- 0.01%) 180,465k 180,580k
Parse Time 1.30s (± 0.54%) 1.30s (± 0.65%) -0.00s (- 0.15%) 1.28s 1.32s
Bind Time 0.70s (± 0.72%) 0.69s (± 0.69%) -0.00s (- 0.29%) 0.69s 0.71s
Check Time 4.48s (± 0.52%) 4.49s (± 0.57%) +0.01s (+ 0.22%) 4.43s 4.54s
Emit Time 2.96s (± 0.58%) 2.98s (± 0.86%) +0.02s (+ 0.64%) 2.93s 3.04s
Total Time 9.43s (± 0.30%) 9.45s (± 0.31%) +0.02s (+ 0.24%) 9.40s 9.54s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-166-generic
Architecturex64
Available Memory16 GB
Available Memory8 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v10.16.3, x64)
  • node (v12.1.0, x64)
  • node (v8.9.0, x64)
  • node (v8.9.0, x86)
Scenarios
  • Angular - node (v10.16.3, x64)
  • Angular - node (v12.1.0, x64)
  • Angular - node (v8.9.0, x64)
  • Angular - node (v8.9.0, x86)
  • Monaco - node (v10.16.3, x64)
  • Monaco - node (v12.1.0, x64)
  • Monaco - node (v8.9.0, x64)
  • Monaco - node (v8.9.0, x86)
  • TFS - node (v10.16.3, x64)
  • TFS - node (v12.1.0, x64)
  • TFS - node (v8.9.0, x64)
  • TFS - node (v8.9.0, x86)
Benchmark Name Iterations
Current 34840 10
Baseline master 10

@ahejlsberg ahejlsberg merged commit 95be956 into master Nov 5, 2019
@ahejlsberg ahejlsberg deleted the fix34661 branch November 5, 2019 20:05
@weswigham
Copy link
Member

@ahejlsberg this PR has broken our nightly publish (you can verify by checking out this branch and running gulp LGK then gulp tests). Namely, once an LKG is performed, we can no longer compile ourselves (our test harness, anyway). We get an implicit any error on line 922 of vfs.ts:

src/harness/vfs.ts:922:31 - error TS7022: 'stats' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

in this code:

        private _getLinks(node: DirectoryInode) {
            if (!node.links) {
                const links = new collections.SortedMap<string, Inode>(this.stringComparer);
                const { source, resolver } = node;
                if (source && resolver) {
                    node.source = undefined;
                    node.resolver = undefined;
                    for (const name of resolver.readdirSync(source)) {
                        const path = vpath.combine(source, name);
                        const stats = resolver.statSync(path); // error on this variable declaration
                        switch (stats.mode & S_IFMT) {
                            case S_IFDIR:
                                const dir = this._mknod(node.dev, S_IFDIR, 0o777);
                                dir.source = vpath.combine(source, name);
                                dir.resolver = resolver;
                                this._addLink(node, links, name, dir);
                                break;
                            case S_IFREG:
                                const file = this._mknod(node.dev, S_IFREG, 0o666);
                                file.source = vpath.combine(source, name);
                                file.resolver = resolver;
                                file.size = stats.size;
                                this._addLink(node, links, name, file);
                                break;
                        }
                    }
                }
                else if (this._shadowRoot && node.shadowRoot) {
                    this._copyShadowLinks(this._shadowRoot._getLinks(node.shadowRoot), links);
                }
                node.links = links;
            }
            return node.links;
        }

we appear to be "finding" a completely bogus circularity. How would you like to handle it?

@ahejlsberg
Copy link
Member Author

ahejlsberg commented Nov 8, 2019

The root issue here is that we're a bit too aggressive in the exhaustiveness analysis. Specifically, when analyzing the possible control flows in a switch with no default, we always check whether the switch is exhaustive and, if so, turn the control flow type into never for the bypass branch. Here's a simple repro that errors in 3.7 even before this PR:

function foo() {
    const foo: number | undefined = 0;
    while (true) {
        const stats = foo;  // Circularity error here!
        switch (stats) {
            case 1: break;
            case 2: break;
        }
    }
}

To determine the type of stats we need to determine the control flow type of foo which ends up checking for switch exhaustiveness in the back edge of the loop. And because the switch statement expression references stats, we have a circularity.

A better approach would be to separately determine the type coming from the non-bypass control flows and the type coming from the bypass flow. Then, only if the type resulting from the bypass flow would widen the final type do we need to check whether to actually include it (i.e. only then do we need the exhaustiveness check). I'll work on a fix to implement that strategy.

@HolgerJeromin
Copy link
Contributor

Does this fix this code, too?

type JsonDataTypeNames = 'string' | 'number' | 'object' | 'array' | 'boolean' | 'null' | 'integer';
declare var schemaType: JsonDataTypeNames | JsonDataTypeNames[] | undefined;

if (!Array.isArray(schemaType)) {                             
} else {            
    for (let i = 0, ii = schemaType.length; i < ii; i++) {
        let validType = schemaType[i];  // any in ts3.7! not JsonDataTypeNames as in 3.6
        switch (validType) { }
    }
}

@ahejlsberg
Copy link
Member Author

@HolgerJeromin It does indeed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Exhaustive switch return type is incorrect without intermediate variable
6 participants