Browse Source

gh-894 Better out of line function support

Turn the internal functions into separate workflow items - which allows
the standard transformations to be applied to them without causing
issues.  Preparation for further work.

Signed-off-by: Gavin Halliday <gavin.halliday@lexisnexis.com>
Gavin Halliday 13 năm trước cách đây
mục cha
commit
9c55be9732

+ 2 - 1
ecl/hql/hqlattr.cpp

@@ -530,6 +530,7 @@ case no_unused53:
     case no_returnresult:
     case no_outputscalar:
     case no_evaluate_stmt:
+    case no_return_stmt:
     case no_setgraphloopresult:
     case no_skip:
     case no_assert:
@@ -606,7 +607,7 @@ case no_unused53:
     case no_persist_check:
 
     case no_unused1: case no_unused2: case no_unused3: case no_unused4: case no_unused5: case no_unused6:
-    case no_unused13: case no_unused14: case no_unused15: case no_unused16: case no_unused17: case no_unused18: case no_unused19:
+    case no_unused13: case no_unused14: case no_unused15: case no_unused17: case no_unused18: case no_unused19:
     case no_unused20: case no_unused21: case no_unused22: case no_unused23: case no_unused24: case no_unused25: case no_unused26: case no_unused27: case no_unused28: case no_unused29:
     case no_unused30: case no_unused31: case no_unused32: case no_unused33: case no_unused34: case no_unused35: case no_unused36: case no_unused37: case no_unused38: case no_unused39:
     case no_unused40: case no_unused41: case no_unused42: case no_unused43: case no_unused44: case no_unused45: case no_unused46: case no_unused47: case no_unused48: case no_unused49:

+ 20 - 1
ecl/hql/hqlexpr.cpp

@@ -916,6 +916,7 @@ const char *getOpString(node_operator op)
     case no_pat_case: return "CASE";
     case no_pat_nocase: return "NOCASE";
     case no_evaluate_stmt: return "EVALUATE";
+    case no_return_stmt: return "RETURN";
     case no_activetable: return "<Active>";
     case no_preload: return "PRELOAD";
     case no_createset: return "SET";
@@ -1086,7 +1087,7 @@ const char *getOpString(node_operator op)
     case no_debug_option_value: return "__DEBUG__";
 
     case no_unused1: case no_unused2: case no_unused3: case no_unused4: case no_unused5: case no_unused6:
-    case no_unused13: case no_unused14: case no_unused15: case no_unused16: case no_unused17: case no_unused18: case no_unused19:
+    case no_unused13: case no_unused14: case no_unused15: case no_unused17: case no_unused18: case no_unused19:
     case no_unused20: case no_unused21: case no_unused22: case no_unused23: case no_unused24: case no_unused25: case no_unused26: case no_unused27: case no_unused28: case no_unused29:
     case no_unused30: case no_unused31: case no_unused32: case no_unused33: case no_unused34: case no_unused35: case no_unused36: case no_unused37: case no_unused38: case no_unused39:
     case no_unused40: case no_unused41: case no_unused42: case no_unused43: case no_unused44: case no_unused45: case no_unused46: case no_unused47: case no_unused48: case no_unused49:
@@ -11052,6 +11053,24 @@ extern IHqlExpression *createExternalReference(_ATOM name, ITypeInfo *_type, Hql
     return CHqlExternal::makeExternalReference(name, _type, attributes)->closeExpr();
 }
 
+IHqlExpression * createExternalFuncdefFromInternal(IHqlExpression * funcdef)
+{
+    IHqlExpression * body = funcdef->queryChild(0);
+    HqlExprArray attrs;
+    unwindChildren(attrs, body, 1);
+
+    if (body->isPure())
+        attrs.append(*createAttribute(pureAtom));
+    if (body->getInfoFlags() & HEFaction)
+        attrs.append(*createAttribute(actionAtom));
+    if (body->getInfoFlags() & HEFcontextDependentException)
+        attrs.append(*createAttribute(contextSensitiveAtom));
+
+    ITypeInfo * returnType = funcdef->queryType()->queryChildType();
+    OwnedHqlExpr externalExpr = createExternalReference(funcdef->queryName(), LINK(returnType), attrs);
+    return replaceChild(funcdef, 0, externalExpr);
+}
+
 extern IHqlExpression* createValue(node_operator op, HqlExprArray& operands) {
     return CHqlExpression::makeExpression(op, NULL, operands);
 }

+ 2 - 1
ecl/hql/hqlexpr.hpp

@@ -342,7 +342,7 @@ enum _node_operator {
         no_hash32,
         no_hash64,
         no_crc,
-    no_unused16,
+        no_return_stmt,
         no_update,    
     no_unused17,
     no_unused18,
@@ -1191,6 +1191,7 @@ extern HQL_API IHqlExpression *createRecord();
 extern HQL_API IHqlExpression *createRecord(const HqlExprArray & fields);
 extern HQL_API IHqlExpression *createExternalReference(_ATOM name, ITypeInfo *_type, IHqlExpression *props);
 extern HQL_API IHqlExpression *createExternalReference(_ATOM name, ITypeInfo *_type, HqlExprArray & attributes);
+HQL_API IHqlExpression * createExternalFuncdefFromInternal(IHqlExpression * funcdef);
 extern HQL_API IHqlExpression *createBoundFunction(IErrorReceiver * errors, IHqlExpression *func, HqlExprArray & ownedActuals, HqlExprArray * functionCache, bool forceExpansion);
 extern HQL_API IHqlExpression *createReboundFunction(IHqlExpression *func, HqlExprArray & ownedActuals);
 extern HQL_API IHqlExpression *createTranslatedExternalCall(IErrorReceiver * errors, IHqlExpression *func, HqlExprArray &actuals);

+ 13 - 0
ecl/hql/hqlfold.cpp

@@ -5081,6 +5081,19 @@ IHqlExpression * CExprFolderTransformer::createTransformed(IHqlExpression * expr
                 return cloneAnnotationAndTransform(expr, result);
             break;
         }
+    case no_call:
+        {
+            //Ensure the bodies of out of line function calls are also folded.
+            IHqlExpression * funcDef = expr->queryDefinition();
+            Owned<IHqlExpression> newFuncDef = transform(funcDef);
+            if (funcDef != newFuncDef)
+            {
+                HqlExprArray children;
+                transformChildren(expr, children);
+                dft.setown(createReboundFunction(newFuncDef, children));
+            }
+            break;
+        }
     }
 
     if (!dft)

+ 13 - 0
ecl/hql/hqlutil.cpp

@@ -7378,3 +7378,16 @@ bool isSetWithUnknownElementSize(ITypeInfo * type)
     }
     return false;
 }
+
+IHqlExpression * replaceParameters(IHqlExpression * body, IHqlExpression * oldParams, IHqlExpression * newParams)
+{
+    HqlMapTransformer simpleTransformer;
+    ForEachChild(i, oldParams)
+    {
+        IHqlExpression * from = oldParams->queryChild(i);
+        IHqlExpression * to = newParams->queryChild(i);
+        simpleTransformer.setMapping(from, to);
+    }
+
+    return simpleTransformer.transformRoot(body);
+}

+ 1 - 0
ecl/hql/hqlutil.hpp

@@ -620,6 +620,7 @@ extern HQL_API IHqlExpression * createConstantRowExpr(IHqlExpression * transform
 extern HQL_API IHqlExpression * createConstantNullRowExpr(IHqlExpression * record);
 extern HQL_API IHqlExpression * ensureOwned(IHqlExpression * expr);
 extern HQL_API bool isSetWithUnknownElementSize(ITypeInfo * type);
+extern HQL_API IHqlExpression * replaceParameters(IHqlExpression * body, IHqlExpression * oldParams, IHqlExpression * newParams);
 
 //In hqlgram2.cpp
 extern HQL_API IPropertyTree * queryEnsureArchiveModule(IPropertyTree * archive, const char * name, IHqlScope * rScope);

+ 43 - 64
ecl/hqlcpp/hqlcpp.cpp

@@ -138,6 +138,31 @@ void SubStringInfo::bindToFrom(HqlCppTranslator & translator, BuildCtx & ctx)
 
 //---------------------------------------------------------------------------
 
+WorkflowItem::WorkflowItem(IHqlExpression * _function) : wfid(0), function(_function)
+{
+    IHqlExpression * body = function->queryChild(0);
+    assertex(body->getOperator() == no_outofline);
+    IHqlExpression * ecl = body->queryChild(0);
+    exprs.append(*createValue(no_return_stmt, makeVoidType(), LINK(ecl)));
+}
+
+IHqlExpression * WorkflowItem::getFunction() const
+{
+    IHqlExpression * body = function->queryChild(0);
+    unsigned max = exprs.ordinality();
+    assertex(max);
+    LinkedHqlExpr newecl = exprs.item(max-1).queryChild(0);
+    for (unsigned i=max-1; i--; )
+    {
+        IHqlExpression * cur = &exprs.item(i);
+        newecl.setown(createCompound(LINK(cur), newecl.getClear()));
+    }
+    OwnedHqlExpr newBody = replaceChild(body, 0, newecl);
+    return replaceChild(function, 0, newBody);
+}
+
+//---------------------------------------------------------------------------
+
 IHqlExpression * DatasetReference::querySelector() const
 {
     if (side == no_none)
@@ -5416,68 +5441,18 @@ void HqlCppTranslator::normalizeBoundExpr(BuildCtx & ctx, CHqlBoundExpr & bound)
 }
 
 
-static IHqlExpression * mapInternalFunctionParameters(IHqlExpression * expr)
-{
-    switch (expr->getOperator())
-    {
-    case no_sortlist:
-        {
-            HqlExprArray args;
-            ForEachChild(i, expr)
-                args.append(*mapInternalFunctionParameters(expr->queryChild(i)));
-            return cloneOrLink(expr, args);
-        }
-    case no_param:
-        {
-            ITypeInfo * type = expr->queryType();
-            //String parameters need to be passed as c++ const string parameters
-            switch (type->getTypeCode())
-            {
-            case type_string:
-            case type_varstring:
-            case type_data:
-            case type_qstring:
-            case type_unicode:
-            case type_utf8:
-            case type_varunicode:
-                if (!expr->hasProperty(constAtom))
-                    return appendOwnedOperand(expr, createAttribute(constAtom));
-                break;
-            }
-            break;
-        }
-    }
-
-    return LINK(expr);
-}
-
-
 IHqlExpression * HqlCppTranslator::doBuildInternalFunction(IHqlExpression * funcdef)
 {
     unsigned match = internalFunctions.find(*funcdef);
     if (match != NotFound)
         return LINK(&internalFunctionExternals.item(match));
 
-    StringBuffer funcname;
-    funcname.append("user").append(internalFunctions.ordinality()+1);
-    if (options.debugGeneratedCpp)
-        funcname.append("_").append(funcdef->queryName()).toLowerCase();
-
-    HqlExprArray attrs;
-    attrs.append(*createLocalAttribute());
-    attrs.append(*createExprAttribute(entrypointAtom, createConstant(funcname)));
-
-    HqlExprArray funcdefArgs;
-    ITypeInfo * returnType = funcdef->queryType()->queryChildType();
-    funcdefArgs.append(*createExternalReference(funcdef->queryName(), LINK(returnType), attrs));
-    funcdefArgs.append(*mapInternalFunctionParameters(queryFunctionParameters(funcdef)));
-    unwindChildren(funcdefArgs, funcdef, 1);
-    OwnedHqlExpr externalFuncdef = funcdef->clone(funcdefArgs);
+    OwnedHqlExpr externalFuncdef = createExternalFuncdefFromInternal(funcdef);
 
     internalFunctions.append(*LINK(funcdef));
     internalFunctionExternals.append(*LINK(externalFuncdef));
 
-    buildFunctionDefinition(externalFuncdef, funcdef);
+    buildFunctionDefinition(funcdef);
     return LINK(externalFuncdef);
 }
 
@@ -11229,12 +11204,12 @@ void HqlCppTranslator::doBuildUserFunctionReturn(BuildCtx & ctx, ITypeInfo * typ
 }
 
 
-void HqlCppTranslator::buildFunctionDefinition(IHqlExpression * externalDef, IHqlExpression * funcdef)
+void HqlCppTranslator::buildFunctionDefinition(IHqlExpression * funcdef)
 {
-    IHqlExpression *body = funcdef->queryChild(0);
+    IHqlExpression * outofline = funcdef->queryChild(0);
     ITypeInfo * returnType = funcdef->queryType()->queryChildType();
-    if (body->getOperator() == no_outofline)
-        body = body->queryChild(0);
+    assertex(outofline->getOperator() == no_outofline);
+    IHqlExpression * bodyCode = outofline->queryChild(0);
 
     StringBuffer s;
     BuildCtx funcctx(*code, helperAtom);
@@ -11244,19 +11219,19 @@ void HqlCppTranslator::buildFunctionDefinition(IHqlExpression * externalDef, IHq
         OwnedHqlExpr pass = getSizetConstant(cppIndexNextActivity(inChildActivity));
         funcctx.addGroupPass(pass);
     }
-    expandFunctionPrototype(s, externalDef);
+    expandFunctionPrototype(s, funcdef);
 
-    if (body->getOperator() == no_cppbody)
+    if (bodyCode->getOperator() == no_cppbody)
     {
         if (!allowEmbeddedCpp())
             throwError(HQLERR_EmbeddedCppNotAllowed);
 
-        IHqlExpression * location = queryLocation(body);
+        IHqlExpression * location = queryLocation(bodyCode);
         const char * locationFilename = location ? location->querySourcePath()->str() : NULL;
         unsigned startLine = location ? location->getStartLine() : 0;
-        IHqlExpression * cppBody = body->queryChild(0);
+        IHqlExpression * cppBody = bodyCode->queryChild(0);
         if (cppBody->getOperator() == no_record)
-            cppBody = body->queryChild(1);
+            cppBody = bodyCode->queryChild(1);
 
         StringBuffer text;
         cppBody->queryValue()->getStringValue(text);
@@ -11302,9 +11277,13 @@ void HqlCppTranslator::buildFunctionDefinition(IHqlExpression * externalDef, IHq
     else
     {
         funcctx.addQuotedCompound(s);
-        OwnedHqlExpr newBody = replaceInlineParameters(funcdef, body);
-        newBody.setown(foldHqlExpression(newBody));
-        doBuildUserFunctionReturn(funcctx, returnType, newBody);
+        //MORE: Need to work out how to handle functions that require the context.
+        //Need to create a class instead.
+        assertex(!outofline->hasProperty(contextAtom));
+
+        OwnedHqlExpr newCode = replaceInlineParameters(funcdef, bodyCode);
+        newCode.setown(foldHqlExpression(newCode));
+        doBuildUserFunctionReturn(funcctx, returnType, newCode);
     }
 }
 

+ 7 - 3
ecl/hqlcpp/hqlcpp.ipp

@@ -438,13 +438,17 @@ class WorkflowItem : public CInterface
 {
     friend class WorkflowTransformer;
 public:
-    WorkflowItem(unsigned _wfid) : wfid(_wfid) {}
+    WorkflowItem(unsigned _wfid) : wfid(_wfid) { }
+    WorkflowItem(IHqlExpression * _function);
     IMPLEMENT_IINTERFACE;
 
-    unsigned queryWfid() { return wfid; }
+    bool isFunction() const { return function != NULL; }
+    IHqlExpression * getFunction() const;
+    unsigned queryWfid() const { return wfid; }
     HqlExprArray & queryExprs() { return exprs; }
 
 private:
+    LinkedHqlExpr function;
     unsigned wfid;
     HqlExprArray exprs;
     UnsignedArray dependencies;
@@ -1084,7 +1088,7 @@ public:
 
     bool expandFunctionPrototype(StringBuffer & s, IHqlExpression * funcdef);
     void expandFunctionPrototype(BuildCtx & ctx, IHqlExpression * funcdef);
-    void buildFunctionDefinition(IHqlExpression * externalDef, IHqlExpression * funcdef);
+    void buildFunctionDefinition(IHqlExpression * funcdef);
     void assignAndCast(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & expr);
     void assignCastUnknownLength(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & pure);
     void assignSwapInt(BuildCtx & ctx, ITypeInfo * to, const CHqlBoundTarget & target, CHqlBoundExpr & pure);

+ 34 - 0
ecl/hqlcpp/hqlcpputil.cpp

@@ -412,3 +412,37 @@ IHqlExpression * projectCreateSetDataset(IHqlExpression * expr)
     return LINK(expr);
 }
 
+IHqlExpression * mapInternalFunctionParameters(IHqlExpression * expr)
+{
+    switch (expr->getOperator())
+    {
+    case no_sortlist:
+        {
+            HqlExprArray args;
+            ForEachChild(i, expr)
+                args.append(*mapInternalFunctionParameters(expr->queryChild(i)));
+            return cloneOrLink(expr, args);
+        }
+    case no_param:
+        {
+            ITypeInfo * type = expr->queryType();
+            //String parameters need to be passed as c++ const string parameters
+            switch (type->getTypeCode())
+            {
+            case type_string:
+            case type_varstring:
+            case type_data:
+            case type_qstring:
+            case type_unicode:
+            case type_utf8:
+            case type_varunicode:
+                if (!expr->hasProperty(constAtom))
+                    return appendOwnedOperand(expr, createAttribute(constAtom));
+                break;
+            }
+            break;
+        }
+    }
+
+    return LINK(expr);
+}

+ 1 - 0
ecl/hqlcpp/hqlcpputil.hpp

@@ -50,6 +50,7 @@ extern IHqlExpression * ensurePositiveOrZeroInt64(IHqlExpression * expr);
 extern void getOutputLibraryName(SCMStringBuffer & libraryName, IConstWorkUnit * wu);
 extern bool canCreateTemporary(IHqlExpression * expr);
 extern IHqlExpression * projectCreateSetDataset(IHqlExpression * createsetExpr);
+extern IHqlExpression * mapInternalFunctionParameters(IHqlExpression * expr);
 
 //Common types and expressions...
 extern ITypeInfo * boolType;

+ 48 - 18
ecl/hqlcpp/hqlhtcpp.cpp

@@ -5475,15 +5475,35 @@ bool HqlCppTranslator::buildCode(IHqlExpression * exprlist, const char * embedde
         if (insideLibrary())
         {
             //always do these checks for consistency
-            assertex(workflow.ordinality() == 1);
-            HqlExprArray & exprs = workflow.item(0).queryExprs();
-            assertex(exprs.ordinality() == 1);
-            IHqlExpression & expr = exprs.item(0);
-            assertex(expr.getOperator() == no_thor);
+            OwnedHqlExpr graph;
+            ForEachItemIn(i, workflow)
+            {
+                WorkflowItem & cur = workflow.item(i);
+                if (!cur.isFunction())
+                {
+                    assertex(!graph);
+                    HqlExprArray & exprs = cur.queryExprs();
+                    assertex(exprs.ordinality() == 1);
+                    graph.set(&exprs.item(0));
+                    assertex(graph->getOperator() == no_thor);
+                }
+            }
+
+            //More: this should be cleaned up - with a flag in the workflow items to indicate a library graph instead...
             if (embeddedLibraryName)
             {
+                ForEachItemIn(i, workflow)
+                {
+                    WorkflowItem & cur = workflow.item(i);
+                    if (cur.isFunction())
+                    {
+                        OwnedHqlExpr function = cur.getFunction();
+                        buildFunctionDefinition(function);
+                    }
+                }
+
                 BuildCtx ctx(*code, goAtom);
-                buildLibraryGraph(ctx, &expr, embeddedLibraryName);
+                buildLibraryGraph(ctx, graph, embeddedLibraryName);
             }
             else
                 buildWorkflow(workflow);
@@ -16906,6 +16926,7 @@ void HqlCppTranslator::buildWorkflow(WorkflowArray & workflow)
     {
         WorkflowItem & action = workflow.item(idx);
         HqlExprArray & exprs = action.queryExprs();
+        unsigned wfid = action.queryWfid();
 
         bool isEmpty = exprs.ordinality() == 0;
         if (exprs.ordinality() == 1 && (exprs.item(0).getOperator() == no_workflow_action))
@@ -16913,22 +16934,29 @@ void HqlCppTranslator::buildWorkflow(WorkflowArray & workflow)
 
         if (!isEmpty)
         {
-            OwnedHqlExpr expr = createActionList(action.queryExprs());
-            unsigned wfid = action.queryWfid();
-
-            IHqlExpression * persistAttr = expr->queryProperty(_workflowPersist_Atom);
-            if (persistAttr)
+            if (action.isFunction())
             {
-                if (!options.freezePersists)
+                OwnedHqlExpr function = action.getFunction();
+                buildFunctionDefinition(function);
+            }
+            else
+            {
+                OwnedHqlExpr expr = createActionList(action.queryExprs());
+
+                IHqlExpression * persistAttr = expr->queryProperty(_workflowPersist_Atom);
+                if (persistAttr)
                 {
-                    HqlExprArray args2;
-                    unwindChildren(args2, expr);
-                    OwnedHqlExpr setResult = createSetResult(args2);
-                    buildWorkflowItem(switchctx, switchStmt, wfid, setResult);
+                    if (!options.freezePersists)
+                    {
+                        HqlExprArray args2;
+                        unwindChildren(args2, expr);
+                        OwnedHqlExpr setResult = createSetResult(args2);
+                        buildWorkflowItem(switchctx, switchStmt, wfid, setResult);
+                    }
                 }
+                else
+                    buildWorkflowItem(switchctx, switchStmt, wfid, expr);
             }
-            else
-                buildWorkflowItem(switchctx, switchStmt, wfid, expr);
         }
     }
 
@@ -17916,6 +17944,8 @@ static bool needsRealThor(IHqlExpression *expr, unsigned flags)
 
     case no_ensureresult:
     case no_setresult:
+    case no_evaluate_stmt:
+    case no_return_stmt:
         {
             IHqlExpression * child0 = expr->queryChild(0);
             if (!child0->queryType()->isScalar())

+ 73 - 4
ecl/hqlcpp/hqlttcpp.cpp

@@ -5008,6 +5008,7 @@ WorkflowTransformer::WorkflowTransformer(IWorkUnit * _wu, HqlCppTranslator & _tr
 : NewHqlTransformer(workflowTransformerInfo), wu(_wu), translator(_translator), wfidCount(0)
 {
     trivialStoredWfid = 0;
+    nextInternalFunctionId = 0;
     onceWfid = 0;
     combineAllStored = translator.queryOptions().combineAllStored;
     combineTrivialStored = translator.queryOptions().combineTrivialStored;
@@ -5503,6 +5504,59 @@ IHqlExpression * WorkflowTransformer::extractCommonWorkflow(IHqlExpression * exp
     return getValue.getClear();
 }
 
+IHqlExpression * WorkflowTransformer::transformInternalFunction(IHqlExpression * newFuncDef)
+{
+    IHqlExpression * body = newFuncDef->queryChild(0);
+    if (body->getOperator() != no_outofline)
+        return LINK(newFuncDef);
+
+    IHqlExpression * ecl = body->queryChild(0);
+
+    StringBuffer funcname;
+    funcname.append("user").append(++nextInternalFunctionId);
+    if (translator.queryOptions().debugGeneratedCpp)
+        funcname.append("_").append(newFuncDef->queryName()).toLowerCase();
+    OwnedHqlExpr funcNameExpr = createConstant(funcname);
+
+    IHqlExpression * formals = newFuncDef->queryChild(1);
+    OwnedHqlExpr newFormals = mapInternalFunctionParameters(formals);
+
+    HqlExprArray bodyArgs;
+    bodyArgs.append(*replaceParameters(ecl, formals, newFormals));
+    unwindChildren(bodyArgs, body, 1);
+    bodyArgs.append(*createLocalAttribute());
+    bodyArgs.append(*createExprAttribute(entrypointAtom, LINK(funcNameExpr)));
+    OwnedHqlExpr newBody = body->clone(bodyArgs);
+    inheritDependencies(newBody);
+
+    HqlExprArray funcdefArgs;
+    funcdefArgs.append(*LINK(newBody));
+    funcdefArgs.append(*LINK(newFormals));
+    unwindChildren(funcdefArgs, newFuncDef, 2);
+    OwnedHqlExpr namedFuncDef = newFuncDef->clone(funcdefArgs);
+    inheritDependencies(namedFuncDef);
+
+    if (ecl->getOperator() == no_cppbody)
+        return namedFuncDef.getClear();
+
+    WorkflowItem * item = new WorkflowItem(namedFuncDef);
+    workflowOut->append(*item);
+    return createExternalFuncdefFromInternal(namedFuncDef);
+}
+
+IHqlExpression * WorkflowTransformer::transformInternalCall(IHqlExpression * transformed)
+{
+    IHqlExpression * funcDef = transformed->queryDefinition();
+    Owned<IHqlExpression> newFuncDef = transform(funcDef);
+
+    HqlExprArray paramters;
+    unwindChildren(paramters, transformed);
+    OwnedHqlExpr rebound = createReboundFunction(newFuncDef, paramters);
+    inheritDependencies(rebound);
+    return rebound.getClear();
+}
+
+
 IHqlExpression * WorkflowTransformer::createTransformed(IHqlExpression * expr)
 {
     //Could short-circuit if doesn't contain workflow, but it also modifies outputs/buildindex...
@@ -5611,6 +5665,12 @@ IHqlExpression * WorkflowTransformer::createTransformed(IHqlExpression * expr)
             }
             break;
         }
+    case no_funcdef:
+        transformed.setown(transformInternalFunction(transformed));
+        break;
+    case no_call:
+        transformed.setown(transformInternalCall(transformed));
+        break;
     }
 
     return extractCommonWorkflow(expr, transformed);
@@ -6024,7 +6084,7 @@ void WorkflowTransformer::percolateScheduledIds(WorkflowArray & workflow)
     {
         WorkflowItem & cur = workflow.item(i);
         Owned<IWorkflowItem> wf = lookupWorkflowItem(cur.queryWfid());
-        if (wf->isScheduledNow())
+        if (wf && wf->isScheduledNow())
         {
             ForEachItemIn(i2, cur.dependencies)
             {
@@ -11832,9 +11892,18 @@ bool HqlCppTranslator::transformGraphForGeneration(IHqlExpression * query, Workf
 
     if (outputLibrary && workflow.ordinality() > 1)
     {
-        SCMStringBuffer libraryName;
-        getOutputLibraryName(libraryName, wu());
-        throwError2(HQLERR_LibraryCannotContainWorkflow, libraryName.str(), "");
+        unsigned cnt = 0;
+        ForEachItemIn(i, workflow)
+        {
+            if (!workflow.item(i).isFunction())
+                cnt++;
+        }
+        if (cnt > 1)
+        {
+            SCMStringBuffer libraryName;
+            getOutputLibraryName(libraryName, wu());
+            throwError2(HQLERR_LibraryCannotContainWorkflow, libraryName.str(), "");
+        }
     }
 
     {

+ 4 - 0
ecl/hqlcpp/hqlttcpp.ipp

@@ -459,6 +459,9 @@ protected:
     IHqlExpression *          extractWorkflow(IHqlExpression * untransformed, IHqlExpression * transformed);
     IHqlExpression *          extractClusterWorkflow(IHqlExpression * expr);
     IHqlExpression *          extractCommonWorkflow(IHqlExpression * expr, IHqlExpression * transformed);
+
+    IHqlExpression *          transformInternalCall(IHqlExpression * transformed);
+    IHqlExpression *          transformInternalFunction(IHqlExpression * transformed);
     
     IHqlExpression *          createCompoundWorkflow(IHqlExpression * expr);
     IHqlExpression *          createIfWorkflow(IHqlExpression * expr);
@@ -512,6 +515,7 @@ protected:
     HqlExprCopyArray          activeLocations;
     unsigned                  trivialStoredWfid;
     unsigned                  onceWfid;
+    unsigned                  nextInternalFunctionId;
     HqlExprArray              trivialStoredExprs;
     HqlExprArray              onceExprs;
     bool                      combineAllStored;