diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
index 990cfce958303755520c57f071ad0a844f7b1ec2..edba93cf227be044180e76fbb148bbc26b1ff9d8 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -142,7 +142,7 @@ public final class AccessArgumentNode extends RNode {
     private void checkPromiseFactory() {
         if (factory == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            Closure defaultClosure = formals.getOrCreatePromiseClosure(formals.getDefaultArgument(index));
+            Closure defaultClosure = formals.getClosureCache().getOrCreatePromiseClosure(formals.getDefaultArgument(index));
             factory = RPromiseFactory.create(PromiseState.Default, defaultClosure);
         }
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
index c9baefdb00e4c3eaa15eb028458d5ee2468b3bd7..b4093331fb475e1187560f1609c512fa197ced46 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -55,6 +55,7 @@ import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.data.Closure;
 import com.oracle.truffle.r.runtime.data.ClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -163,7 +164,7 @@ public class ArgumentMatcher {
             argNodes = suppliedArgs.getArguments();
             signature = suppliedArgs.getSignature();
         }
-        return ArgumentMatcher.matchNodes(target, argNodes, signature, s3DefaultArguments, callingNode, arguments, noOpt);
+        return ArgumentMatcher.matchNodes(target, argNodes, signature, s3DefaultArguments, callingNode, arguments.getClosureCache(), noOpt);
     }
 
     public static MatchPermutation matchArguments(ArgumentsSignature supplied, ArgumentsSignature formal, RBaseNode callingNode, RBuiltinDescriptor builtin) {
@@ -327,7 +328,7 @@ public class ArgumentMatcher {
      *         accordingly.
      */
     private static Arguments<RNode> matchNodes(RRootNode target, RNode[] suppliedArgs, ArgumentsSignature suppliedSignature, S3DefaultArguments s3DefaultArguments, RBaseNode callingNode,
-                    ClosureCache closureCache, boolean noOpt) {
+                    RNodeClosureCache closureCache, boolean noOpt) {
         CompilerAsserts.neverPartOfCompilation();
         assert suppliedArgs.length == suppliedSignature.getLength();
 
@@ -494,13 +495,13 @@ public class ArgumentMatcher {
              * InfixEmulationFunctions.tilde).
              */
             RNode defaultArg = formals.getDefaultArgument(formalIndex);
-            Closure defaultClosure = formals.getOrCreatePromiseClosure(defaultArg);
+            Closure defaultClosure = formals.getClosureCache().getOrCreatePromiseClosure(defaultArg);
             return PromiseNode.create(RPromiseFactory.create(PromiseState.Default, defaultClosure), noOpt, false);
         }
         return ConstantNode.create(formals.getInternalDefaultArgumentAt(formalIndex));
     }
 
-    private static RNode wrapMatched(FormalArguments formals, RBuiltinDescriptor builtin, ClosureCache closureCache, RNode suppliedArg, int formalIndex, boolean noOpt, FastPathFactory fastPath) {
+    private static RNode wrapMatched(FormalArguments formals, RBuiltinDescriptor builtin, RNodeClosureCache closureCache, RNode suppliedArg, int formalIndex, boolean noOpt, FastPathFactory fastPath) {
         // Create promise, unless it's the empty value
         if (suppliedArg instanceof ConstantNode) {
             ConstantNode a = (ConstantNode) suppliedArg;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
index f5f5cf0093a4ce1a615879d794cbe79ecb83dad9..aa7f399b5cd68256b509ad420d882144e43b01d9 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,6 @@
 package com.oracle.truffle.r.nodes.function;
 
 import java.util.Arrays;
-import java.util.IdentityHashMap;
 
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
@@ -34,15 +33,14 @@ import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.profiles.BranchProfile;
-import com.oracle.truffle.r.nodes.access.FrameSlotNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.data.Closure;
 import com.oracle.truffle.r.runtime.data.ClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
@@ -57,7 +55,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  * {@link RootCallTarget} for every argument.
  * </p>
  */
-public final class CallArgumentsNode extends RBaseNode implements UnmatchedArguments {
+public final class CallArgumentsNode extends RBaseNode {
     /**
      * A list of arguments. Single arguments may be <code>null</code>; semantics have to be
      * specified by implementing classes
@@ -66,24 +64,22 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
 
     protected final ArgumentsSignature signature;
 
-    @Child private FrameSlotNode varArgsSlotNode;
     @Child private PromiseCheckHelperNode promiseHelper;
 
+    private final RNodeClosureCache closureCache = new RNodeClosureCache();
+
     /**
      * If a supplied argument is a {@link ReadVariableNode} whose name is "...", this field contains
      * the index of the name. Otherwise it is an empty list.
      */
     @CompilationFinal(dimensions = 1) private final int[] varArgsSymbolIndices;
 
-    private final IdentityHashMap<RNode, Closure> closureCache = new IdentityHashMap<>();
-
     private CallArgumentsNode(RNode[] arguments, ArgumentsSignature signature, int[] varArgsSymbolIndices) {
         assert signature != null && signature.getLength() == arguments.length : Arrays.toString(arguments) + " " + signature;
         this.arguments = arguments;
         this.signature = signature;
         assert signature != null;
         this.varArgsSymbolIndices = varArgsSymbolIndices;
-        this.varArgsSlotNode = !containsVarArgsSymbol() ? null : FrameSlotNode.create(ArgumentsSignature.VARARG_NAME);
     }
 
     /**
@@ -126,6 +122,10 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
         return varArgs;
     }
 
+    public RNodeClosureCache getClosureCache() {
+        return closureCache;
+    }
+
     /**
      * This methods unrolls all "..." in the argument list. The result varies if the number of
      * arguments in the varargs or their names change.
@@ -262,25 +262,14 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
         return varArgsSymbolIndices.length > 0;
     }
 
-    public int[] getVarArgsSymbolIndices() {
-        return varArgsSymbolIndices;
-    }
-
-    @Override
-    public IdentityHashMap<RNode, Closure> getContent() {
-        return closureCache;
-    }
-
     /**
      * @return The {@link RNode}s of the arguments given to a function call, in the same order. A
      *         single argument being <code>null</code> means 'argument not provided'.
      */
-    @Override
     public RNode[] getArguments() {
         return arguments;
     }
 
-    @Override
     public ArgumentsSignature getSignature() {
         return signature;
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java
index 6e6db26cb0fccd899fb10c9f7fd4c50d8b6b4f7e..bd6d0baf292d733a7ecd2c8e449d959acfa2d817 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,6 @@
 package com.oracle.truffle.r.nodes.function;
 
 import java.util.Arrays;
-import java.util.IdentityHashMap;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.RootCallTarget;
@@ -31,8 +30,8 @@ import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.data.Closure;
 import com.oracle.truffle.r.runtime.data.ClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -50,12 +49,10 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  * ever one {@link RootCallTarget} for every default argument.
  * </p>
  */
-public final class FormalArguments extends Arguments<RNode> implements ClosureCache {
+public final class FormalArguments extends Arguments<RNode> {
 
     public static final FormalArguments NO_ARGS = new FormalArguments(new RNode[0], new Object[0], ArgumentsSignature.empty(0));
 
-    private final IdentityHashMap<RNode, Closure> closureCache = new IdentityHashMap<>();
-
     /**
      * These argument constants define what will be passed along in case there is no supplied
      * argument for the given argument slot. In the case of normal functions (as opposed to
@@ -64,6 +61,8 @@ public final class FormalArguments extends Arguments<RNode> implements ClosureCa
      */
     @CompilationFinal(dimensions = 1) private final Object[] internalDefaultArguments;
 
+    private final RNodeClosureCache closureCache = new RNodeClosureCache();
+
     private FormalArguments(RNode[] defaultArguments, Object[] internalDefaultArguments, ArgumentsSignature signature) {
         super(defaultArguments, signature);
         this.internalDefaultArguments = internalDefaultArguments;
@@ -117,8 +116,7 @@ public final class FormalArguments extends Arguments<RNode> implements ClosureCa
                         value == RArgsValuesAndNames.EMPTY;
     }
 
-    @Override
-    public IdentityHashMap<RNode, Closure> getContent() {
+    public RNodeClosureCache getClosureCache() {
         return closureCache;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
index 8001fc01002329f63db99348295bd5e6c34fd54c..e0dbba8ff097a1738b959f3d45146a6656cc1aee 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
@@ -72,6 +72,7 @@ import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.TruffleRLanguage;
 import com.oracle.truffle.r.runtime.data.Closure;
 import com.oracle.truffle.r.runtime.data.ClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
@@ -87,7 +88,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor;
 
-public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNode, RSyntaxFunction, ClosureCache {
+public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNode, RSyntaxFunction {
 
     private final FormalArguments formalArguments;
 
@@ -108,6 +109,8 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
     private SourceSection sourceSectionR;
     private final SourceSection[] argSourceSections;
 
+    private final RNodeClosureCache closureCache = new RNodeClosureCache();
+
     @Child private RNode saveArguments;
     @Child private FrameSlotNode onExitSlot;
     @Child private InlineCacheNode onExitExpressionCache;
@@ -380,7 +383,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
                                     CompilerDirectives.transferToInterpreter();
                                     RInternalError.shouldNotReachHere("unexpected type for on.exit entry: " + expr.car());
                                 }
-                                onExitExpressionCache.execute(frame, getOrCreateLanguageClosure((RNode) expr.car()));
+                                onExitExpressionCache.execute(frame, closureCache.getOrCreateLanguageClosure((RNode) expr.car()));
                             }
                         }
                     } catch (ReturnException ex) {
@@ -565,13 +568,4 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         }
         return handlerStackSlot;
     }
-
-    @Override
-    public IdentityHashMap<RNode, Closure> getContent() {
-        if (languageClosureCache == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            languageClosureCache = new IdentityHashMap<>();
-        }
-        return languageClosureCache;
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
index 8167d69768ac27941ebe9caae88aff77e3cb21f6..adb31fe1b3da2c65c83c82b723655ac7d6fd04ea 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -48,7 +48,7 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.data.Closure;
-import com.oracle.truffle.r.runtime.data.ClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
@@ -356,7 +356,7 @@ public abstract class PromiseNode extends RNode {
     }
 
     @TruffleBoundary
-    static RNode createVarArgs(RNode[] nodes, ArgumentsSignature signature, ClosureCache closureCache, boolean forcedEager) {
+    static RNode createVarArgs(RNode[] nodes, ArgumentsSignature signature, RNodeClosureCache closureCache, boolean forcedEager) {
         return new VarArgsPromiseNode(nodes, signature, closureCache, forcedEager);
     }
 
@@ -368,7 +368,7 @@ public abstract class PromiseNode extends RNode {
         private final Closure[] closures;
         private final ArgumentsSignature signature;
 
-        private VarArgsPromiseNode(RNode[] nodes, ArgumentsSignature signature, ClosureCache closureCache, boolean forcedEager) {
+        private VarArgsPromiseNode(RNode[] nodes, ArgumentsSignature signature, RNodeClosureCache closureCache, boolean forcedEager) {
             this.promised = new RNode[nodes.length];
             this.closures = new Closure[nodes.length];
             boolean noOpt = false;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java
deleted file mode 100644
index fd0461f0dc9fac10eeb915e7d01ccc980dd7bfb0..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.nodes.function;
-
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.data.ClosureCache;
-import com.oracle.truffle.r.runtime.nodes.RNode;
-
-/**
- * An interface all arguments that are going to be matched need to implement.
- */
-public interface UnmatchedArguments extends ClosureCache {
-    /**
-     * @return The arguments to be matched. Individual {@link RNode}s may be <code>null</code> to
-     *         denote "missingness"!
-     */
-    RNode[] getArguments();
-
-    ArgumentsSignature getSignature();
-}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java
index a8976382cd81765d33f10da5b45468cc6983709f..ef41f3c88e4d5422105bc6e8ac9ae520b4902c60 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,49 +22,64 @@
  */
 package com.oracle.truffle.r.runtime.data;
 
-import java.util.IdentityHashMap;
-import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 /**
- * A trait that enables the caching of {@link Closure}s for certain expressions ({@link RNode}s).
+ * Class that enables the caching of {@link Closure}s for certain expressions ({@link RNode}s).
+ * Instance of this class is supposed to be a field in AST node, which is using the cache. The field
+ * must be initialized in the constructor, but the underlying data structure (ConcurrentHashMap) is
+ * initialized lazily. All methods are thread safe.
+ * 
+ * Closures need to be cached so that we cache the corresponding call-targets and if the expression
+ * is evaluated again, we invoke it through the same call-target, which may be compiled by Truffle.
  */
-public interface ClosureCache {
+public abstract class ClosureCache<K> {
 
-    default Closure getOrCreatePromiseClosure(RNode expr) {
-        return getOrCreateClosure(Closure.PROMISE_CLOSURE_WRAPPER_NAME, expr);
+    private ConcurrentHashMap<K, Closure> cache;
+
+    public Closure getOrCreatePromiseClosure(K key) {
+        return getOrCreateClosure(Closure.PROMISE_CLOSURE_WRAPPER_NAME, key);
     }
 
-    default Closure getOrCreateLanguageClosure(RNode expr) {
-        return getOrCreateClosure(Closure.LANGUAGE_CLOSURE_WRAPPER_NAME, expr);
+    public Closure getOrCreateLanguageClosure(K key) {
+        return getOrCreateClosure(Closure.LANGUAGE_CLOSURE_WRAPPER_NAME, key);
     }
 
-    /**
-     * @param expr
-     * @return A {@link Closure} representing the given {@link RNode}. If expr is <code>null</code>
-     *         <code>null</code> is returned.
-     */
+    protected abstract RNode keyToNode(K key);
+
     @TruffleBoundary
-    default Closure getOrCreateClosure(String name, RNode expr) {
-        if (expr == null) {
+    private Closure getOrCreateClosure(String name, K key) {
+        if (key == null) {
             return null;
         }
+        if (cache == null) {
+            initMap();
+        }
+        return cache.computeIfAbsent(key, k -> Closure.create(name, keyToNode(k)));
+    }
 
-        IdentityHashMap<RNode, Closure> cache = getContent();
-        Closure result = cache.get(expr);
-        if (result == null) {
-            result = Closure.create(name, expr);
-            cache.put(expr, result);
+    private synchronized void initMap() {
+        if (cache == null) {
+            cache = new ConcurrentHashMap<>();
         }
-        return result;
     }
 
-    /**
-     * Access to the raw content.
-     *
-     * @return The {@link Map} containing the cached values
-     */
-    IdentityHashMap<RNode, Closure> getContent();
+    public static final class RNodeClosureCache extends ClosureCache<RNode> {
+        @Override
+        protected RNode keyToNode(RNode key) {
+            return key;
+        }
+    }
+
+    public static final class SymbolClosureCache extends ClosureCache<String> {
+        @Override
+        protected RNode keyToNode(String key) {
+            return RContext.getASTBuilder().lookup(RSyntaxNode.SOURCE_UNAVAILABLE, key, false).asRNode();
+        }
+    }
 }