Sfoglia il codice sorgente

HPCC-894 Improve support for out of line functions

Signed-off-by: Gavin Halliday <gavin.halliday@lexisnexis.com>
Gavin Halliday 10 anni fa
parent
commit
a7dcdc188a

+ 6 - 5
common/deftype/deftype.cpp

@@ -1947,17 +1947,18 @@ extern DEFTYPE_API ITypeInfo *makeGroupedTableType(ITypeInfo *basetype)
     return commonUpType(new CGroupedTableTypeInfo(basetype));
 }
 
-extern DEFTYPE_API ITypeInfo *makeFunctionType(ITypeInfo *basetype, IInterface * parameters, IInterface * defaults)
+extern DEFTYPE_API ITypeInfo *makeFunctionType(ITypeInfo *basetype, IInterface * parameters, IInterface * defaults, IInterface * attrs)
 {
     assertex(basetype->getTypeCode() != type_function); // not just yet anyway
     if (!basetype || !parameters)
     {
-        basetype->Release();
-        parameters->Release();
-        defaults->Release();
+        ::Release(basetype);
+        ::Release(parameters);
+        ::Release(defaults);
+        ::Release(attrs);
         throwUnexpected();
     }
-    return commonUpType(new CFunctionTypeInfo(basetype, parameters, defaults));
+    return commonUpType(new CFunctionTypeInfo(basetype, parameters, defaults, attrs));
 }
 
 /* In basetype: linked. Return: linked */

+ 4 - 3
common/deftype/deftype.hpp

@@ -212,8 +212,9 @@ private:
 
 interface IFunctionTypeExtra : public IInterface
 {
-    virtual IInterface * queryParameters() = 0;
-    virtual IInterface * queryDefaults() = 0;
+    virtual IInterface * queryParameters() const = 0;
+    virtual IInterface * queryDefaults() const = 0;
+    virtual IInterface * queryAttributes() const = 0;
 };
 
 interface IEnumeratedTypeBuilder : public IInterface
@@ -254,7 +255,7 @@ extern DEFTYPE_API ITypeInfo *makeRowType(ITypeInfo *basetype);
 extern DEFTYPE_API ITypeInfo *makeSetType(ITypeInfo *basetype);
 extern DEFTYPE_API ITypeInfo *makeTransformType(ITypeInfo *basetype);
 extern DEFTYPE_API ITypeInfo *makeSortListType(ITypeInfo *basetype);
-extern DEFTYPE_API ITypeInfo *makeFunctionType(ITypeInfo *basetype, IInterface * args, IInterface * defaults);
+extern DEFTYPE_API ITypeInfo *makeFunctionType(ITypeInfo *basetype, IInterface * args, IInterface * defaults, IInterface * attrs);
 
 extern DEFTYPE_API ITypeInfo * makePointerType(ITypeInfo * basetype);
 extern DEFTYPE_API ITypeInfo * makeArrayType(ITypeInfo * basetype, unsigned size=0);

+ 6 - 4
common/deftype/deftype.ipp

@@ -773,9 +773,10 @@ class CFunctionTypeInfo : public CBasedTypeInfo, implements IFunctionTypeExtra
 private:
     Owned<IInterface> parameters;
     Owned<IInterface> defaults;
+    Owned<IInterface> attrs;
 public:
-    CFunctionTypeInfo(ITypeInfo * _basetype, IInterface * _parameters, IInterface * _defaults) 
-        : CBasedTypeInfo(_basetype, UNKNOWN_LENGTH), parameters(_parameters), defaults(_defaults) 
+    CFunctionTypeInfo(ITypeInfo * _basetype, IInterface * _parameters, IInterface * _defaults, IInterface *_attrs)
+        : CBasedTypeInfo(_basetype, UNKNOWN_LENGTH), parameters(_parameters), defaults(_defaults), attrs(_attrs)
     {}
     IMPLEMENT_IINTERFACE_USING(CBasedTypeInfo)
 
@@ -791,8 +792,9 @@ public:
     virtual bool equals(const CTypeInfo & other) const;
 
 //IFunctionTypeExtra
-    virtual IInterface * queryParameters() { return parameters; }
-    virtual IInterface * queryDefaults() { return defaults; }
+    virtual IInterface * queryParameters() const { return parameters; }
+    virtual IInterface * queryDefaults() const { return defaults; }
+    virtual IInterface * queryAttributes() const { return attrs; }
 };
 
 

+ 21 - 6
ecl/hql/hqlexpr.cpp

@@ -158,6 +158,7 @@ static IHqlExpression * cachedNullRecord;
 static IHqlExpression * cachedNullRowRecord;
 static IHqlExpression * cachedOne;
 static IHqlExpression * cachedLocalAttribute;
+static IHqlExpression * cachedContextAttribute;
 static IHqlExpression * constantTrue;
 static IHqlExpression * constantFalse;
 static IHqlExpression * defaultSelectorSequenceExpr;
@@ -238,6 +239,7 @@ MODULE_INIT(INIT_PRIORITY_HQLINTERNAL)
     cachedNullRowRecord = createRecord(nonEmptyAttr);
     cachedOne = createConstant(1);
     cachedLocalAttribute = createAttribute(localAtom);
+    cachedContextAttribute = createAttribute(contextAtom);
     constantTrue = createConstant(createBoolValue(true));
     constantFalse = createConstant(createBoolValue(false));
     defaultSelectorSequenceExpr = createAttribute(_selectorSequence_Atom);
@@ -271,6 +273,7 @@ MODULE_EXIT()
     constantFalse->Release();
     constantTrue->Release();
     blank->Release();
+    cachedContextAttribute->Release();
     cachedLocalAttribute->Release();
     cachedOne->Release();
     cachedActiveTableExpr->Release();
@@ -7790,13 +7793,27 @@ IHqlExpression * createFunctionDefinition(IIdAtom * id, IHqlExpression * value,
     return createFunctionDefinition(id, args);
 }
 
+bool functionBodyUsesContext(IHqlExpression * body)
+{
+    //All functions are assumed to require the context, unless it is an external c++ function without a context attr
+    switch (body->getOperator())
+    {
+    case no_external:
+        return (body->queryAttribute(contextAtom) != NULL);
+    default:
+        return true;
+    }
+}
+
 IHqlExpression * createFunctionDefinition(IIdAtom * id, HqlExprArray & args)
 {
     IHqlExpression * body = &args.item(0);
     IHqlExpression * formals = &args.item(1);
     IHqlExpression * defaults = args.isItem(2) ? &args.item(2) : NULL;
+    OwnedHqlExpr attrs;
+    if (functionBodyUsesContext(body))
+        attrs.set(cachedContextAttribute);
 
-#if 1
     //This is a bit of a waste of time, but need to improve assignableFrom for a function type to ignore the uids.
     QuickExpressionReplacer defaultReplacer;
     HqlExprArray normalized;
@@ -7813,11 +7830,7 @@ IHqlExpression * createFunctionDefinition(IIdAtom * id, HqlExprArray & args)
     OwnedHqlExpr newFormals = formals->clone(normalized);
     OwnedHqlExpr newDefaults = defaults ? defaultReplacer.transform(defaults) : NULL;
 
-    ITypeInfo * type = makeFunctionType(body->getType(), newFormals.getClear(), newDefaults.getClear());
-#else
-    ITypeInfo * type = makeFunctionType(body->getType(), LINK(formals), LINK(defaults));
-#endif
-
+    ITypeInfo * type = makeFunctionType(body->getType(), newFormals.getClear(), newDefaults.getClear(), attrs.getClear());
     return createNamedValue(no_funcdef, type, id, args);
 }
 
@@ -12050,6 +12063,8 @@ IHqlExpression * createExternalFuncdefFromInternal(IHqlExpression * funcdef)
         attrs.append(*createAttribute(actionAtom));
     if (body->getInfoFlags() & HEFcontextDependentException)
         attrs.append(*createAttribute(contextSensitiveAtom));
+    if (functionBodyUsesContext(body))
+        attrs.append(*LINK(cachedContextAttribute));
 
     ITypeInfo * returnType = funcdef->queryType()->queryChildType();
     OwnedHqlExpr externalExpr = createExternalReference(funcdef->queryId(), LINK(returnType), attrs);

+ 1 - 0
ecl/hql/hqlexpr.hpp

@@ -1806,6 +1806,7 @@ inline int boolToInt(bool x)                    { return x ? 1 : 0; }
 extern HQL_API IHqlExpression * createFunctionDefinition(IIdAtom * name, IHqlExpression * value, IHqlExpression * parms, IHqlExpression * defaults, IHqlExpression * attrs);
 extern HQL_API IHqlExpression * createFunctionDefinition(IIdAtom * name, HqlExprArray & args);
 extern HQL_API IHqlExpression * queryNonDelayedBaseAttribute(IHqlExpression * expr);
+extern HQL_API bool functionBodyUsesContext(IHqlExpression * body);
 
 #define NO_AGGREGATE        \
          no_count:          \

+ 6 - 13
ecl/hql/hqlgram2.cpp

@@ -890,15 +890,7 @@ IHqlExpression * HqlGram::convertToOutOfLineFunction(const ECLlocation & errpos,
     if (expr->getOperator() != no_outofline)
     {
         if (queryParametered())
-        {
-            OwnedHqlExpr mapped = convertWorkflowToImplicitParmeters(defineScopes.tos().activeParameters, defineScopes.tos().activeDefaults, expr);
-            if (containsWorkflow(mapped))
-            {
-                reportError(ERR_USER_FUNC_NO_WORKFLOW, errpos, "Out of line user functions cannot contain workflow/stored");
-                return mapped.getClear();
-            }
-            return createWrapper(no_outofline, mapped.getClear());
-        }
+            return createWrapper(no_outofline, LINK(expr));
     }
     return LINK(expr);
 }
@@ -996,7 +988,6 @@ IHqlExpression * HqlGram::processEmbedBody(const attribute & errpos, IHqlExpress
                 }
             }
         }
-
         return createWrapper(no_outofline, result->queryType(), args);
     }
     return result.getClear();
@@ -1674,8 +1665,10 @@ IHqlExpression * HqlGram::forceEnsureExprType(IHqlExpression * expr, ITypeInfo *
     //or the out of line node should be added much later.
     if (expr->getOperator() == no_outofline)
     {
-        OwnedHqlExpr ret = forceEnsureExprType(expr->queryChild(0), type);
-        return createWrapper(no_outofline, LINK(ret));
+        HqlExprArray args;
+        args.append(*forceEnsureExprType(expr->queryChild(0), type));
+        unwindChildren(args, expr, 1);
+        return expr->clone(args);
     }
         
     OwnedHqlExpr ret = ensureExprType(expr, type);
@@ -6076,7 +6069,7 @@ void HqlGram::addFunctionParameter(const attribute & errpos, IIdAtom * name, ITy
 
     HqlExprArray attrs;
     endList(attrs);
-    Owned<ITypeInfo> funcType = makeFunctionType(type, LINK(formals), defaults.getClear());
+    Owned<ITypeInfo> funcType = makeFunctionType(type, LINK(formals), defaults.getClear(), NULL);
     addActiveParameterOwn(errpos, createParameter(name, nextParameterIndex(), LINK(funcType), attrs), defValue);
 }
 

+ 0 - 39
ecl/hql/hqltrans.cpp

@@ -4632,45 +4632,6 @@ void verifySplitConsistency(IHqlExpression * expr)
 
 //---------------------------------------------------------------------------
 
-static HqlTransformerInfo globalToParameterTransformerInfo("GlobalToParameterTransformer");
-class GlobalToParameterTransformer : public QuickHqlTransformer
-{
-public:
-    GlobalToParameterTransformer(HqlExprArray & _parameters, HqlExprArray & _defaults) 
-        : QuickHqlTransformer(globalToParameterTransformerInfo, NULL), parameters(_parameters), defaults(_defaults)
-    {}
-
-    virtual IHqlExpression * createTransformedBody(IHqlExpression * expr);
-
-protected:
-    HqlExprArray & parameters;
-    HqlExprArray & defaults;
-};
-
-IHqlExpression * GlobalToParameterTransformer::createTransformedBody(IHqlExpression * expr)
-{
-    if (expr->getOperator() != no_colon)
-        return QuickHqlTransformer::createTransformedBody(expr);
-
-    StringBuffer paramName;
-    paramName.append("_implicit_hidden_").append(parameters.ordinality());
-    HqlExprArray attrs;
-    attrs.append(*createAttribute(_hidden_Atom));
-    IHqlExpression * param = createParameter(createIdAtom(paramName.str()), parameters.ordinality(), expr->getType(), attrs);
-    parameters.append(*param);
-    defaults.append(*LINK(expr));
-    return LINK(param);
-}
-
-IHqlExpression * convertWorkflowToImplicitParmeters(HqlExprArray & parameters, HqlExprArray & defaults, IHqlExpression * expr)
-{
-    GlobalToParameterTransformer transformer(parameters, defaults);
-    return transformer.transform(expr);
-}
-
-
-//---------------------------------------------------------------------------
-
 static HqlTransformerInfo createRowSelectorExpanderInfo("CreateRowSelectorExpander");
 class CreateRowSelectorExpander : public NewHqlTransformer
 {

+ 46 - 3
ecl/hql/hqlutil.cpp

@@ -2645,6 +2645,49 @@ void checkSelectConsistency(IHqlExpression * expr)
 
 //---------------------------------------------------------------------------
 
+static HqlTransformerInfo parameterDependencyCheckerInfo("ParameterDependencyChecker");
+class ParameterDependencyChecker  : public NewHqlTransformer
+{
+public:
+    ParameterDependencyChecker() : NewHqlTransformer(parameterDependencyCheckerInfo), foundParameter(false)
+    {
+    }
+
+    virtual void analyseExpr(IHqlExpression * expr)
+    {
+        if (expr->isFullyBound() || alreadyVisited(expr) || foundParameter)
+            return;
+
+        if (expr->getOperator() == no_param)
+        {
+            foundParameter = true;
+            return;
+        }
+
+        NewHqlTransformer::analyseExpr(expr);
+    }
+
+    bool isDependent(IHqlExpression * expr)
+    {
+        analyse(expr, 0);
+        return foundParameter;
+    }
+
+protected:
+    bool foundParameter;
+};
+
+
+bool isDependentOnParameter(IHqlExpression * expr)
+{
+    if (expr->isFullyBound())
+        return false;
+    ParameterDependencyChecker checker;
+    return checker.isDependent(expr);
+}
+
+//---------------------------------------------------------------------------
+
 void DependencyGatherer::doGatherDependencies(IHqlExpression * expr)
 {
     if (expr->queryTransformExtra())
@@ -6686,7 +6729,7 @@ void ErrorSeverityMapper::exportMappings(IWorkUnit * wu) const
     for (unsigned i=firstActiveMapping; i < max; i++)
     {
         IHqlExpression & cur = severityMappings.item(i);
-        wu->setWarningSeverity(getIntValue(cur.queryChild(0)), getCheckSeverity(cur.queryChild(1)->queryName()));
+        wu->setWarningSeverity((unsigned)getIntValue(cur.queryChild(0)), getCheckSeverity(cur.queryChild(1)->queryName()));
     }
 }
 
@@ -7108,7 +7151,7 @@ public:
         StringBuffer mangledReturnParameters;
         mangleFunctionReturnType(mangledReturn, mangledReturnParameters, retType);
 
-        if (body->hasAttribute(contextAtom))
+        if (functionBodyUsesContext(body))
             mangled.append("P12ICodeContext");
         else if (body->hasAttribute(globalContextAtom) )
             mangled.append("P18IGlobalCodeContext");
@@ -7355,7 +7398,7 @@ public:
 
         mangled.append(mangledReturn);
 
-        if (body->hasAttribute(contextAtom))
+        if (functionBodyUsesContext(body))
             mangled.append("PVICodeContext@@");
         else if (body->hasAttribute(globalContextAtom) )
             mangled.append("PVIGlobalCodeContext@@");

+ 2 - 0
ecl/hql/hqlutil.hpp

@@ -197,6 +197,8 @@ extern HQL_API IHqlExpression * createTransformForField(IHqlExpression * field,
 extern HQL_API IHqlExpression * convertScalarToRow(IHqlExpression * value, ITypeInfo * fieldType);
 extern HQL_API bool splitResultValue(SharedHqlExpr & dataset, SharedHqlExpr & attribute, IHqlExpression * value);
 
+//Is this really dependent on a parameter - expr->isFullyBound() can give false positives.
+extern HQL_API bool isDependentOnParameter(IHqlExpression * expr);
 
 inline void extendConditionOwn(SharedHqlExpr & cond, node_operator op, IHqlExpression * r)
 {

+ 2 - 0
ecl/hqlcpp/hqlcerrors.hpp

@@ -261,6 +261,7 @@
 #define HQLWRN_GlobalActionDependendOnScope     4543
 #define HQLWRN_NoThorContextDependent           4544
 #define HQLWRN_OnlyLocalMergeJoin               4545
+#define HQLWRN_WorkflowDependParameter          4546
 
 //Temporary errors
 #define HQLERR_OrderOnVarlengthStrings          4601
@@ -537,6 +538,7 @@
 #define HQLWRN_GlobalActionDependendOnScope_Text "Global action appears to be context dependent - this may cause a dataset not active error"
 #define HQLWRN_NoThorContextDependent_Text      "NOTHOR expression%s appears to access a parent dataset - this may cause a dataset not active error"
 #define HQLWRN_OnlyLocalMergeJoin_Text          "Only LOCAL versions of %s are currently supported on THOR"
+#define HQLWRN_WorkflowDependParameter_Text     "Workflow action %s appears to be dependent upon a parameter"
 
 #define HQLERR_DistributionVariableLengthX_Text "DISTRIBUTION does not support variable length field '%s'"
 #define HQLERR_DistributionUnsupportedTypeXX_Text "DISTRIBUTION does not support field '%s' with type %s"

+ 10 - 3
ecl/hqlcpp/hqlcpp.cpp

@@ -1987,15 +1987,18 @@ IHqlExpression * HqlCppTranslator::bindFunctionCall(IIdAtom * name, HqlExprArray
     OwnedHqlExpr function = needFunction(name);
     useFunction(function);
     assertex(function->getOperator() == no_funcdef);
+    IFunctionTypeExtra * funcTypeExtra = queryFunctionTypeExtra(function->queryType());
+    assertex(funcTypeExtra);
     IHqlExpression * body = function->queryChild(0);
 
+
     HqlExprArray bodyArgs;
     unwindChildren(bodyArgs, body);
 
     HqlExprArray funcArgs;
     funcArgs.append(*createValue(body->getOperator(), LINK(newType), bodyArgs));
     unwindChildren(funcArgs, function, 1);
-    ITypeInfo * funcType = makeFunctionType(LINK(newType), LINK(function->queryChild(1)), LINK(function->queryChild(2)));
+    ITypeInfo * funcType = makeFunctionType(LINK(newType), LINK(function->queryChild(1)), LINK(function->queryChild(2)), LINK(funcTypeExtra->queryAttributes()));
     OwnedHqlExpr newFunction = createValue(function->getOperator(), funcType, funcArgs);
     return bindFunctionCall(newFunction, args);
 }
@@ -5687,6 +5690,7 @@ void HqlCppTranslator::doBuildCall(BuildCtx & ctx, const CHqlBoundTarget * tgt,
             attrs.append(*LINK(params));
             if (defaults)
                 attrs.append(*LINK(defaults));
+            attrs.append(*createAttribute(contextAtom));
 
             ITypeInfo * returnType = type->queryChildType();
             OwnedHqlExpr externalExpr = createExternalReference(def->queryId(), LINK(returnType), attrs);
@@ -11849,8 +11853,11 @@ void HqlCppTranslator::buildFunctionDefinition(IHqlExpression * funcdef)
         funcctx.addQuotedCompound(proto);
         //MORE: Need to work out how to handle functions that require the context.
         //Need to create a class instead.
-        assertex(!outofline->hasAttribute(contextAtom));
-
+        if (functionBodyUsesContext(outofline))
+        {
+            funcctx.associateExpr(codeContextMarkerExpr, codeContextMarkerExpr);
+            funcctx.associateExpr(globalContextMarkerExpr, globalContextMarkerExpr);
+        }
         OwnedHqlExpr newCode = replaceInlineParameters(funcdef, bodyCode);
         newCode.setown(foldHqlExpression(newCode));
         ITypeInfo * returnType = funcdef->queryType()->queryChildType();

+ 11 - 1
ecl/hqlcpp/hqlttcpp.cpp

@@ -5804,6 +5804,14 @@ IHqlExpression * WorkflowTransformer::extractWorkflow(IHqlExpression * untransfo
         }
     }
 
+    if (isDependentOnParameter(expr))
+    {
+        EclIR::dbglogIR(1, queryLocationIndependent(expr));
+        StringBuffer s;
+        getStoredDescription(s, info.sequence, info.originalLabel, true);
+        translator.reportWarning(CategoryMistake, SeverityUnknown, queryActiveLocation(expr), HQLWRN_WorkflowDependParameter, HQLWRN_WorkflowDependParameter_Text, s.str());
+    }
+
     OwnedHqlExpr setValue;
     OwnedHqlExpr getValue;
     bool done = false;
@@ -6047,7 +6055,9 @@ IHqlExpression * WorkflowTransformer::transformInternalFunction(IHqlExpression *
 
     WorkflowItem * item = new WorkflowItem(namedFuncDef);
     workflowOut->append(*item);
-    return createExternalFuncdefFromInternal(namedFuncDef);
+    OwnedHqlExpr external = createExternalFuncdefFromInternal(namedFuncDef);
+    copyDependencies(queryBodyExtra(namedFuncDef), queryBodyExtra(external));
+    return external.getClear();
 }
 
 IHqlExpression * WorkflowTransformer::transformInternalCall(IHqlExpression * transformed)

+ 7 - 3
ecl/hqlcpp/hqlwcpp.cpp

@@ -536,9 +536,13 @@ void HqlCppWriter::generateType(ITypeInfo * type, const char * name)
                 StringBuffer parameterText;
                 IFunctionTypeExtra * extra = queryFunctionTypeExtra(type);
                 IHqlExpression * args = static_cast<IHqlExpression *>(extra->queryParameters());
+                IHqlExpression * attrs = static_cast<IHqlExpression *>(extra->queryAttributes());
+                if (queryAttributeInList(contextAtom, attrs))
+                    parameterText.append("ICodeContext * ctx");
+
                 ForEachChild(i, args)
                 {
-                    if (i)
+                    if (parameterText.length())
                         parameterText.append(", ");
                     generateExprCpp(parameterText, args->queryChild(i));
                 }
@@ -656,7 +660,7 @@ bool HqlCppWriter::generateFunctionPrototype(IHqlExpression * funcdef, const cha
 
     bool firstParam = true;
     out.append('(');
-    if (body->hasAttribute(contextAtom))
+    if (functionBodyUsesContext(body))
     {
         out.append("ICodeContext * ctx");
         firstParam = false;
@@ -1170,7 +1174,7 @@ StringBuffer & HqlCppWriter::generateExprCpp(IHqlExpression * expr)
                 else
                     out.append(funcdef->queryBody()->queryId()->str());
                 out.append('(');
-                if (props->hasAttribute(contextAtom))
+                if (functionBodyUsesContext(props))
                 {
                     out.append("ctx");
                     if (numArgs)

+ 1 - 1
testing/regress/ecl/cppbody11.ecl

@@ -24,7 +24,7 @@ real scoreIt(scoreFunc func, real a, real b) := DEFINE
     
 real scoreIt2(scoreFunc func, real a, real b) := BEGINC++
     
-    return func(a-1.0,b-1.0) * func(a+1.0,b+1.0);
+    return func(ctx, a-1.0,b-1.0) * func(ctx, a+1.0,b+1.0);
     
 ENDC++;
     

+ 41 - 0
testing/regress/ecl/cppbody12.ecl

@@ -0,0 +1,41 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2014 HPCC Systems.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+real storedval := 1 : stored('s');
+
+real scoreFunc(real a, real b) := 1;
+real scoreFunc2(real a, real b) := BEGINC++ 
+#option context 
+ENDC++;
+
+real scoreIt(scoreFunc func, real a, real b) := BEGINC++
+    #option context
+    return func(ctx,a-1.0,b-1.0) * func(ctx,a+1.0,b+1.0);
+    
+ENDC++;
+    
+real doSum(real a, real b) := DEFINE (a + b + storedval);
+
+ds := DATASET(100, transform({unsigned id}, SELF.id := COUNTER));
+s := SORT(ds, HASH(id));
+c := COUNT(NOFOLD(s));
+
+real doSum2(real a, real b) := DEFINE (a + b + c);
+
+output(scoreIt(doSum, 10, 20));
+output(scoreIt(doSum2, 100, 200));
+output(scoreIt(doSum2, 1, 3));

+ 9 - 0
testing/regress/ecl/key/cppbody12.xml

@@ -0,0 +1,9 @@
+<Dataset name='Result 1'>
+ <Row><Result_1>957.0</Result_1></Row>
+</Dataset>
+<Dataset name='Result 2'>
+ <Row><Result_2>159996.0</Result_2></Row>
+</Dataset>
+<Dataset name='Result 3'>
+ <Row><Result_3>10812.0</Result_3></Row>
+</Dataset>