diff --git a/ChangeLog b/ChangeLog
index 3ce0d883927bad3f95e80a305d3ddff8fb3b65c3..ac56606ae1ea498563c0680888270da5ab8abddf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2019-03-18  Jesper Öqvist <jesper.oqvist@cs.lth.se>
+
+    * Allow implicit root type for collection attributes in grammars with
+    multiple root types.
+
 2018-06-14  Jesper Öqvist <jesper.oqvist@cs.lth.se>
 
     * Removed generation of the is$Equal method. The method was unused.
diff --git a/src/jastadd/ast/ASTErrors.jrag b/src/jastadd/ast/ASTErrors.jrag
index a2109f3d9ba1bda8e777e1ecaa78f06620341db0..96a8f4481e4ebfee390ade81065441e25dd14445 100644
--- a/src/jastadd/ast/ASTErrors.jrag
+++ b/src/jastadd/ast/ASTErrors.jrag
@@ -43,15 +43,6 @@ aspect ASTErrors {
       [new LinkedList<Problem>()]
       root Grammar;
 
-  syn boolean TypeDecl.isRootNode() = false;
-
-  eq ASTDecl.isRootNode() = isPotentialRootNode() && parents().isEmpty();
-
-  syn boolean TypeDecl.isPotentialRootNode() = false;
-
-  eq ASTDecl.isPotentialRootNode() =
-      !hasAbstract() && !isASTNodeDecl() && !isOptSubtype() && !isListSubtype();
-
   Grammar contributes Problem.builder()
           .message("there is no root node in the grammar!")
           .buildWarning()
diff --git a/src/jastadd/ast/AttributeProblems.jrag b/src/jastadd/ast/AttributeProblems.jrag
index de7fa1df46794143073a65080f76668e36772065..aa5e9ae6b3997e6388f68b347cd955f96f267934 100644
--- a/src/jastadd/ast/AttributeProblems.jrag
+++ b/src/jastadd/ast/AttributeProblems.jrag
@@ -461,22 +461,6 @@ aspect AttributeProblems {
     return error(msg);
   }
 
-  CollDecl contributes multipleRootsProblem()
-      when hasMultipleRootsProblem()
-      to TypeDecl.attributeProblems()
-      for hostClass();
-
-  syn boolean CollDecl.hasMultipleRootsProblem() = root == null && grammar().roots().size() > 1;
-
-  syn Problem CollDecl.multipleRootsProblem() {
-    StringBuilder buf = new StringBuilder();
-    buf.append("multiple tree roots to search for contributions. Please explicitly select one of");
-    for (ASTDecl decl : grammar().roots()) {
-      buf.append(" " + decl.name());
-    }
-    return error(buf.toString());
-  }
-
   CollDecl contributes error("No tree roots to search for contributions. "
           + "Please declare an explicit root node.")
       when hasNoRootProblem()
diff --git a/src/jastadd/ast/CollectionAttributes.jrag b/src/jastadd/ast/CollectionAttributes.jrag
index c4ca0f82ac98782293ffdf89c664fe86044d9a2a..28ba15e93bc9f79823243cf6186815b421aef09b 100644
--- a/src/jastadd/ast/CollectionAttributes.jrag
+++ b/src/jastadd/ast/CollectionAttributes.jrag
@@ -1,4 +1,4 @@
-/* Copyright (c) 2005-2016, The JastAdd Team
+/* Copyright (c) 2005-2019, The JastAdd Team
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,9 @@ aspect CollectionAttributes {
   /** @return the type name of the collection root node */
   syn String CollDecl.rootType() = root().name();
 
+  /** Returns {@code true} if the declaration does not have an explicit root type. */
+  syn boolean CollDecl.implicitRoot() = root == null;
+
   /** @return the type name of the collection root node */
   syn String CollEq.rootType() = decl().rootType();
 
@@ -134,9 +137,18 @@ aspect CollectionAttributes {
     }
   }
 
-  syn TypeDecl AttrDecl.root() = grammar().root();
-
-  eq CollDecl.root() = root != null ? grammar().lookup(root) : super.root();
+  syn TypeDecl CollDecl.root() {
+    if (root != null) {
+      return grammar().lookup(root);
+    } else {
+      Collection<ASTDecl> roots = grammar().roots();
+      if (roots.size() == 1) {
+        return roots.iterator().next();
+      } else {
+        return grammar().lookup(config().astNodeType());
+      }
+    }
+  }
 
   // Only used by circular collection attributes.
   public void AttrDecl.emitCircularCollectionEval(PrintStream out) {
@@ -428,6 +440,10 @@ aspect CollectionAttributes {
     return s.toString();
   }
 
+  /**
+   * Finds all roots in the grammar.
+   * A root is not a child of any other node.
+   */
   syn lazy Collection<ASTDecl> Grammar.roots() {
     Collection<ASTDecl> roots = new HashSet<ASTDecl>();
     for (TypeDecl decl : getTypeDeclList()) {
@@ -438,7 +454,18 @@ aspect CollectionAttributes {
     return roots;
   }
 
-  syn ASTDecl Grammar.root() = roots().isEmpty() ? null : roots().iterator().next();
+  /**
+   * Returns {@code true} if this type is a root in the abstract grammar.
+   * A root is not abstract and not a child of any other type in the grammar.
+   */
+  syn boolean TypeDecl.isRootNode() = false;
+
+  eq ASTDecl.isRootNode() = isPotentialRootNode() && parents().isEmpty();
+
+  syn boolean TypeDecl.isPotentialRootNode() = false;
+
+  eq ASTDecl.isPotentialRootNode() =
+      !hasAbstract() && !isASTNodeDecl() && !isOptSubtype() && !isListSubtype();
 
   eq ASTDecl.getCollDecl().hostClass() = this;
 
diff --git a/src/template/ast/Collections.tt b/src/template/ast/Collections.tt
index 660efeced6f71c5cbd3eae21475fb79d9b1287bb..d5c16669fb4e9985c4b82ccfa078328b9502e7ff 100644
--- a/src/template/ast/Collections.tt
+++ b/src/template/ast/Collections.tt
@@ -1,4 +1,4 @@
-# Copyright (c) 2013-2015, The JastAdd Team
+# Copyright (c) 2013-2019, The JastAdd Team
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -25,6 +25,22 @@
 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 # POSSIBILITY OF SUCH DAMAGE.
 
+CollDecl.findRoot [[
+$if(#implicitRoot)
+$ASTNode node = this;
+while (node.getParent() != null) {
+  node = node.getParent();
+}
+$else
+$ASTNode node = this;
+while (node != null && !(node instanceof #rootType)) {
+  node = node.getParent();
+}
+$endif
+$include(CollDecl.collDebugCheck)
+#rootType root = (#rootType) node;
+]]
+
 CollDecl.collDebugCheck [[
 $if(DebugMode)
 if (node == null) {
@@ -37,12 +53,7 @@ $endif
 CollDecl.computeMethod:onePhase [[
   /** @apilevel internal */
   private #getType #(name)_compute() {
-    $ASTNode node = this;
-    while (node != null && !(node instanceof #rootType)) {
-      node = node.getParent();
-    }
-    $include(CollDecl.collDebugCheck)
-    #rootType root = (#rootType) node;
+    $include(CollDecl.findRoot)
     root.survey_#collectionId();
     if (#(signature)_value == null) {
       #(signature)_value = $BottomValue;
@@ -54,12 +65,7 @@ CollDecl.computeMethod:onePhase [[
 CollDecl.computeMethod:twoPhase [[
   /** @apilevel internal */
   private #getType #(name)_compute() {
-    $ASTNode node = this;
-    while (node != null && !(node instanceof #rootType)) {
-      node = node.getParent();
-    }
-    $include(CollDecl.collDebugCheck)
-    #rootType root = (#rootType) node;
+    $include(CollDecl.findRoot)
     root.survey_#collectionId();
     #getType _computedValue = $BottomValue;
     $include(CollDecl.collectContributions)
@@ -69,7 +75,7 @@ CollDecl.computeMethod:twoPhase [[
 
 CollDecl.collectContributions [[
 if (root.contributorMap_#collectionId.containsKey(this)) {
-  for ($ASTNode contributor : root.contributorMap_#collectionId.get(this)) {
+  for ($ASTNode contributor : (java.util.Set<$ASTNode>) root.contributorMap_#collectionId.get(this)) {
     contributor.contributeTo_#(signature)(_computedValue);
   }
 }