diff --git a/ChangeLog b/ChangeLog
index eebd77c0256ec41ccb09414788d9ca5143805fa8..0cfa4cea9a3df9b53a2f32962f03f0b3ac0060e0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,6 +4,8 @@
     written in CSV format to the specified file. The number of synthesized,
     inherited, and collection attribute declarations and equations written
     for each AST class.
+    * NTAs declared in the abstract grammar will now always be cached,
+    just like attribute-only NTAs.
 
 2016-10-24  Jesper Öqvist <jesper.oqvist@cs.lth.se>
 
diff --git a/src/jastadd/ast/Flush.jrag b/src/jastadd/ast/Flush.jrag
index 3492554132b4d436d6d270cb02af36fb6faa354b..895b1f895c9d51608a51d907e4b816b316ecd353 100644
--- a/src/jastadd/ast/Flush.jrag
+++ b/src/jastadd/ast/Flush.jrag
@@ -52,7 +52,7 @@ aspect Flush {
   syn String AttrDecl.resetVisit() = templateContext().expand("AttrDecl.resetAttrVisit");
 
   public void AttrDecl.emitResetMethod(PrintStream out) {
-    if (isLazy() || isCircular()) {
+    if (isMemoized()) {
       templateContext().expand("AttrDecl.resetMethod", out);
     }
   }
diff --git a/src/jastadd/ast/JragCodeGen.jrag b/src/jastadd/ast/JragCodeGen.jrag
index 22349fa61224eda0399c7420e2e23160b54c208a..b2f453f490cf49ce3ee2f95af99a621cc8c7c589 100644
--- a/src/jastadd/ast/JragCodeGen.jrag
+++ b/src/jastadd/ast/JragCodeGen.jrag
@@ -86,13 +86,17 @@ aspect JragCodeGen {
   syn String Rewrite.sourceLocation() = ASTNode.sourceLocation(getFileName(), getStartLine());
 
   /**
-   * Decide if an attribute should be cached based on the cache mode and
-   * current global configuration.
+   * Decides if this attribute should be memoized.
    */
-  syn boolean AttrDecl.shouldCache(CacheMode mode) {
-    switch (mode) {
+  syn boolean AttrDecl.isMemoized() {
+    if (declaredNTA() || isCircular()) {
+      // NTAs and circular attributes are always cached.
+      return true;
+    }
+    // Check the cache mode:
+    switch (getCacheMode()) {
       case DEFAULT:
-        return config().cacheAll();
+        return config().cacheAll() || isNTA();
       case LAZY:
         return !config().cacheNone() || isNTA();
       case CACHED:
@@ -100,16 +104,10 @@ aspect JragCodeGen {
       case UNCACHED:
         return isNTA();
     }
+    // Only if there is a missing switch case:
     throw new Error("unhandled cache mode");
   }
 
-  syn boolean AttrDecl.isLazy() = declaredNTA() || isCircular() || shouldCache(getCacheMode());
-
-  eq SynDecl.isLazy() = declaredNTA() || isCircular() || shouldCache(getCacheMode());
-
-  eq InhDecl.isLazy() = declaredNTA() || isCircular() || shouldCache(getCacheMode());
-
-  eq CollDecl.isLazy() = declaredNTA() || isCircular() || shouldCache(getCacheMode());
 
   public String Grammar.genImportsList() {
     Set imports = new LinkedHashSet();
@@ -226,12 +224,12 @@ aspect JragCodeGen {
           sb.append(String.format("if (%s_visited == null) %s_visited = %s;\n",
               signature(), signature(), config().createDefaultSet()));
         }
-        if (getNumParameter() != 0 && isLazy() && !simpleCacheCheck()) {
+        if (getNumParameter() != 0 && isMemoized() && !simpleCacheCheck()) {
           sb.append(String.format("if (%s_computed == null) %s_computed = %s;\n",
               signature(), signature(), config().createDefaultMap()));
         }
       }
-      if (getNumParameter() != 0 && (isLazy() || isCircular())) {
+      if (getNumParameter() != 0 && (isMemoized() || isCircular())) {
         sb.append(String.format("if (%s_values == null) %s_values = %s;\n",
             signature(), signature(), config().createDefaultMap()));
       }
@@ -304,7 +302,7 @@ aspect JragCodeGen {
   syn boolean TokenComponent.isPrimitive() = AttrDecl.isPrimitiveType(getTokenId().getTYPE());
 
   public String AttrDecl.parameterStructure() {
-    if (!isParameterized() || (!isLazy() && !isCircular() && !config().visitCheckEnabled())) {
+    if (!isParameterized() || (!isMemoized() && !config().visitCheckEnabled())) {
       return "";
     } else if (getNumParameter() == 1) {
       return "Object _parameters = " + getParameter(0).getName() + ";\n";
@@ -323,7 +321,7 @@ aspect JragCodeGen {
    * Generate the state variable if needed.
    */
   public String AttrDecl.lazyState() {
-    if (isLazy()) {
+    if (isMemoized()) {
       return config().stateClassName() + " state = state();";
     } else {
       return "";
@@ -381,7 +379,7 @@ aspect JragCodeGen {
   public boolean TypeDecl.hasLazySynEqFor(AttrDecl attr) {
     if (attr instanceof SynDecl) {
       SynEq synEq = lookupSynEq(attr.signature());
-      return synEq != null && (synEq.decl().isLazy() || synEq.decl().isCircular());
+      return synEq != null && synEq.decl().isMemoized();
     }
     return false;
   }
@@ -432,7 +430,7 @@ aspect JragCodeGen {
   syn boolean AttrEq.canInlineBlock(AttrDecl decl) = false;
 
   eq SynEq.canInlineBlock(AttrDecl decl) =
-      hasComputeBlock() && !decl.isLazy() && !decl.isNTA() && !decl.isCircular();
+      hasComputeBlock() && !decl.isMemoized();
 
   /**
    * Generates the method to compute an attribute with a specific equation
@@ -520,7 +518,7 @@ aspect JragCodeGen {
     attr.emitVisitedDeclarations(out);
     if (!attr.isCircular()) {
       attr.emitResetMethod(out);
-      if (attr.isLazy()) {
+      if (attr.isMemoized()) {
         attr.emitCacheDeclarations(out);
       }
     } else if (attr.getNumParameter() == 0) {
@@ -770,8 +768,6 @@ aspect JragCodeGen {
     }
   }
 
-  syn boolean AttrDecl.hasCache() = isLazy() || isCircular();
-
   /**
    * @return {@code true} if the attribute declaration corresponds to a
    * non-token NTA component
@@ -786,7 +782,7 @@ aspect Compute {
       "getParent().Define_" + name() + "(this, null" + inhParametersTail() + ")";
 
   syn String AttrDecl.computeLhs() {
-    if (isLazy() && getNumParameter() == 0) {
+    if (isMemoized() && getNumParameter() == 0) {
       return signature() + "_value";
     } else {
       return getType() + " " + signature() + "_value";
diff --git a/src/jastadd/ast/Trace.jrag b/src/jastadd/ast/Trace.jrag
index f0529659e8c9dd87f186f1ecac53f9e1f5b25cad..e1926228594351ee3ecad61ae2a358eb9abeb38e 100644
--- a/src/jastadd/ast/Trace.jrag
+++ b/src/jastadd/ast/Trace.jrag
@@ -50,7 +50,7 @@ aspect Trace {
     } else if (this instanceof CollDecl) {
       b.append("coll ");
     }
-    if (isLazy()) {
+    if (isMemoized()) {
       b.append("lazy ");
     }
     if (isCircular()) {
diff --git a/src/jastadd/incremental/IncrementalDDG.jadd b/src/jastadd/incremental/IncrementalDDG.jadd
index 3635d049bfaca7c8b261c16ac1af365ffe74417d..1b4e5add2427b35b4320b209ebeffd96db7e6771 100644
--- a/src/jastadd/incremental/IncrementalDDG.jadd
+++ b/src/jastadd/incremental/IncrementalDDG.jadd
@@ -64,13 +64,13 @@ aspect IncrementalDDG {
       ArrayList list = new ArrayList();
       for (int k = 0; k < getNumSynDecl(); k++) {
         AttrDecl attr = getSynDecl(k);
-        if (attr.isLazy() || attr.isCircular()) {
+        if (attr.isMemoized()) {
           list.add(attr);
         }
       }
       for (int k = 0; k < getNumInhDecl(); k++) {
         AttrDecl attr = getInhDecl(k);
-        if (attr.isLazy() || attr.isCircular()) {
+        if (attr.isMemoized()) {
           list.add(attr);
         }
       }
@@ -145,13 +145,13 @@ aspect IncrementalDDG {
       ArrayList list = new ArrayList();
       for (int k = 0; k < getNumSynDecl(); k++) {
         AttrDecl attr = getSynDecl(k);
-        if (attr != null && (attr.isLazy() || attr.isCircular())) {
+        if (attr != null && attr.isMemoized()) {
           list.add(attr);
         }
       }
       for (int k = 0; k < getNumInhDecl(); k++) {
         AttrDecl attr = getInhDecl(k);
-        if (attr != null && (attr.isLazy() || attr.isCircular())) {
+        if (attr != null && attr.isMemoized()) {
           list.add(attr);
         }
       }
diff --git a/src/jastadd/incremental/IncrementalEval.jadd b/src/jastadd/incremental/IncrementalEval.jadd
index 422784b789378d6d8ae78e793f8ceb6ecf34a09b..f68ddc57036d6843ceea8954c56d346995eac5e4 100644
--- a/src/jastadd/incremental/IncrementalEval.jadd
+++ b/src/jastadd/incremental/IncrementalEval.jadd
@@ -61,13 +61,13 @@ aspect IncrementalEval {
     }
     for (int k = 0; k < getNumSynEq(); k++) {
       AttrDecl attr = getSynEq(k).decl();
-      if (attr != null && (attr.isLazy() || attr.isCircular())) {
+      if (attr != null && attr.isMemoized()) {
         list.add(attr);
       }
     }
     for (int k = 0; k < getNumInhDecl(); k++) {
       InhDecl attr = getInhDecl(k);
-      if (attr.isLazy() || attr.isCircular()) {
+      if (attr.isMemoized()) {
         list.add(attr);
       }
     }
diff --git a/src/template/ast/Attributes.tt b/src/template/ast/Attributes.tt
index 41df0ccbd6590a41415e1344ee91e9bceb052a95..edf1e3de225a80c044e28a84e0763cd000c85b66 100644
--- a/src/template/ast/Attributes.tt
+++ b/src/template/ast/Attributes.tt
@@ -306,7 +306,7 @@ AttrDecl.emitEquation [[
 ]]
 
 AttrDecl.cacheCheck [[
-$if(#hasCache)
+$if(#isMemoized)
 $include(AttrDecl.incHookAttrRead)
   $if(!#isParameterized)
     $if(#simpleCacheCheck)
@@ -342,7 +342,7 @@ $endif
 
 # Update the cache value for this attribute if caching is enabled.
 AttrDecl.cacheUpdate [[
-$if(#isLazy)
+$if(#isMemoized)
   $if(LegacyRewrite)
 if (#cacheStoreCondition) {
   $endif
@@ -392,7 +392,7 @@ $endif
 
 AttrDecl.cacheInit [[
 $if(LegacyRewrite)
-  $if(#isLazy)
+  $if(#isMemoized)
 $include(AttrDecl.cacheInitRewrite)
   $endif
 $endif
@@ -416,7 +416,7 @@ state().assertSameCircle(#(signature)_circle, "#hostClassName.#signatureJavaStyl
 #(signature)_circle = state().currentCircle();
 state().enterLazyAttribute();
 $else
-  $if(#isLazy)
+  $if(#isMemoized)
     $if(#simpleCacheCheck)
 state().enterLazyAttribute();
     $endif
@@ -428,7 +428,7 @@ AttrDecl.leaveLazyAttribute [[
 $if(ComponentCheck)
 state().leaveLazyAttribute();
 $else
-  $if(#isLazy)
+  $if(#isMemoized)
     $if(#simpleCacheCheck)
 state().leaveLazyAttribute();
     $endif
diff --git a/src/template/flush/Flush.tt b/src/template/flush/Flush.tt
index 7edb61cefbceba97feb8e683a7b0aab05634136e..0b4ee7653e7856b23c859c84f7965052a2c37d7f 100644
--- a/src/template/flush/Flush.tt
+++ b/src/template/flush/Flush.tt
@@ -126,7 +126,7 @@ $endif
 ]]
 
 AttrDecl.resetAttrCache [[
-$if(#isLazy)
+$if(#isMemoized)
   $if(#isParameterized)
     $if(!#simpleCacheCheck)
 #(signature)_computed = $CreateDefaultMap;
diff --git a/src/template/incremental/Tracking.tt b/src/template/incremental/Tracking.tt
index 1e3a25ebf1a92344d40fba5d6f8f1892d7d0d107..9bd43b7c6b85138cb702187ca0fb22c80467cb58 100644
--- a/src/template/incremental/Tracking.tt
+++ b/src/template/incremental/Tracking.tt
@@ -420,7 +420,7 @@ $endif
 
 AttrDecl.incHookAttrCompStart = [[
 $if(IncrementalEnabled)
-$if(#hasCache)
+$if(#isMemoized)
 
 $if(IncrementalLevelParam)
 $DDGNodeName tmpHandler = new $DDGNodeName(this, "", null);
@@ -455,7 +455,7 @@ $endif
 
 AttrDecl.incHookAttrCompStartCircular = [[
 $if(IncrementalEnabled)
-$if(#hasCache)
+$if(#isMemoized)
 
 $if(IncrementalLevelParam)
 $if(!#isParameterized)
@@ -491,7 +491,7 @@ $endif
 
 AttrDecl.incHookAttrCompEnd = [[
 $if(IncrementalEnabled)
-$if(#hasCache)
+$if(#isMemoized)
 
 $if(IncrementalLevelParam)
 state().exitAttrStoreEval(tmpHandler);
@@ -529,7 +529,7 @@ $endif
 
 AttrDecl.incHookAttrCompEndCircular = [[
 $if(IncrementalEnabled)
-$if(#hasCache)
+$if(#isMemoized)
 
 $if(IncrementalLevelParam)
 $if(!#isParameterized)
@@ -571,7 +571,7 @@ $endif
 
 AttrDecl.incHookAttrCompBeforeStore = [[
 $if(IncrementalEnabled)
-$if(#hasCache)
+$if(#isMemoized)
 
 $if(IncrementalLevelParam)
 $if(!#isParameterized)
@@ -597,7 +597,7 @@ $endif
 
 AttrDecl.incHookAttrCompAfterStore = [[
 $if(IncrementalEnabled)
-$if(#hasCache)
+$if(#isMemoized)
 
 $if(IncrementalLevelParam)
 tmpHandler.clearDependencies();
diff --git a/src/template/trace/TraceHooks.tt b/src/template/trace/TraceHooks.tt
index aafbcefc012853d12e8269495d78cd41c317afa0..88f2478ec04edfd57adbb351196c72414322d748 100644
--- a/src/template/trace/TraceHooks.tt
+++ b/src/template/trace/TraceHooks.tt
@@ -39,7 +39,7 @@ $if (TraceCompute)
     $if(#isCircular)
 state().trace().computeBegin(this, "#hostClassName.#signatureJavaStyle", _parameters, "");
     $else
-      $if(!#isLazy)
+      $if(!#isMemoized)
         $if(!VisitCheckEnabled)
           $if(#hasOneParameter)
 Object _parameters = #parameters;
@@ -82,7 +82,7 @@ $endif
 
 AttrDecl.traceCacheRead [[
 $if (TraceCache)
-  $if(#isLazy)
+  $if(#isMemoized)
     $if(#isParameterized)
       $if(#isCircular)
 state().trace().cacheRead(this, "#hostClassName.#signatureJavaStyle", _parameters, _cache);
@@ -98,7 +98,7 @@ $endif
 
 AttrDecl.traceCacheStore [[
 $if (TraceCache)
-  $if(#isLazy)
+  $if(#isMemoized)
     $if(#isParameterized)
       $if(#isCircular)
 state().trace().cacheWrite(this, "#hostClassName.#signatureJavaStyle", _parameters, _value.value);
@@ -114,7 +114,7 @@ $endif
 
 AttrDecl.traceCacheAbort [[
 $if (TraceCache)
-  $if(#isLazy)
+  $if(#isMemoized)
     $if(#isParameterized)
       $if(#isCircular)
 state().trace().cacheAbort(this, "#hostClassName.#signatureJavaStyle", _parameters, _value.value);