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

Make sure type parameters stay fixed throughout the inference process #2356

Merged
merged 12 commits into from
Mar 17, 2015

Conversation

JsonFreeman
Copy link
Contributor

This fixes #2182 and fixes #2127.

The problem is essentially that after we fix a type parameter during type argument inference, we may unfix it later. This happens because the inference context that we use to store the results gets forgotten on every iteration of type argument inference. The main part of this change is to make the inference context persist throughout the processing of signature.

As an example, if you have:

declare function f<T>(x: T, y: (p: T) => T, z: (p: T) => T): T;
f(0, x => null, x => null);

Right now, T will be inferred to any, because we do 3 passes (one for each argument in this case). The results from each pass are as follows:

  1. T is inferred to be number because of the 0
  2. T is fixed because we pass a lambda for the second argument, so we do not infer from the null
  3. T eventually gets fixed when we get to z for a similar reason, but we've forgotten that it got fixed in parameter y, so we've inferred from the null in the first lambda, and so we get any

This also had a very unusual effect with constraints, which led to a crash. Namely, when it comes time to fix a type parameter, and the type we were going to infer is not assignable to the constraint, we infer the constraint instead, and we fix the type parameter. Then later we may infer back from that constraint, thereby adding an inference candidate that should never have been added. As a result, we have no common supertype among candidates, when really we should only have had one candidate.

The moral of the story is that when a type parameter gets fixed, it should stay fixed. This change is not totally complete, in that fixedness does not get persisted between overloads, but that is a phenomenon for another day.

}
else {
// Infer the empty object type when no inferences were made
inferredType = emptyObjectType;
inferenceSucceeded = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems odd. No inferences were made, but inference succeeded? Can you provide some some explnatories comments on this? Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, but this is just a rule of the language. If there were no inferences, inference still succeeds. I don't love it. But @DanielRosenwasser has a PR where he issues an error for this case under a compiler flag.

Copy link
Member

Choose a reason for hiding this comment

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

It's not yet a PR; I'd like to make it a PR if we could solve some of the original concerns in #360.

To clarify, when inference for a type argument fails, the process falls back to using {} as a type argument. 👍 for explaining what a failure scenario would be in contrast to no inferences being found.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I'll add a comment.

@@ -4379,6 +4379,8 @@ module ts {
}
}

Debug.assert(!!downfallType, "If there is no common supertype, each type should have a downfallType");
Copy link
Member

Choose a reason for hiding this comment

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

downfallType? Is this supposed to be like a fallbackType? If so, rename it. downfallType sounds like it describes revolutions, lost wars, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is supposed to be downfallType. It is supposed to sound like it describes revolutions and lost wars, but in particular tragic heroes.

The downfallType is the type that caused a particular candidate to not be the common supertype. So if it weren't for this one downfallType, the type in question could have been the common supertype.

Note that downfallType is the first type that is not a subtype of type. It's not necessarily the only one.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add this explanation in the code

The downfallType is the type that caused a particular candidate to not be the common supertype. So if it weren't for this one downfallType, the type in question could have been the common supertype.

@danquirk
Copy link
Member

Is there a bug logged for the overload portion?

@JsonFreeman
Copy link
Contributor Author

@danquirk I have not seen one. I can try to file one if I can cook up a good repro.

@JsonFreeman
Copy link
Contributor Author

I've logged #2378 for the remaining work in this area.

}
return {
typeParameters: typeParameters,
inferUnionTypes: inferUnionTypes,
inferenceCount: 0,
inferences: inferences,
inferredTypes: new Array(typeParameters.length),
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this have failedTypeParameterIndex as a property? It is optional though I find it will be easier to read through

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I'm going to leave it out. The reason it's optional is because it is always initially undefined, and undefined means that there have been no failures (yet).

JsonFreeman added a commit that referenced this pull request Mar 17, 2015
Make sure type parameters stay fixed throughout the inference process
@JsonFreeman JsonFreeman merged commit 2bcf84c into master Mar 17, 2015
@JsonFreeman JsonFreeman deleted the typeParameterFixing branch March 17, 2015 18:42
@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Type inference algorithm in compiler differs from spec? Bug: Cannot read property 'flags' of undefined
6 participants