diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
index c5bcf37bf11496fab7af17ef4999ba125513e539..69e36da32770b378ff24172297974186c13d207d 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
@@ -168,10 +168,14 @@ public final class REngine implements RContext.Engine {
      * on return.
      *
      * @param function the actual function that invoked the "eval", e.g. {@code eval}, {@code evalq}
-     *            , {@code local}.
+     *            , {@code local}, or {@code null} if identification isn't important.
      */
     public Object eval(RFunction function, RLanguage expr, REnvironment envir, REnvironment enclos) throws PutException {
-        RootCallTarget callTarget = makeCallTarget((RNode) expr.getRep(), REnvironment.globalEnv());
+        return eval(function, (RNode) expr.getRep(), envir, enclos);
+    }
+
+    private static Object eval(RFunction function, RNode exprRep, REnvironment envir, @SuppressWarnings("unused") REnvironment enclos) throws PutException {
+        RootCallTarget callTarget = makeCallTarget(exprRep, REnvironment.globalEnv());
         MaterializedFrame envFrame = envir.getFrame();
         VirtualFrame vFrame = RRuntime.createVirtualFrame();
         // We make the new frame look like it was a real call to "function".
@@ -219,7 +223,18 @@ public final class REngine implements RContext.Engine {
 
     public Object evalPromise(RPromise expr, VirtualFrame frame) throws RError {
         RootCallTarget callTarget = makeCallTarget((RNode) expr.getRep(), REnvironment.emptyEnv());
-        return expr.setValue(runCall(callTarget, frame, false, false));
+        return runCall(callTarget, frame, false, false);
+    }
+
+    public Object evalPromise(RPromise promise) throws RError {
+        // have to do the full out eval
+        try {
+            return eval(lookupBuiltin("eval"), (RNode) promise.getRep(), promise.getEnv(), null);
+        } catch (PutException ex) {
+            // TODO a new, rather unlikely, error
+            assert false;
+            return null;
+        }
     }
 
     private static Object parseAndEvalImpl(ANTLRStringStream stream, Source source, VirtualFrame frame, REnvironment envForFrame, boolean printResult) {
diff --git a/com.oracle.truffle.r.native/lib/darwin/libRDerived.dylib b/com.oracle.truffle.r.native/lib/darwin/libRDerived.dylib
index 8d58a619d4c489fe40556023a711d7e24dc88d08..7357b08e5493dfd3624a3345209e9f84f6d9a3a4 100755
Binary files a/com.oracle.truffle.r.native/lib/darwin/libRDerived.dylib and b/com.oracle.truffle.r.native/lib/darwin/libRDerived.dylib differ
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
index 1ebdab86068c86ac7db3aaa4c301d9ecbdb059dc..1878e1c16fc049375d3b7ee3a4743cf05d8a21ae 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
@@ -97,6 +97,8 @@ public class EvalFunctions {
     @RBuiltin(name = "eval", nonEvalArgs = {0}, kind = SUBSTITUTE)
     public abstract static class Eval extends EvalAdapter {
 
+        public abstract Object execute(VirtualFrame frame, RPromise expr, REnvironment envir, RMissing enclos);
+
         @Specialization
         public Object doEval(VirtualFrame frame, RPromise expr, @SuppressWarnings("unused") RMissing envir, RMissing enclos) {
             return doEval(frame, expr, REnvironment.globalEnv(), enclos);
@@ -114,7 +116,7 @@ public class EvalFunctions {
              * caller, so we can evaluate the promise using frame.
              */
             controlVisibility();
-            Object exprVal = RContext.getEngine().evalPromise(expr, frame);
+            Object exprVal = expr.getValue(frame);
             return doEvalBody(exprVal, envir, enclos);
         }
 
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 6a6fcdc898e150df98859a6c3e9ebe74637b6abf..0cf58769a4ec1b7a83963a8503d5f8d4c353ebe1 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
@@ -45,7 +45,7 @@ public final class PromiseNode extends RNode {
      */
     @Override
     public Object execute(VirtualFrame frame) {
-        return new RPromise(languageRep.getRep());
+        return RDataFactory.createPromise(languageRep.getRep());
     }
 
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
index 395daa1647b3263c0637acfd4f7256b43f3ad2fc..01a62746c56d19c2e62083946b923d08cb7038b5 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
@@ -168,11 +168,17 @@ public final class RContext extends ExecutionContext {
         Object eval(RFunction function, RLanguage expr, REnvironment envir, REnvironment enclos) throws PutException;
 
         /**
-         * Evaluate a promise in the given frame (for a builtin, where we can use the
-         * {@link VirtualFrame}) of the caller directly).
+         * Evaluate a promise in the given frame, where we can use the {@link VirtualFrame}) of the
+         * caller directly). This should <b>only</b> be called by the {@link RPromise} class.
          */
         Object evalPromise(RPromise expr, VirtualFrame frame) throws RError;
 
+        /**
+         * Evaluate a promise in the {@link MaterializedFrame} stored with the promise. This should
+         * <b>only</b> be called by the {@link RPromise} class.
+         */
+        Object evalPromise(RPromise expr) throws RError;
+
     }
 
     private final HashMap<Object, RFunction> cachedFunctions = new HashMap<>();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
index cd9a544d3845e59bb577ea2035321ef2e2067011..e3da591a9e3982366c8ef2cc020e8134bda75cd9 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
@@ -317,11 +317,19 @@ public final class RDataFactory {
     }
 
     public static RSymbol createSymbol(String name) {
-        return new RSymbol(name);
+        return traceDataCreated(new RSymbol(name));
     }
 
     public static RLanguage createLanguage(Object rep) {
-        return new RLanguage(rep);
+        return traceDataCreated(new RLanguage(rep));
+    }
+
+    public static RPromise createPromise(Object rep, REnvironment env) {
+        return traceDataCreated(new RPromise(rep, env));
+    }
+
+    public static RPromise createPromise(Object rep) {
+        return createPromise(rep, null);
     }
 
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
index b1244a0a25ee37357312b674114855636fbd2180..3fb095a02370bbefbf676222a5ffd1ecd2d56e6b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
@@ -22,43 +22,73 @@
  */
 package com.oracle.truffle.r.runtime.data;
 
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.r.runtime.*;
+
 /**
  * Denotes an R {@code promise}. It extends {@link RLanguageRep} with a (lazily) evaluated value.
  */
 @com.oracle.truffle.api.CompilerDirectives.ValueType
 public class RPromise extends RLanguageRep {
     /**
-     * Denotes a promise that raised an error during evaluation.
+     * For promises associated with environments (frames) that are not top-level.
+     */
+    private REnvironment env;
+    /**
+     * When {@code null} the promise has not been evaluated.
      */
-    private static Object ERROR = new Object();
-
     private Object value;
 
     /**
-     * Create the promise with a representation that allow evaluation later.
+     * Create the promise with a representation that allows evaluation later in the "current" frame.
+     * The frame may need to be set if the promise is passed as an argument to another function.
      */
-    public RPromise(Object rep) {
+    RPromise(Object rep) {
+        this(rep, null);
+    }
+
+    /**
+     * Create the promise with a representation that allows evaluation later in a given frame.
+     */
+    RPromise(Object rep, REnvironment env) {
         super(rep);
+        this.env = env;
+    }
+
+    public REnvironment getEnv() {
+        return env;
     }
 
     /**
-     * This is a workaround for the fact that REngine can't be called from here (at the moment),
-     * otherwise the evaluation would be implicitly done in {@link #getValue}.
+     * Get the value of the promise, evaluating it if necessary in the associated environment. A
+     * promise is evaluate-once.
      */
-    public Object setValue(Object newValue) {
+    public Object getValue() {
         if (value == null) {
-            if (newValue == null) {
-                this.value = ERROR;
-            } else {
-                this.value = newValue;
+            assert env != null;
+            try {
+                value = RContext.getEngine().evalPromise(this);
+            } catch (RError e) {
+                value = e;
+                throw e;
             }
-        } else {
-            assert false : "promise already has a value";
         }
-        return this.value;
+        return value;
     }
 
-    public Object getValue() {
+    /**
+     * Get the value of the promise, evaluating it if necessary in the given {@link VirtualFrame}. A
+     * promise is evaluate-once.
+     */
+    public Object getValue(VirtualFrame frame) {
+        if (value == null) {
+            try {
+                value = RContext.getEngine().evalPromise(this, frame);
+            } catch (RError e) {
+                value = e;
+                throw e;
+            }
+        }
         return value;
     }