浏览代码

HPCC-15767 Restructure the flags - step1. No code changes

Signed-off-by: Gavin Halliday <gavin.halliday@lexisnexis.com>
Gavin Halliday 9 年之前
父节点
当前提交
ed94ba8c97
共有 9 个文件被更改,包括 243 次插入38 次删除
  1. 8 0
      ecl/hql/hqlatoms.cpp
  2. 4 0
      ecl/hql/hqlatoms.hpp
  3. 180 22
      ecl/hql/hqlexpr.cpp
  4. 42 14
      ecl/hql/hqlexpr.hpp
  5. 1 0
      ecl/hql/hqlgram.y
  6. 1 0
      ecl/hql/hqlgram2.cpp
  7. 2 1
      ecl/hql/hqllex.l
  8. 1 1
      ecl/hql/hqlthql.cpp
  9. 4 0
      ecl/hql/hqlutil.cpp

+ 8 - 0
ecl/hql/hqlatoms.cpp

@@ -118,6 +118,7 @@ IAtom * _container_Atom;
 IAtom * contextAtom;
 IAtom * contextSensitiveAtom;
 IAtom * costAtom;
+IAtom * costlyAtom;
 IAtom * countAtom;
 IAtom * counterAtom;
 IAtom * _countProject_Atom;
@@ -286,6 +287,7 @@ IAtom * noConstAtom;
 IAtom * nofoldAtom;
 IAtom * _noHoist_Atom;
 IAtom * noLocalAtom;
+IAtom * noMoveAtom;
 IAtom * _nonEmpty_Atom;
 IAtom * noOverwriteAtom;
 IAtom * _normalized_Atom;
@@ -335,6 +337,7 @@ IAtom * _propAligned_Atom;
 IAtom * _propRecordCount_Atom;
 IAtom * _propSize_Atom;
 IAtom * _propUnadorned_Atom;
+IAtom* _pseudoAction_Atom;
 IAtom * pseudoentrypointAtom;
 IAtom * pullAtom;
 IAtom * pulledAtom;
@@ -441,6 +444,7 @@ IAtom * versionAtom;
 IAtom * virtualAtom;
 IAtom * _virtualSeq_Atom;
 IAtom * volatileAtom;
+IAtom * _volatileId_Atom;
 IAtom * warningAtom;
 IAtom * webserviceAtom;
 IAtom * wholeAtom;
@@ -564,6 +568,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKEATOM(context);
     MAKEATOM(contextSensitive);
     MAKEATOM(cost);
+    MAKEATOM(costly);
     MAKEATOM(count);
     MAKEATOM(counter);
     MAKESYSATOM(countProject);
@@ -733,6 +738,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKEATOM(nofold);
     MAKESYSATOM(noHoist);
     MAKEATOM(noLocal);
+    MAKEATOM(noMove);
     MAKESYSATOM(nonEmpty);
     MAKEATOM(noOverwrite);
     MAKESYSATOM(normalized);
@@ -784,6 +790,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKESYSATOM(propUnadorned);
     MAKEATOM(prototype);
     MAKEATOM(proxyAddress);
+    MAKESYSATOM(pseudoAction);
     MAKEATOM(pseudoentrypoint);
     MAKEATOM(pull);
     MAKEATOM(pulled);
@@ -887,6 +894,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKEATOM(virtual);
     MAKESYSATOM(virtualSeq);
     MAKEATOM(volatile);
+    MAKESYSATOM(volatileId);
     MAKEATOM(warning);
     MAKEATOM(webservice);
     MAKEATOM(whole);

+ 4 - 0
ecl/hql/hqlatoms.hpp

@@ -122,6 +122,7 @@ extern HQL_API IAtom * _container_Atom;
 extern HQL_API IAtom * contextAtom;
 extern HQL_API IAtom * contextSensitiveAtom;
 extern HQL_API IAtom * costAtom;
+extern HQL_API IAtom * costlyAtom;
 extern HQL_API IAtom * countAtom;
 extern HQL_API IAtom * counterAtom;
 extern HQL_API IAtom * _countProject_Atom;
@@ -290,6 +291,7 @@ extern HQL_API IAtom * noConstAtom;
 extern HQL_API IAtom * nofoldAtom;
 extern HQL_API IAtom * _noHoist_Atom;
 extern HQL_API IAtom * noLocalAtom;
+extern HQL_API IAtom * noMoveAtom;
 extern HQL_API IAtom * _nonEmpty_Atom;
 extern HQL_API IAtom * noOverwriteAtom;
 extern HQL_API IAtom * _normalized_Atom;
@@ -340,6 +342,7 @@ extern HQL_API IAtom * _propAligned_Atom;
 extern HQL_API IAtom * _propRecordCount_Atom;
 extern HQL_API IAtom * _propSize_Atom;
 extern HQL_API IAtom * _propUnadorned_Atom;
+extern HQL_API IAtom * _pseudoAction_Atom;
 extern HQL_API IAtom * pseudoentrypointAtom;
 extern HQL_API IAtom * pullAtom;
 extern HQL_API IAtom * pulledAtom;
@@ -446,6 +449,7 @@ extern HQL_API IAtom * versionAtom;
 extern HQL_API IAtom * virtualAtom;
 extern HQL_API IAtom * _virtualSeq_Atom;
 extern HQL_API IAtom * volatileAtom;
+extern HQL_API IAtom * _volatileId_Atom;
 extern HQL_API IAtom * warningAtom;
 extern HQL_API IAtom * webserviceAtom;
 extern HQL_API IAtom * wholeAtom;

+ 180 - 22
ecl/hql/hqlexpr.cpp

@@ -3749,9 +3749,14 @@ void CHqlExpression::updateFlagsAfterOperands()
         infoFlags2 |= HEF2constant;
         break;
     case no_attr:
-        infoFlags = (infoFlags & (HEFhousekeeping|HEFalwaysInherit));
-        infoFlags2 |= HEF2constant;
-        break;
+        {
+            infoFlags = (infoFlags & (HEFhousekeeping|HEFalwaysInherit));
+            infoFlags2 |= HEF2constant;
+            IAtom * name = queryName();
+            if (name == _volatileId_Atom)
+                infoFlags |= HEFvolatile;
+            break;
+        }
     case no_newxmlparse:
     case no_xmlparse:
         //clear flag unless set in the dataset.
@@ -3781,12 +3786,15 @@ void CHqlExpression::updateFlagsAfterOperands()
         break;
     case no_attr_link:
     case no_attr_expr:
-        if (queryName() == onFailAtom)
         {
-            infoFlags &= ~(HEFonFailDependent|HEFcontainsSkip); // ONFAIL(SKIP) - skip shouldn't extend any further
+            IAtom * name = queryName();
+            if (name == onFailAtom)
+                infoFlags &= ~(HEFonFailDependent|HEFcontainsSkip); // ONFAIL(SKIP) - skip shouldn't extend any further
+            else if (name == _volatileId_Atom)
+                infoFlags |= HEFvolatile;
+            infoFlags &= ~(HEFthrowscalar|HEFthrowds|HEFoldthrows);
+            break;
         }
-        infoFlags &= ~(HEFthrowscalar|HEFthrowds|HEFoldthrows);
-        break;
     case no_clustersize:
         //wrong, but improves the generated code
         infoFlags |= HEFvolatile;
@@ -4341,7 +4349,7 @@ unsigned CHqlExpression::getCachedEclCRC()
     case no_attr_link:
         {
             const IAtom * name = queryBody()->queryName();
-            if (name == _uid_Atom)
+            if (name == _uid_Atom || name == _volatileId_Atom)
                 return 0;
             const char * nameText = str(name);
             crc = hashnc((const byte *)nameText, strlen(nameText), crc);
@@ -8105,7 +8113,10 @@ IHqlExpression * createFunctionDefinition(IIdAtom * id, IHqlExpression * value,
     if (defaults)
         args.append(*defaults);
     if (attrs)
+    {
         attrs->unwindList(args, no_comma);
+        ::Release(attrs);
+    }
     return createFunctionDefinition(id, args);
 }
 
@@ -10280,28 +10291,88 @@ CHqlExternal *CHqlExternal::makeExternalReference(IIdAtom * _id, ITypeInfo *_typ
 
 //==============================================================================================================
 
+extern bool isVolatileFuncdef(IHqlExpression * funcdef)
+{
+    if (funcdef->hasAttribute(volatileAtom))
+        return true;
+
+    IHqlExpression * body = funcdef->queryChild(0);
+    switch (body->getOperator())
+    {
+    case no_external:
+        {
+            if (body->hasAttribute(volatileAtom))
+                return true;
+            return false;
+        }
+    case no_outofline:
+        {
+            //Out of line volatile c++ functions create new instances each time they are called.
+            //otherwise it requires an explicit volatile qualifier.
+            IHqlExpression * bodycode = body->queryChild(0);
+            if (bodycode->getOperator() == no_embedbody)
+                return bodycode->queryAttribute(volatileAtom);
+            return false;
+        }
+    default:
+        return false;
+    }
+}
+
 CHqlExternalCall::CHqlExternalCall(IHqlExpression * _funcdef, ITypeInfo * _type, HqlExprArray &_ownedOperands) : CHqlExpressionWithType(no_externalcall, _type, _ownedOperands), funcdef(_funcdef)
 {
-    IHqlExpression * def = funcdef->queryChild(0);
+    IHqlExpression * body = funcdef->queryChild(0);
+    unsigned impureFlags = 0;
+    if (body->hasAttribute(failAtom))
+        impureFlags |= isDataset() ? HEFthrowds : HEFthrowscalar;
+    if (body->hasAttribute(noMoveAtom) || body->hasAttribute(contextSensitiveAtom))
+        impureFlags |= HEFcontextDependentException;
+    if (body->hasAttribute(costlyAtom))
+        impureFlags |= HEFcostly;
+
+    if (isVolatileFuncdef(funcdef))
+        impureFlags |= HEFvolatile;
     //Once aren't really pure, but are as far as the code generator is concerned.  Split into more flags if it becomes an issue.
-    if (!def->hasAttribute(pureAtom) && !def->hasAttribute(onceAtom))
+    if (!body->hasAttribute(pureAtom) && !body->hasAttribute(onceAtom))
     {
         infoFlags |= (HEFvolatile);
     }
+
+#if 0
+    if (body->hasAttribute(ctxmethodAtom))
+    {
+        StringBuffer entrypoint;
+        getStringValue(entrypoint, queryAttributeChild(body, entrypointAtom, 0));
+        if (streq(entrypoint.str(), "getNodeNum") ||
+            streq(entrypoint.str(), "getFilePart"))
+        {
+            impureFlags |= HEFcontextDependentException;
+        }
+        if (streq(entrypoint.str(), "getPlatform"))
+        {
+            //impureFlags |= HEFvolatile;
+        }
+    }
+#endif
+
+    infoFlags |= impureFlags;
     
-    if (def->hasAttribute(actionAtom) || (type && type->getTypeCode() == type_void))
+    if (body->hasAttribute(actionAtom) || (type && type->getTypeCode() == type_void))
         infoFlags |= HEFaction;
 
-    if (def->hasAttribute(userMatchFunctionAtom))
+    if (body->hasAttribute(userMatchFunctionAtom))
     {
         infoFlags |= HEFcontainsNlpText;
     }
 
-    if (def->hasAttribute(contextSensitiveAtom))
-        infoFlags |= HEFcontextDependentException;
-
-    if (def->hasAttribute(ctxmethodAtom) || def->hasAttribute(gctxmethodAtom) || def->hasAttribute(globalContextAtom) || def->hasAttribute(contextAtom))
+    if (body->hasAttribute(ctxmethodAtom) || body->hasAttribute(gctxmethodAtom) || body->hasAttribute(globalContextAtom) || body->hasAttribute(contextAtom))
         infoFlags |= HEFaccessRuntimeContext;
+
+    if (hasAttribute(_pseudoAction_Atom))
+    {
+        ::Release(type);
+        type = makeVoidType();
+    }
 }
 
 bool CHqlExternalCall::equals(const IHqlExpression & other) const
@@ -11586,8 +11657,14 @@ IHqlExpression * ParameterBindTransformer::createExpandedCall(IHqlExpression *fu
 {
     assertex(funcdef->getOperator() == no_funcdef);
     IHqlExpression * formals = funcdef->queryChild(1);
-    assertex(formals->numChildren() == resolvedActuals.length());
-    ForEachItemIn(i, resolvedActuals)
+
+    unsigned numFormals = formals->numChildren();
+    assertex(numFormals <= resolvedActuals.length());
+
+    for (unsigned i1 = numFormals; i1 < resolvedActuals.length(); i1++)
+        assertex(resolvedActuals.item(i1).isAttribute());
+
+    ForEachChild(i, formals)
     {
         IHqlExpression * formal = formals->queryChild(i);
         IHqlExpression * actual = &resolvedActuals.item(i);
@@ -11620,6 +11697,46 @@ extern HQL_API bool isKey(IHqlExpression * expr)
 
 //-------------------------------------------------------------------------------------
 
+static HqlTransformerInfo volatileIdModifierInfo("VolatileIdModifier");
+class VolatileIdModifier : public QuickHqlTransformer
+{
+public:
+    VolatileIdModifier(IHqlExpression * _volatileid)
+    : QuickHqlTransformer(volatileIdModifierInfo, NULL), volatileid(_volatileid)
+    {
+    }
+
+protected:
+    virtual IHqlExpression * createTransformedBody(IHqlExpression * expr)
+    {
+        if (expr->getOperator() == no_outofline)
+            return LINK(expr);
+
+        if (expr->isAttribute() && (expr->queryName() == _volatileId_Atom))
+        {
+            HqlExprArray args;
+            args.append(*LINK(expr));
+            args.append(*LINK(volatileid));
+            return createExprAttribute(_volatileId_Atom, args);
+        }
+
+        return QuickHqlTransformer::createTransformedBody(expr);
+    }
+
+protected:
+    IHqlExpression * volatileid;
+};
+
+IHqlExpression * modifyVolatileIds(IHqlExpression * expr, IHqlExpression * volatileid)
+{
+    VolatileIdModifier modifier(volatileid);
+    return modifier.transform(expr);
+}
+
+
+
+//-------------------------------------------------------------------------------------
+
 static HqlTransformerInfo callExpandTransformerInfo("CallExpandTransformer");
 class CallExpandTransformer : public QuickHqlTransformer
 {
@@ -11863,8 +11980,15 @@ static IHqlExpression * createNormalizedCall(IHqlExpression *funcdef, const HqlE
 
 inline IHqlExpression * expandFunctionalCallBody(CallExpansionContext & ctx, IHqlExpression * call)
 {
-    ParameterBindTransformer binder(ctx, call);
-    return binder.createExpandedCall(call);
+    OwnedHqlExpr ret;
+    {
+        ParameterBindTransformer binder(ctx, call);
+        ret.setown(binder.createExpandedCall(call));
+    }
+    IHqlExpression * volatileid = call->queryAttribute(_volatileId_Atom);
+    if (volatileid)
+        return modifyVolatileIds(ret, volatileid);
+    return ret.getClear();
 }
 
 static IHqlExpression * normalizeTrailingAttributes(IHqlExpression * call)
@@ -15251,6 +15375,38 @@ IHqlExpression * queryOnlyField(IHqlExpression * record)
     return ret;
 }
 
+//---------------------------------------------------------------------------------------------------------------------
+
+bool canDuplicateActivity(IHqlExpression * expr)
+{
+    unsigned max = expr->numChildren();
+    for (unsigned i = getNumChildTables(expr); i < max; i++)
+    {
+        IHqlExpression * cur = expr->queryChild(i);
+        if (!canDuplicateExpr(cur))
+            return false;
+    }
+    return true;
+}
+
+bool hasTransformWithSkip(IHqlExpression * expr)
+{
+    unsigned max = expr->numChildren();
+    for (unsigned i = getNumChildTables(expr); i < max; i++)
+    {
+        IHqlExpression * cur = expr->queryChild(i);
+        if (containsSkip(cur))
+            return true;
+    }
+    return false;
+}
+
+bool isNoSkipInlineDataset(IHqlExpression * expr)
+{
+    assertex(expr->getOperator() == no_inlinetable);
+    IHqlExpression * values = expr->queryChild(0);
+    return !hasTransformWithSkip(values);
+}
 
 bool isPureActivity(IHqlExpression * expr)
 {
@@ -15300,6 +15456,8 @@ bool assignsContainSkip(IHqlExpression * expr)
     }
 }
 
+//---------------------------------------------------------------------------------------------------------------------
+
 extern HQL_API bool isKnownTransform(IHqlExpression * transform)
 {
     switch (transform->getOperator())
@@ -15547,10 +15705,10 @@ IHqlExpression * createSelector(node_operator op, IHqlExpression * ds, IHqlExpre
 }
 
 static UniqueSequenceCounter uidSequence;
-IHqlExpression * createUniqueId()
+IHqlExpression * createUniqueId(IAtom * name)
 {
     unsigned __int64 uid = uidSequence.next();
-    return createSequence(no_attr, NULL, _uid_Atom, uid);
+    return createSequence(no_attr, NULL, name, uid);
 }
 
 static UniqueSequenceCounter counterSequence;

+ 42 - 14
ecl/hql/hqlexpr.hpp

@@ -112,15 +112,16 @@ enum
     HEFunbound                  = 0x00000010,
     HEFinternalSelect          = 0x00000020,
     HEFcontainsDatasetAliasLocally= 0x00000040,
-  HEF____unused2____          = 0x00000080,
-  HEF____unused3____          = 0x00000100,
-    HEFfunctionOfGroupAggregate = 0x00000200,
-    HEFvolatile                 = 0x00000400,           // value changes each time called - e.g., random()
-    HEFaction                   = 0x00000800,           // an action, or something that can have a side-effect
-    HEFaccessRuntimeContext     = 0x00000100,
-    HEFthrowscalar              = 0x00002000,           // scalar/action that can throw an exception
-    HEFthrowds                  = 0x00004000,           // dataset that can throw an exception
-    HEFoldthrows                = 0x00008000,           // old throws flag, which I should remove asap
+    HEFvolatile                 = 0x00000080,           // value changes each time it is called - e.g., RANDOM()
+    HEFcontextDependentException= 0x00000100,           // depends on the context, but not known how
+    HEFcostly                   = 0x00000200,           // an expensive operation
+    HEFaction                   = 0x00000400,           // an action, or something that can have a side-effect.  Not convinced this is needed
+
+    HEFaccessRuntimeContext     = 0x00000800,
+    HEFthrowscalar              = 0x00001000,           // scalar/action that can throw an exception
+    HEFthrowds                  = 0x00002000,           // dataset that can throw an exception
+    HEFoldthrows                = 0x00004000,           // old throws flag, which I should remove asap
+    HEFfunctionOfGroupAggregate = 0x00008000,
 
 // code generator specific start from the bottom up.
 //NB: update the select 
@@ -139,7 +140,7 @@ enum
     HEFcontainsSkip             = 0x04000000,
     HEFcontainsCounter          = 0x08000000,
     HEFassertkeyed              = 0x10000000,
-    HEFcontextDependentException= 0x20000000,               // A context dependent item that doesn't fit into any other category - for use as a last resort!
+    HEF__unused5__              = 0x20000000,
     HEFcontainsAlias            = 0x40000000,
     HEFcontainsAliasLocally     = 0x80000000,
 
@@ -148,15 +149,17 @@ enum
     HEFalwaysInherit            = HEFunbound|HEFinternalSelect,
     HEFassigninheritFlags       = ~(HEFhousekeeping|HEFalwaysInherit),          // An assign inherits all but this list from the rhs value 
 
+    HEFthrow                    = (HEFthrowscalar|HEFthrowds),
 //  HEFcontextDependent         = (HEFgraphDependent|HEFcontainsNlpText|HEFcontainsXmlText|HEFcontainsSkip|HEFcontainsCounter|HEFtransformDependent|HEFtranslated|HEFonFailDependent|HEFcontextDependentException|HEFthrowscalar|HEFthrowds),
     HEFcontextDependent         = (HEFgraphDependent|HEFcontainsNlpText|HEFcontainsXmlText|HEFcontainsSkip|HEFcontainsCounter|HEFtransformDependent|HEFtranslated|HEFonFailDependent|HEFcontextDependentException|HEFoldthrows),
     HEFretainedByActiveSelect   = (HEFhousekeeping|HEFalwaysInherit),
 
     HEFintersectionFlags        = (0),
-    HEFunionFlags               = (HEFunbound|HEFfunctionOfGroupAggregate|HEFvolatile|HEFaction|HEFthrowscalar|HEFthrowds|HEFoldthrows|
+    HEFunionFlags               = (HEFunbound|HEFfunctionOfGroupAggregate|
+                                   HEFvolatile|HEFcontextDependentException|HEFcostly|HEFaction|HEFthrowscalar|HEFthrowds|HEFoldthrows|
                                    HEFonFailDependent|HEFcontainsActiveDataset|HEFcontainsActiveNonSelector|HEFcontainsDataset|
                                    HEFtranslated|HEFgraphDependent|HEFcontainsNlpText|HEFcontainsXmlText|HEFtransformDependent|
-                                   HEFcontainsSkip|HEFcontainsCounter|HEFassertkeyed|HEFcontextDependentException|HEFcontainsAlias|HEFcontainsAliasLocally|
+                                   HEFcontainsSkip|HEFcontainsCounter|HEFassertkeyed|HEFcontainsAlias|HEFcontainsAliasLocally|
                                    HEFinternalSelect|HEFcontainsThisNode|HEFcontainsDatasetAliasLocally|HEFaccessRuntimeContext),
 
     HEFcontextDependentNoThrow  = (HEFcontextDependent & ~(HEFthrowscalar|HEFthrowds|HEFoldthrows)),
@@ -1505,7 +1508,8 @@ extern HQL_API unsigned unwoundCount(IHqlExpression * expr, node_operator op);
 extern HQL_API void unwindAttribute(HqlExprArray & args, IHqlExpression * expr, IAtom * name);
 extern HQL_API IHqlExpression * queryChildOperator(node_operator op, IHqlExpression * expr);
 extern HQL_API IHqlExpression * createSelector(node_operator op, IHqlExpression * ds, IHqlExpression * seq);
-extern HQL_API IHqlExpression * createUniqueId();
+extern HQL_API IHqlExpression * createUniqueId(IAtom * name);
+
 extern HQL_API IHqlExpression * createUniqueRowsId();
 extern HQL_API IHqlExpression * createCounter();
 extern HQL_API IHqlExpression * createSelectorSequence();
@@ -1527,6 +1531,9 @@ extern HQL_API IHqlExpression * ensureDeserialized(IHqlExpression * expr, ITypeI
 extern HQL_API IHqlExpression * ensureSerialized(IHqlExpression * expr, IAtom * serialForm);
 extern HQL_API bool isDummySerializeDeserialize(IHqlExpression * expr);
 
+inline IHqlExpression * createUniqueId() { return createUniqueId(_uid_Atom); }
+inline IHqlExpression * createVolatileId() { return createUniqueId(_volatileId_Atom); }
+
 extern HQL_API unsigned getRepeatMax(IHqlExpression * expr);
 extern HQL_API unsigned getRepeatMin(IHqlExpression * expr);
 extern HQL_API bool isStandardRepeat(IHqlExpression * expr);
@@ -1659,12 +1666,32 @@ extern HQL_API bool isSameUnqualifiedType(ITypeInfo * l, ITypeInfo * r);
 extern HQL_API bool isSameFullyUnqualifiedType(ITypeInfo * l, ITypeInfo * r);
 extern HQL_API IHqlExpression * queryNewSelectAttrExpr();
 
+//The following functions deal with the different aspects of impure functions - volatile,costly,throw,skip,...
+
+inline bool isVolatile(IHqlExpression * expr)           { return (expr->getInfoFlags() & HEFvolatile) != 0; }
+//Is it ok to duplicate the evaluation of this expression in another context?
+inline bool canDuplicateExpr(IHqlExpression * expr)      { return (expr->getInfoFlags() & (HEFvolatile|HEFcostly)) == 0; }
+//Is it legal to evaluate this expression in a different context - e.g, in a parent instead of child query
+inline bool canChangeContext(IHqlExpression * expr)     { return (expr->getInfoFlags() & (HEFvolatile|HEFcontextDependent|HEFthrow|HEFcontainsSkip|HEFcostly)) == 0; }
+//Is it ok to convert a conditional expression to an unconditional expression?
+inline bool canRemoveGuard(IHqlExpression * expr)       { return (expr->getInfoFlags() & (HEFthrow|HEFcontainsSkip|HEFcostly)) == 0; }
+//Is it legal to reuse the value created in another context for this expression?
+inline bool canCommonUpContext(IHqlExpression * expr)     { return (expr->getInfoFlags() & (HEFcontextDependent|HEFcontainsSkip)) == 0; }
+
+//Is it legal to duplicate this activity?
+extern HQL_API bool canDuplicateActivity(IHqlExpression * expr);
+
+extern HQL_API bool hasTransformWithSkip(IHqlExpression * expr);
+extern HQL_API bool isNoSkipInlineDataset(IHqlExpression * expr);
+
+
+
+
 //The following are wrappers for the code generator specific getInfoFlags()
 //inline bool isTableInvariant(IHqlExpression * expr)       { return (expr->getInfoFlags() & HEFtableInvariant) != 0; }
 inline bool containsActiveDataset(IHqlExpression * expr){ return (expr->getInfoFlags() & HEFcontainsActiveDataset) != 0; }
 inline bool containsActiveNonSelector(IHqlExpression * expr)
                                                         { return (expr->getInfoFlags() & HEFcontainsActiveNonSelector) != 0; }
-
 inline bool containsNonActiveDataset(IHqlExpression * expr) { return (expr->getInfoFlags() & (HEFcontainsDataset)) != 0; }
 inline bool containsAnyDataset(IHqlExpression * expr)   { return (expr->getInfoFlags() & (HEFcontainsDataset|HEFcontainsActiveDataset)) != 0; }
 inline bool containsAlias(IHqlExpression * expr)        { return (expr->getInfoFlags() & HEFcontainsAlias) != 0; }
@@ -1711,6 +1738,7 @@ inline IHqlExpression * queryRecord(IHqlExpression * expr)
 }
 
 extern HQL_API bool isPureVirtual(IHqlExpression * cur);
+extern HQL_API bool isVolatileFuncdef(IHqlExpression * funcdef);
 inline bool isForwardScope(IHqlScope * scope) { return scope && (queryExpression(scope)->getOperator() == no_forwardscope); }
 
 extern HQL_API bool isContextDependent(IHqlExpression * expr, bool ignoreFailures = false, bool ignoreGraph = false);

+ 1 - 0
ecl/hql/hqlgram.y

@@ -470,6 +470,7 @@ static void eclsyntaxerror(HqlGram * parser, const char * s, short yystate, int
   VALIDATE
   VARIANCE
   VIRTUAL
+  VOLATILE
   WAIT
   TOK_WARNING
   WHEN

+ 1 - 0
ecl/hql/hqlgram2.cpp

@@ -6224,6 +6224,7 @@ IHqlExpression *HqlGram::bindParameters(const attribute & errpos, IHqlExpression
             }
             else
             {
+                //Binding an external, outofline or beginc++ function
                 bool expandCall = insideTemplateFunction() ? false : expandCallsWhenBound;
                 // do the actual binding
                 return createBoundFunction(this, function, actuals, lookupCtx.functionCache, expandCall);

+ 2 - 1
ecl/hql/hqllex.l

@@ -964,6 +964,7 @@ UPDATE              { RETURNSYM(UPDATE); }
 USE                 { RETURNSYM(USE); }
 VALIDATE            { RETURNSYM(VALIDATE); }
 VIRTUAL             { RETURNSYM(VIRTUAL); }
+VOLATILE            { RETURNSYM(VOLATILE); }
 WAIT                { RETURNSYM(WAIT); }
 WARNING             { RETURNSYM(TOK_WARNING); }
 WHEN                { RETURNSYM(WHEN); }
@@ -1993,4 +1994,4 @@ void HqlLex::doEnterEmbeddedMode(yyscan_t yyscanner)
 {
    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
    BEGIN(CPP);
-}
+}

+ 1 - 1
ecl/hql/hqlthql.cpp

@@ -1561,7 +1561,7 @@ void HqltHql::toECL(IHqlExpression *expr, StringBuffer &s, bool paren, bool inTy
             bool first = true;
             while(IHqlExpression *kid = expr->queryChild(idx))
             {
-                bool isHidden = false;
+                bool isHidden = !expandProcessed && kid->isAttribute();
                 if (formals && !expandProcessed)
                 {
                     IHqlExpression *formal = formals->queryChild(idx);

+ 4 - 0
ecl/hql/hqlutil.cpp

@@ -5807,6 +5807,10 @@ IHqlExpression * extractCppBodyAttrs(unsigned lenBuffer, const char * buffer)
                     i = end;
                     if(matchOption(start, end, buffer, 4, "pure", false, valueStart, valueEnd))
                         attrs.setown(createComma(attrs.getClear(), createAttribute(pureAtom)));
+                    else if (matchOption(start, lenBuffer, buffer, 8, "volatile", false, valueStart, valueEnd))
+                        attrs.setown(createComma(attrs.getClear(), createAttribute(volatileAtom)));
+                    else if (matchOption(start, end, buffer, 4, "costly", false, valueStart, valueEnd))
+                        attrs.setown(createComma(attrs.getClear(), createAttribute(costlyAtom)));
                     else if (matchOption(start, end, buffer, 4, "once", false, valueStart, valueEnd))
                         attrs.setown(createComma(attrs.getClear(), createAttribute(onceAtom)));
                     else if (matchOption(start, end, buffer, 6, "action", false, valueStart, valueEnd))