Skip to content

Fix an issue with types possibly being related to discriminated type despite of incompatible variance #51208

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

Closed
wants to merge 2 commits into from

Conversation

Andarist
Copy link
Contributor

fixes #51180

This union was correctly failing a check here:
https://github.dev/microsoft/TypeScript/blob/4c9afe8812fcdb4658472ccbced4a5cd4bae70ea/src/compiler/checker.ts#L19750
but its result isn't directly used if the check failed. This check effectively went through isRelatedTo to this block and failed there:
https://github.dev/microsoft/TypeScript/blob/4c9afe8812fcdb4658472ccbced4a5cd4bae70ea/src/compiler/checker.ts#L20150-L20171

So since this correct result of Ternary.False was not returned there (for reasons I don't understand but they seem to be important), the execution has continued. It continued up to this check here which succeeded:
https://github.dev/microsoft/TypeScript/blob/4c9afe8812fcdb4658472ccbced4a5cd4bae70ea/src/compiler/checker.ts#L20219

In there, only the structures are compared here:
https://github.dev/microsoft/TypeScript/blob/4c9afe8812fcdb4658472ccbced4a5cd4bae70ea/src/compiler/checker.ts#L20370-L20383

I've tried to call isRelated there instead but that experiment failed (maybe somebody else could make it work though). So I've reused the code related to variances that was responsible for creating the "initial" correct result of Ternary.False.

@typescript-bot typescript-bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Oct 18, 2022
Comment on lines 20370 to 20390
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(type) & ObjectFlags.Reference && (source as TypeReference).target === (type as TypeReference).target &&
!isTupleType(source) && !(isMarkerType(source) || isMarkerType(type))) {
// When strictNullChecks is disabled, the element type of the empty array literal is undefinedWideningType,
// and an empty array literal wouldn't be assignable to a `never[]` without this check.
if (isEmptyArrayLiteralType(source)) {
return Ternary.True;
}
// We have type references to the same generic type, and the type references are not marker
// type references (which are intended by be compared structurally). Obtain the variance
// information for the type parameters and relate the type arguments accordingly.
const variances = getVariances((source as TypeReference).target);
// We return Ternary.Maybe for a recursive invocation of getVariances (signalled by emptyArray). This
// effectively means we measure variance only from type parameter occurrences that aren't nested in
// recursive instantiations of the generic type.
if (variances === emptyArray) {
return Ternary.Unknown;
}
if (!typeArgumentsRelatedTo(getTypeArguments(source as TypeReference), getTypeArguments(type as TypeReference), variances, /* reportErrors */ false, IntersectionState.None)) {
return Ternary.False;
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is effectively a copy-paste (with very small adjustments) of the block here:
https://github.dev/microsoft/TypeScript/blob/4c9afe8812fcdb4658472ccbced4a5cd4bae70ea/src/compiler/checker.ts#L20150-L20171

This definitely should be deduplicated if the solution itself is correct. I will wait for the review before making any changes to this.

Copy link
Member

Choose a reason for hiding this comment

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

Using variance results more is usually good for perf, but a variance-based result and a structural result being different is fundamentally the problem. Usually means some comparison needs to add an Unmeasurrable or Unreliable marker to the result calculated for the variance relationship.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you think there is a problem with the added code? Or that there is a problem elsewhere and that elsewhere Unmeasurable/Unreliable is not handled correctly?

Copy link
Member

@weswigham weswigham Nov 7, 2022

Choose a reason for hiding this comment

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

More likely that a non-strictly-co--or-contra-variant comparison somewhere needs to throw out a Unmeasurable or Unreliable flag during the measurement comparison but isn't yet doing so.

@Andarist
Copy link
Contributor Author

It's kinda wild that the CI fails here with a type error. I can't repro this locally (using the same script) and I don't know how I would get a type error at that location as the changes in this PR are totally unrelated to it.

@RyanCavanaugh
Copy link
Member

The failure here is saying that this change causes TS to no longer be able to build itself. Confirming, I see this locally checking out your PR branch, but not in main.

You can run

gulp local --built

to reproduce

@RyanCavanaugh
Copy link
Member

Aside, it's quite rare to break TS without affecting any other tests! We'd want a testcase that isolates the problem this exposes, assuming there is one.

@Andarist
Copy link
Contributor Author

Thanks for the tip on how to reproduce this locally! I was trying gulp local before (guided by the error logs) but I didn't use the --built flag (nor --lkg=false that is used by the CI).

I've checked out the failing call site and as far as I can tell - it's a legit one :P it shouldn't fail. I've created a TS playground demonstrating the problem: TS playground. Gonna wrap this up later in a test case that fails with this PR but passes with the main branch and gonna open a PR with that directly to main.

In the meantime, maybe you'd have suggestions on how this PR could be improved to fix the original issue without degrading this case?

@Andarist
Copy link
Contributor Author

OTOH - IIRC variance check can return a different result than a full structural check and that's intended... 🤔

@weswigham
Copy link
Member

weswigham commented Nov 7, 2022

IRC variance check can return a different result than a full structural check and that's intended... 🤔

Definitely not(*). Any time the variance measurement for a type differs from the structural check, it's a bug, usually in the variance calculation logic. (We have many structural type relationships that aren't strictly co- or contra- variant and so have Unreliable and Unmeasurable bail-outs in variance measurement to flag those cases so we rely partially or solely on structural results for them, respectively).

* Unless in and out annotations are used, then it's anyone's guess. We toss a lot of variance measurement correctness guarantees out the window when those get used, since they can often be more restrictive than the measured variance, but only get used for variance-based comparisons, and not structural ones. The resulting mismatch can cause a headache of weird behavior which I usually just warn people off with "only use in and out for performance, and only if you know what you're doing".

Copy link
Member

@weswigham weswigham left a comment

Choose a reason for hiding this comment

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

Just formally marking changes requested - based on the behavior, there's definitely a comparison somewhere whose variance result needs to be marked Unreliable and today is not. Tracking that down and marking it is the correct fix.

@Andarist
Copy link
Contributor Author

This has fallen off my radar and pushed down the priority list but I will be back here to investigate this further (unless somebody beats me to it). For now, I will convert this to a draft to indicate that I don't exactly work on this right now.

@Andarist Andarist marked this pull request as draft March 20, 2023 10:21
# Conflicts:
#	src/compiler/checker.ts
@Andarist
Copy link
Contributor Author

Andarist commented Jun 5, 2023

@weswigham going to close this PR since you have fixed the inconsistency in #52106 but part of the issue still looks suspicious and perhaps could be looked into or explained (see here)

@Andarist Andarist closed this Jun 5, 2023
@Andarist Andarist deleted the fix/51180 branch June 5, 2023 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Type confusion when function returns a union
5 participants