The parent field in the empty container singletons gets set to a node in the tree

Issue #316 resolved
Axel Mårtensson created an issue

The parent field in the empty container singletons List.EMPTY and Opt.EMPTY gets set to some node in the tree when setChild() is called, which has consequences for inherited attributes. The following test case illustrates the problem and can be used to test our proposed fix below. It tries to evaluate an inherited attribute on an empty container singleton:

// emptyContainerSingletons should not have their parent field set to a node in the tree
// .grammar: { A ::= B* C*; B; C; }
// .options: emptyContainerSingletons |
import static runtime.Test.*;
public class Test {
  public static void main(String[] args) {
    A a = new A(new List<B>(), new List<C>());
    try {
        a.getBs().inB();
    catch (NullPointerException e) {
        return;
    }
    fail("expected NullPointerException");
  }
}
aspect Test {
    inh boolean List.inB();
    eq A.getB(int i).inB() = true;
    eq A.getC(int i).inB() = false;
}

where inB() currently evaluates to false when compiling with the empty container singleton option, which is the equation for the C child, even though we evaluate the equation on the B child.

To fix this, we suggest overriding ASTNode.setParent() with an empty method in List.EMPTY and Opt.EMPTY to keep the parent field null.

diff --git a/src/template/ast/components/ListComponent.tt b/src/template/ast/components/ListComponent.tt
index beffa6c..c46706d 100644
--- a/src/template/ast/components/ListComponent.tt
+++ b/src/template/ast/components/ListComponent.tt
@@ -55,6 +55,9 @@ protected static final $List $List.EMPTY = new $(List)() {
   public $List setChild($ASTNode child, int pos) {
     throw new Error("attempting to setChild() in empty $List singleton!");
   }
+  @Override
+  public void setParent(ASTNode node) {
+  }
  };
$endif
]]
diff --git a/src/template/ast/components/OptionalComponent.tt b/src/template/ast/components/OptionalComponent.tt
index b1e7931..4fad707 100644
--- a/src/template/ast/components/OptionalComponent.tt
+++ b/src/template/ast/components/OptionalComponent.tt
@@ -46,6 +46,9 @@ $if(EmptyContainerSingletons)
     public $Opt setChild($ASTNode child, int pos) {
       throw new Error("attempting to setChild() in empty $Opt singleton!");
     }
+    @Override
+    public void setParent(ASTNode node) {
+    }
   };
$endif
]]

One benefit of this solution is that some programming errors may become easier to find, because we get a NullPointerException right away when we try to evaluate attributes on the empty container singletons.

Comments (4)

  1. Jesper Öqvist

    Thank you for the bug report Axel! I agree that empty container singletons should not have a parent pointer. I can’t think of a better way to solve this so I’ll apply your patch.

  2. Jesper Öqvist

    @Axel Mårtensson It does not seem like setChild(singleton) is called through normal AST construction. In which cases are you getting non-null parent pointers for the singletons?

    Correction: the singletons do get their parent pointers changed in normal AST construction - I was just testing it wrong.

  3. Jesper Öqvist

    I’m adding a few tests for the singleton containers since I couldn’t find any among our regression tests.

  4. Jesper Öqvist

    [codegen] Disable setParent() on empty container singletons

    By suggestion from Axel Mårtensson, empty container singleton objects should not have their parent reference changed.

    fixes #316

    → <<cset c9e3f5b16683>>

  5. Log in to comment