Behavior of "TypeCheckingContext.checkRoot()" is different than in-IDE type checking.

I’m currently encountering an discrepancy between the type of a node in the IDE and its’ type after having the typechecker run programmatically. For some reason, some type equations aren’t substituting properly when I run `TypeCheckingContext.checkRoot()`. I’m getting back a typevar-style type instead of a resolved type. When the same code is checked in the IDE the type has been resolved.

For some context, we are reading models programmatically and need node types. Before the operation that reads the model, we run `checkRoot` on all root nodes in the model.

3
5 comments

What you describe might be an indication of a bug. Would you care to submit a bug report with steps to reproduce? Thanks.

0
Avatar
Permanently deleted user

Took me a while to reproduce, but I can upload a sample project that allows for reproduction. I'm not going to submit a bug report quite yet as I have found a way to make this work. However - it doesn't seem to be expected behavior, so I'll summarize my findings.

Given a root node with code that uses some indirect inference (i.e. inference that is not resolved via a single equation), I run this on a root:

```
System.out.println("INITIAL:"); 
root.descendants.forEach({~it => System.out.println(it.concept + " : " + it.type); }); 

TypeCheckingContext checkingContext = TypeContextManager.getInstance().createTypeCheckingContext(root); 
checkingContext.checkRoot(true); 

System.out.println("AFTER:"); 
root.descendants.forEach({~it => System.out.println(it.concept + " : " + it.type); }); 
System.out.println("AFTER2:"); // Equivalent to the previous line
root.descendants.forEach({~it => System.out.println(it.concept + " : " + TypeChecker.getInstance().getTypeOf(it)); }); 

System.out.println("USING CONTEXT"); 
root.descendants.forEach({~it => System.out.println(it.concept + " : " + checkingContext.typeOf(it)); });
```


In the section of code where we need access to types, we are using the `node.type` form (enabled by the typesystem language). Seeing as how the TypeChecker is singleton (along with most of the objects in the type engine), I would expect the same results using `node.type` as I get from the IDE. (i.e. "cmd + shift + t" on a node). However, this isn't the result - the "AFTER" and "AFTER2" both print types w/ var names like "a" or "p1".

When I added the last loop, the one that uses the checking context that I created to run the type checking, I receive the correct answer.

When viewing the source for `TypeContextManager`, it seems that the various types of `TypeCheckingContext`s are created by passing in the singleton TypeChecker. This would lead me to believe that I'd get the same result when using `node.type`. Apparently this assumption is wrong :)

At this point, I'm mainly curious as to how this all operates behind the scenes, and if I need to replace all of the `node.type` style calls with `someTypeCheckingContext.typeOf(x)` and pass the TypeCheckingContext through my program. I'm not even sure if calling `typeOf` is valid, given that the intent of this class seems to be to run type checking, not inspect types after checking has finished.

0

First, let me check if I understand the problem correctly. You are saying that the types reported right after the "INITIAL" mark are correct, but after a typechecking context has been created they fail to be computed? That would indeed be strange, and I would definitely want to look into it. 

However, the issue with types being "incomplete" on executing a .type operation is a well-known issue: a single node being typechecked in isolation may produce an incomplete result, as the typechecker operates without all the necessary information. This may be related to as "local typechecking", and the type system has some (rudimentary) support for dealing with it. 

When the root has been checked within a typechecking context, all further calls to .type are simply table lookups, and the types should be complete, given the correct type inference rules. To avoid having to handle the context directly, the class `TypeContextManager` provides helper methods 'runTypeCheckingAction()` and the like. One has to pass an instance of `ITypeContextOwner`, which can be the default one. All calls to .type originating from within this computation will make use of the typechecking context created and managed internally. 

0
Avatar
Permanently deleted user

Thanks for the reply - I wanted to note that I sent some additional information (and a sample project) to you on the MPS slack.

0

Yes, thanks for that, I'll take a look at it later. 

0

Please sign in to leave a comment.