[compiler] Refactor checker to use context, fixing template decorator bugs#9588
[compiler] Refactor checker to use context, fixing template decorator bugs#9588witemple-msft wants to merge 9 commits intomicrosoft:mainfrom
Conversation
commit: |
|
All changed packages have been documented.
Show changes
|
|
You can try these changes here
|
| /** No flags set. */ | ||
| None = 0, | ||
| /** Currently checking within an uninstantiated template declaration. */ | ||
| InTemplateDeclaration = 1 << 0, |
There was a problem hiding this comment.
Do you think it would be worth splitting this 2 flags(Declaration + Template) so you could easily have InTemplateInstance too?
There was a problem hiding this comment.
It wouldn't really be splitting this flag into two, since this flag would continue to have the same behavior. As far as I can tell, there isn't a use case for "am I within an instantiation of a template" as a predicate. I haven't studied it extremely deeply, though. I was only focusing on this "am I a type that is part of an uninstantiated template" question.
There was a problem hiding this comment.
there is just quite a few checks in the checker where we want to know if we should finish the type and this involve checking if its either a non template declaration or a template instance
seems like its contained in this function
but I guess its an internal thing so we could always add the split later
There was a problem hiding this comment.
shouldRunDecorators is deleted in this branch and replaced with ctx.hasFlags(CheckFlags.InTemplateDeclaration) everywhere. Basically we always run decorators if the type is concrete, and never run decorators if the type is part of (is or references unbound parameters of) an uninstantiated template.
One semantic change to how the flag is calculated vs. how this function works in main is that it isn't good enough to check if t.kind === "TemplateParameter". That catches cases where the argument evaluates exactly to an unbound parameter, but not cases where the argument evaluates to something that contains an unbound parameter. The new logic addresses that among other things.
|
|
||
| // If we are instantating operation inside of interface | ||
| if (isTemplatedNode(node) && mapper !== undefined && parentInterface) { | ||
| mapper = { ...mapper, partial: true }; | ||
| if (isTemplatedNode(node) && ctx.mapper !== undefined && parentInterface) { |
There was a problem hiding this comment.
I think this one might be able to simplify to use the new flag
There was a problem hiding this comment.
I don't think it does, because this logic is for a templated operation declaration inside an interface instance.
interface A<T> {
op b<U>(param: T): U;
}
// Activates codepath in question with mapper. InTemplateDeclaration is off
// when checking `A` because it is concrete, but reactivated when checking
// `b` within the instance because `b` is templated.
interface AA extends A<string>;
// Now `b` from the concrete instance of `A` is instantiated and the flag is off
// again.
op example is AA.b<numeric>;Each concrete instance of A instantiates a new b that is itself a template. It inherits the mapper from the instance of A, but that mapper is partial because b is a template even in the context of a concrete instance of A. It's a bit of a different question.
This PR fixes several bugs related to the logic around running decorators on templates. Current decorator logic is insufficient because it is based on whether the
mapperis set and the node has template arguments, but we have cases where this logic breaks down, like:This change fixes those problems by refactoring the checker to work with an immutable
CheckContextthat is passed through the checker API. Checking always begins withCheckContext.DEFAULTat any checker API entrypoint. The context carries (a) the current TypeMapper, (b) an integer set of flags (there is currently one flag:CheckFlags.InTemplateDeclaration), and (c) a set of observed template parameter instances in a certain scope.There are no public API changes to the Checker, only internal changes to allow the Checker itself to pass
CheckContextto functions that are also exposed in the public API without that option.The context manages the state required to track whether the checker is currently declaring a template and how it should behave as it traverses references to declared types.
CheckFlags.InTemplateDeclarationif there is no type mapper, and the node has template parameters.CheckFlags.InTemplateDeclarationif the type reference is concrete (does not contain a template parameter).ctx.hasFlags(CheckFlags.InTemplateDeclaration), and decorators will only run on fully concrete instances and will be inhibited on any type instance that references a template parameter.