langium
langium copied to clipboard
Incorrect types inferrence for parents with own properties
When a parent has no own properties, we generate a type Parent :
Parent:
ChildA | ChildB;
ChildA:
'a' a=NUMBER commonProp=ID;
ChildB:
'b' b=NUMBER commonProp=ID;
generates
export type Parent = ChildA | ChildB;
export interface ChildA extends AstNode {
a: number
commonProp: string
}
export interface ChildB extends AstNode {
b: number
commonProp: string
}
Besides, common children properties like commonProp will be lifted automatically. However, as only a parent has own properties, we generate an interface Parent:
Parent:
parentProp=ID (ChildA | ChildB);
// ChildA and ChildB are without changes
generates
export interface Parent extends AstNode {
parentProp: string
}
export interface ChildA extends AstNode {
a: number
commonProp: string
}
export interface ChildB extends AstNode {
b: number
commonProp: string
}
You can notice, that ChildA and ChildB don't extend Parent and their commonProp is not lifted in Parent.
Langium version: 0.5.0 Package name: langium
The current behavior
Inferred Parent -- ChildA -- ChildB hierarchy is wrong when Parent has own properties (look above).
The expected behavior
export interface Parent extends AstNode {
parentProp: string
commonProp: string
}
export interface ChildA extends Parent {
a: number
}
export interface ChildB extends Parent {
b: number
}
The solution to the problem requires restoring properties lifting algorithm.
However, as only a parent has own properties, we generate an interface
Parent:Parent: parentProp=ID (ChildA | ChildB); // ChildA and ChildB are without changesgenerates
export interface Parent extends AstNode { parentProp: string } export interface ChildA extends AstNode { a: number commonProp: string } export interface ChildB extends AstNode { b: number commonProp: string }You can notice, that
ChildAandChildBdon't extendParentand theircommonPropis not lifted inParent.Langium version: 0.5.0 Package name: langium
@pluralia IMO the behavior is totally correct. Actually you grammar is incorrect, as the rule
Parent:
parentProp=ID (ChildA | ChildB);
needs to instantiate an object of type Parent once it consumes an ID in order to assign it to prop parentProp.
The result of ChildA | ChildB is then unassigned (and eventually lost).
If you can write your grammar like
Parent:
(ChildA | ChildB) parentProp=ID;
you should obtain your desired result.
Alternatively you could use assigned actions, like
Parent:
parentProp=ID (ChildA.props = current | ChildB.props = current);
(in order to keep your syntax, of course the AST will still look different).
The current behavior
Inferred
Parent--ChildA--ChildBhierarchy is wrong whenParenthas own properties (look above).The expected behavior
export interface Parent extends AstNode { parentProp: string commonProp: string } export interface ChildA extends Parent { a: number } export interface ChildB extends Parent { b: number }
If you can write your grammar like
Parent: (ChildA | ChildB) parentProp=ID;you should obtain your desired result.
@sailingKieler, could you explain, why this has to get different result with?
Parent:
(ChildA | ChildB) parentProp=ID;
Parent: (ChildA | ChildB) parentProp=ID;
In this case the rule doesn't instantiate an object itself, but proceeds with the result of (ChildA | ChildB).
It's the same pattern as for rules like:
Expression:
Literal | UnaryOp | BinaryOp
;
plus the additional property assignment
Thank you for the explanation!
If you can write your grammar like
Parent: (ChildA | ChildB) parentProp=ID;you should obtain your desired result.
This infers the same types and the issue is still here.
This infers the same types and the issue is still here.
Ok, then it's really a mistake ;-). Does Xtext's type inference test contain a corresponding test case?
Yes, in Xtext it's forbidden to call unassigned rules after object creation (action or property assignment). But in Langium we are not forced to do the same, see discussion in https://github.com/langium/langium/discussions/205.