From 167f7d23442db5a3b09adc32d37159c5ca8c16db Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Wed, 7 Mar 2018 11:41:53 +0100
Subject: [PATCH] Implement the `function` builtin

---
 .../r/engine/RRuntimeASTAccessImpl.java       |  2 +
 .../r/nodes/builtin/base/BasePackage.java     |  3 +-
 .../truffle/r/nodes/builtin/base/Call.java    | 29 ++------
 .../builtin/base/infix/FunctionBuiltin.java   | 67 +++++++++++++++++--
 .../oracle/truffle/r/nodes/RASTBuilder.java   | 28 ++++++++
 .../oracle/truffle/r/runtime/RDeparse.java    | 35 +++++++++-
 .../com/oracle/truffle/r/runtime/RError.java  |  1 +
 .../truffle/r/runtime/nodes/RCodeBuilder.java | 10 ++-
 .../truffle/r/test/ExpectedTestOutput.test    | 26 +++++--
 .../r/test/builtins/TestBuiltin_ascall.java   |  6 +-
 .../r/test/builtins/TestBuiltin_function.java | 37 ++++++++++
 11 files changed, 205 insertions(+), 39 deletions(-)
 create mode 100644 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_function.java

diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
index 1e8fa828c1..bfd35372ed 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
@@ -67,6 +67,7 @@ import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntimeASTAccess;
 import com.oracle.truffle.r.runtime.RSrcref;
@@ -84,6 +85,7 @@ import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index 17a34fa9a8..601f97464f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -72,7 +72,6 @@ import com.oracle.truffle.r.nodes.builtin.base.infix.BreakBuiltinNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.infix.ForBuiltin;
 import com.oracle.truffle.r.nodes.builtin.base.infix.ForBuiltinNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.infix.FunctionBuiltin;
-import com.oracle.truffle.r.nodes.builtin.base.infix.FunctionBuiltinNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.infix.IfBuiltin;
 import com.oracle.truffle.r.nodes.builtin.base.infix.IfBuiltinNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.infix.NextBuiltin;
@@ -798,7 +797,7 @@ public class BasePackage extends RBuiltinPackage {
         add(BraceBuiltin.class, BraceBuiltinNodeGen::create);
         add(BreakBuiltin.class, BreakBuiltinNodeGen::create);
         add(ForBuiltin.class, ForBuiltinNodeGen::create);
-        add(FunctionBuiltin.class, FunctionBuiltinNodeGen::create);
+        add(FunctionBuiltin.class, FunctionBuiltin::create);
         add(IfBuiltin.class, IfBuiltinNodeGen::create);
         add(NextBuiltin.class, NextBuiltinNodeGen::create);
         add(ParenBuiltin.class, ParenBuiltin::new, ParenBuiltin::special);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java
index da1db2993a..c60c75338c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.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
@@ -33,6 +33,8 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.base.infix.FunctionBuiltin;
+import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
@@ -79,32 +81,15 @@ public abstract class Call extends RBuiltinNode.Arg2 {
     @TruffleBoundary
     public static RLanguage makeCall(TruffleRLanguage language, RSyntaxNode target, Object[] arguments, ArgumentsSignature signature) {
         assert arguments.length == signature.getLength();
-        if (target instanceof RSyntaxLookup && "function".equals(((RSyntaxLookup) target).getIdentifier())) {
-            return makeFunction(language, arguments);
+        if (target instanceof RSyntaxLookup && "function".equals(((RSyntaxLookup) target).getIdentifier()) && arguments.length == 2) {
+            // this optimization is not strictly necessary, `function` builtin is functional too.
+            FunctionExpressionNode function = FunctionBuiltin.createFunctionExpressionNode(language, arguments[0], arguments[1]);
+            return RDataFactory.createLanguage(Closure.createLanguageClosure(function.asRNode()));
         } else {
             return makeCall0(target, arguments, signature);
         }
     }
 
-    private static RLanguage makeFunction(TruffleRLanguage language, Object[] arguments) {
-        CompilerAsserts.neverPartOfCompilation();
-        Object body = arguments.length <= 1 ? RNull.instance : arguments[1];
-        Object argList = arguments.length == 0 ? RNull.instance : arguments[0];
-        ArrayList<RCodeBuilder.Argument<RSyntaxNode>> finalArgs = new ArrayList<>();
-        while (argList != RNull.instance) {
-            if (!(argList instanceof RPairList)) {
-                throw RError.error(RError.SHOW_CALLER, Message.BAD_FUNCTION_EXPR);
-            }
-            RPairList pl = (RPairList) argList;
-            String name = ((RSymbol) pl.getTag()).getName();
-            RSyntaxNode value = RASTUtils.createNodeForValue(pl.car()).asRSyntaxNode();
-            finalArgs.add(RCodeBuilder.argument(RSyntaxNode.LAZY_DEPARSE, name, value));
-            argList = pl.cdr();
-        }
-        RSyntaxNode function = RContext.getASTBuilder().function(language, RSyntaxNode.LAZY_DEPARSE, finalArgs, RASTUtils.createNodeForValue(body).asRSyntaxNode(), null);
-        return RDataFactory.createLanguage(Closure.createLanguageClosure(function.asRNode()));
-    }
-
     @TruffleBoundary
     private static RLanguage makeCall0(RSyntaxNode target, Object[] arguments, ArgumentsSignature signature) {
         RSyntaxNode[] args = new RSyntaxNode[arguments.length];
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/FunctionBuiltin.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/FunctionBuiltin.java
index 3909ffbb92..4f885fac2f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/FunctionBuiltin.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/FunctionBuiltin.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
@@ -22,23 +22,78 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base.infix;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_FRAME;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
+import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+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.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RExpression;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RPairList;
+import com.oracle.truffle.r.runtime.data.RPromise;
+import com.oracle.truffle.r.runtime.data.RSymbol;
+import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
+import com.oracle.truffle.r.runtime.nodes.RCodeBuilder.Argument;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-@RBuiltin(name = "function", kind = PRIMITIVE, parameterNames = {"x"}, behavior = READS_FRAME)
-public abstract class FunctionBuiltin extends RBuiltinNode.Arg1 {
+@RBuiltin(name = "function", kind = PRIMITIVE, nonEvalArgs = 1, parameterNames = {"args", "body"}, behavior = COMPLEX)
+public final class FunctionBuiltin extends RBuiltinNode.Arg2 {
 
     static {
         Casts.noCasts(FunctionBuiltin.class);
     }
 
-    @Specialization
-    protected Object doIt(@SuppressWarnings("unused") Object x) {
-        throw RInternalError.unimplemented();
+    @Child private CreateAndExecuteFunctionExpr createFunNode;
+
+    @Override
+    public Object execute(VirtualFrame frame, Object args, Object body) {
+        if (createFunNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            createFunNode = insert(new CreateAndExecuteFunctionExpr());
+        }
+        return createFunNode.execute(frame.materialize(), getRLanguage(), args, body);
+    }
+
+    public static FunctionBuiltin create() {
+        return new FunctionBuiltin();
+    }
+
+    protected static final class CreateAndExecuteFunctionExpr extends TruffleBoundaryNode {
+        @Child private FunctionExpressionNode funExprNode;
+
+        @TruffleBoundary
+        public Object execute(MaterializedFrame frame, TruffleRLanguage language, Object args, Object body) {
+            funExprNode = insert(createFunctionExpressionNode(getRLanguage(), args, body));
+            return funExprNode.execute(frame);
+        }
+    }
+
+    public static FunctionExpressionNode createFunctionExpressionNode(TruffleRLanguage language, Object args, Object body) {
+        List<Argument<RSyntaxNode>> finalArgs = RContext.getASTBuilder().getFunctionExprArgs(args);
+        return (FunctionExpressionNode) RContext.getASTBuilder().function(language, RSyntaxNode.LAZY_DEPARSE, finalArgs, RASTUtils.createNodeForValue(body).asRSyntaxNode(), null);
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
index f970469231..b682b1268a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
@@ -22,8 +22,10 @@
  */
 package com.oracle.truffle.r.nodes;
 
+import java.util.ArrayList;
 import java.util.List;
 
+import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.frame.FrameDescriptor;
@@ -53,12 +55,17 @@ import com.oracle.truffle.r.nodes.function.signature.MissingNode;
 import com.oracle.truffle.r.nodes.function.signature.QuoteNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.context.TruffleRLanguage;
 import com.oracle.truffle.r.runtime.data.REmpty;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.data.RShareable;
+import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.EvaluatedArgumentsVisitor;
 import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
@@ -190,6 +197,27 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
         return FunctionExpressionNode.create(source, callTarget);
     }
 
+    @Override
+    public ArrayList<Argument<RSyntaxNode>> getFunctionExprArgs(Object args) {
+        CompilerAsserts.neverPartOfCompilation();
+        if (!(args instanceof RPairList || args == RNull.instance)) {
+            throw RError.error(RError.SHOW_CALLER, Message.INVALID_FORMAL_ARG_LIST, "function");
+        }
+        ArrayList<Argument<RSyntaxNode>> finalArgs = new ArrayList<>();
+        Object argList = args;
+        while (argList != RNull.instance) {
+            if (!(argList instanceof RPairList)) {
+                throw RError.error(RError.SHOW_CALLER, Message.INVALID_FORMAL_ARG_LIST, "function");
+            }
+            RPairList pl = (RPairList) argList;
+            String name = ((RSymbol) pl.getTag()).getName();
+            RSyntaxNode value = RASTUtils.createNodeForValue(pl.car()).asRSyntaxNode();
+            finalArgs.add(RCodeBuilder.argument(RSyntaxNode.LAZY_DEPARSE, name, value));
+            argList = pl.cdr();
+        }
+        return finalArgs;
+    }
+
     @Override
     public RootCallTarget rootFunction(TruffleRLanguage language, SourceSection source, List<Argument<RSyntaxNode>> params, RSyntaxNode body, String name) {
         // Parse argument list
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
index 7bef648d8b..0b5398bfaa 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
@@ -28,6 +28,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.interop.TruffleObject;
@@ -35,6 +36,7 @@ import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
@@ -67,6 +69,8 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.interop.TruffleObjectConverter;
+import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
+import com.oracle.truffle.r.runtime.nodes.RCodeBuilder.Argument;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
@@ -521,6 +525,9 @@ public class RDeparse {
                 RSyntaxElement[] args = call.getSyntaxArguments();
                 if (lhs instanceof RSyntaxLookup) {
                     String symbol = ((RSyntaxLookup) lhs).getIdentifier();
+                    if ("function".equals(symbol)) {
+                        return visitFunctionFunction(args);
+                    }
                     RDeparse.Func func = RDeparse.getFunc(symbol);
                     if (func != null) {
                         PPInfo info = func.info;
@@ -675,10 +682,34 @@ public class RDeparse {
 
             @Override
             protected Void visit(RSyntaxFunction function) {
+                return visitFunctionExpr(function.getSyntaxSignature(), function.getSyntaxArgumentDefaults(), function.getSyntaxBody());
+            }
+
+            private Void visitFunctionExpr(ArgumentsSignature signature, RSyntaxElement[] argsDefaults, RSyntaxElement body) {
                 append("function(");
-                appendArgs(function.getSyntaxSignature(), function.getSyntaxArgumentDefaults(), 0, true);
+                appendArgs(signature, argsDefaults, 0, true);
                 append(") ");
-                appendFunctionBody(function.getSyntaxBody());
+                appendFunctionBody(body);
+                return null;
+            }
+
+            private Void visitFunctionFunction(RSyntaxElement[] args) {
+                if (args.length > 0 && !(args[0] instanceof RSyntaxConstant)) {
+                    throw RError.error(RError.SHOW_CALLER2, Message.BAD_FUNCTION_EXPR);
+                }
+                Object funArgsValue = args.length > 0 ? ((RSyntaxConstant) args[0]).getValue() : RNull.instance;
+                List<Argument<RSyntaxNode>> syntaxArgs = RContext.getASTBuilder().getFunctionExprArgs(funArgsValue);
+                String[] names = new String[syntaxArgs.size()];
+                RSyntaxNode[] values = new RSyntaxNode[syntaxArgs.size()];
+                for (int i = 0; i < syntaxArgs.size(); i++) {
+                    names[i] = syntaxArgs.get(i).name;
+                    values[i] = syntaxArgs.get(i).value;
+                }
+                Object body = args.length <= 1 ? RNull.instance : args[1];
+                if (!(body instanceof RSyntaxElement)) {
+                    body = RContext.getASTBuilder().constant(RSyntaxNode.SOURCE_UNAVAILABLE, body);
+                }
+                visitFunctionExpr(ArgumentsSignature.get(names), values, (RSyntaxElement) body);
                 return null;
             }
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index a0282f634e..9c04e55fe4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -944,6 +944,7 @@ public final class RError extends RuntimeException implements TruffleException {
         VERSION_N_NOT_SUPPORTED("version %d not supported"),
         ATOMIC_VECTOR_ARGUMENTS_ONLY("atomic vector arguments only"),
         MUST_BE_COMPLEX_MATRIX("'%s' must be a complex matrix"),
+        INVALID_FORMAL_ARG_LIST("invalid formal argument list for \"%s\""),
         SINGULAR_BACKSOLVE("singular matrix in 'backsolve'. First zero in diagonal [%d]");
 
         public final String message;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java
index 1071ca6c13..9d4415f7ab 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 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
@@ -32,6 +32,7 @@ import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.TruffleRLanguage;
+import com.oracle.truffle.r.runtime.data.RNull;
 
 /**
  * Implementers of this interface can be used to generate a representation of an R closure.
@@ -120,6 +121,13 @@ public interface RCodeBuilder<T> {
      */
     RootCallTarget rootFunction(TruffleRLanguage language, SourceSection source, List<Argument<T>> arguments, T body, String name);
 
+    /**
+     * Given a {@link com.oracle.truffle.r.runtime.data.RPairList} or {@link RNull}, this method
+     * creates a corresponding list of named arguments with default values if any, like if passed to
+     * the `function` expression.
+     */
+    List<Argument<RSyntaxNode>> getFunctionExprArgs(Object args);
+
     void setContext(CodeBuilderContext context);
 
     CodeBuilderContext getContext();
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index 1a21c8090a..440ac65414 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -5917,10 +5917,6 @@ foo()
 #call('function')
 function() NULL
 
-##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace#
-#call('function', 'a')
-Error: badly formed function expression
-
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace#
 #call('function', pairlist(a=1))
 function(a = 1) NULL
@@ -5949,6 +5945,13 @@ pairlist(a = )()
 #e <- substitute(a$b(c)); as.call(lapply(e, function(x) x))
 a$b(c)
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace#
+#invisible(call('function', 'a'))
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace#
+#length(call('function', 'a'))
+[1] 2
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#
 #typeof(as.call(list(substitute(graphics::par))))
 [1] "language"
@@ -28261,6 +28264,21 @@ Error: atomic vector arguments only
 #argv <- structure(list(pv = 0.200965994008331, digits = 3), .Names = c('pv',     'digits'));do.call('format.pval', argv)
 [1] "0.201"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_function.testFunctionFunction#
+#do.call('function', list(as.pairlist(list(x=4)), expression(x + 1)[[1]]))
+function (x = 4)
+x + 1
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_function.testFunctionFunction#Output.IgnoreErrorContext#
+#eval(call('function', 1, expression(x + 1)[[1]]))
+Error in eval(call("function", 1, expression(x + 1)[[1]])) :
+  invalid formal argument list for "function"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_function.testFunctionFunction#
+#eval(call('function', as.pairlist(list(x=4)), expression(x + 1)[[1]]))
+function (x = 4)
+x + 1
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_g.testg1#Output.IgnoreErrorContext#
 #argv <- list(1);g(argv[[1]]);
 Error in g(argv[[1]]) : could not find function "g"
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java
index 7b55239637..bc6fff85e3 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java
@@ -4,7 +4,7 @@
  * http://www.gnu.org/licenses/gpl-2.0.html
  *
  * Copyright (c) 2012-2014, Purdue University
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
@@ -68,7 +68,9 @@ public class TestBuiltin_ascall extends TestBase {
         assertEval(Output.IgnoreWhitespace, "e <- expression(function(a) b); as.call(list(e[[1]][[1]]))");
         assertEval("e <- expression(function(a) b); as.call(list(e[[1]][[2]]))");
         assertEval("call('foo')");
-        assertEval(Output.IgnoreWhitespace, "call('function', 'a')");
+        // Note: call('function', 'a') should not cause the exception, it should be the printing
+        assertEval(Output.IgnoreWhitespace, "invisible(call('function', 'a'))");
+        assertEval(Output.IgnoreWhitespace, "length(call('function', 'a'))");
         assertEval(Output.IgnoreWhitespace, "call('function', pairlist(a=1))");
         assertEval(Output.IgnoreWhitespace, "call('function', pairlist(a=1), 3)");
         assertEval(Output.IgnoreWhitespace, "call('function', pairlist(a=1), 5,3)");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_function.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_function.java
new file mode 100644
index 0000000000..eac9c3629d
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_function.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ * 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.test.builtins;
+
+import org.junit.Test;
+
+import com.oracle.truffle.r.test.TestBase;
+
+// Checkstyle: stop line length check
+public class TestBuiltin_function extends TestBase {
+    @Test
+    public void testFunctionFunction() {
+        assertEval(Output.IgnoreErrorContext, "eval(call('function', 1, expression(x + 1)[[1]]))");
+        assertEval("eval(call('function', as.pairlist(list(x=4)), expression(x + 1)[[1]]))");
+        assertEval("do.call('function', list(as.pairlist(list(x=4)), expression(x + 1)[[1]]))");
+    }
+}
-- 
GitLab