// Mark the variables listed in 'with' clauses, if any, with tiMark markers. // Same as markOuterVarsWithIntents() in implementForallIntents.cpp, // except uses byrefVars instead of forallIntents. static void markOuterVarsWithIntents(CallExpr* byrefVars, SymbolMap& uses) { if (!byrefVars) return; Symbol* marker = NULL; // Keep in sync with setupForallIntents() - the actuals alternate: // (tiMark arg | reduce opExpr), task-intent variable [, repeat] for_actuals(actual, byrefVars) { SymExpr* se = toSymExpr(actual); INT_ASSERT(se); // comes as an UnresolvedSymExpr from the parser, // should have been resolved in ScopeResolve // or it is a SymExpr over a tiMark ArgSymbol // or over chpl__reduceGlob Symbol* var = se->symbol(); if (marker) { SymbolMapElem* elem = uses.get_record(var); if (elem) { elem->value = marker; } else { if (isVarSymbol(marker)) { // this is a globalOp created in setupOneReduceIntent() INT_ASSERT(!strcmp(marker->name, "chpl__reduceGlob")); USR_WARN(byrefVars, "the variable '%s' is given a reduce intent and not mentioned in the loop body - it will have the unit value after the loop", var->name); } } marker = NULL; } else { marker = var; INT_ASSERT(marker); // otherwise the alternation logic will not work } }
void CallInfo::haltNotWellFormed() const { for (int i = 1; i <= call->numActuals(); i++) { Expr* actual = call->get(i); if (NamedExpr* named = toNamedExpr(actual)) { actual = named->actual; } SymExpr* se = toSymExpr(actual); INT_ASSERT(se); Symbol* sym = se->symbol(); Type* t = sym->type; if (t == dtUnknown && sym->hasFlag(FLAG_TYPE_VARIABLE) == false) { USR_FATAL(call, "use of '%s' before encountering its definition, " "type unknown", sym->name); } else if (t->symbol->hasFlag(FLAG_GENERIC) == true) { INT_FATAL(call, "the type of the actual argument '%s' is generic", sym->name); } } }
static Expr* postFoldPrimop(CallExpr* call) { Expr* retval = call; if (call->isPrimitive(PRIM_MOVE) == true) { retval = postFoldMove(call); } else if (call->isPrimitive(PRIM_QUERY_TYPE_FIELD) == true || call->isPrimitive(PRIM_QUERY_PARAM_FIELD) == true) { SymExpr* classWrap = toSymExpr(call->get(1)); if (AggregateType* at = toAggregateType(classWrap->symbol()->type)) { const char* memberName = get_string(call->get(2)); Vec<Symbol*> keys; at->substitutions.get_keys(keys); forv_Vec(Symbol, key, keys) { if (strcmp(memberName, key->name) == 0) { // If there is a substitution for it, replace this call with that // substitution if (Symbol* value = at->substitutions.get(key)) { retval = new SymExpr(value); call->replace(retval); } } } } else {
// // Assumes 'parent' is a PRIM_MOVE or PRIM_ASSIGN // // Returns false if the LHS of the move/assign indicates that the rhs cannot // be a const-ref. For example, if we have a case like this: // // (move A, (set-reference B)) // // where 'A' and 'B' are references, B cannot be a const-ref if A is not a // const-ref. // // In the case of a dereference, (move A, (deref B)), this function will return // true because we're simply reading B. // static bool canRHSBeConstRef(CallExpr* parent, SymExpr* use) { INT_ASSERT(isMoveOrAssign(parent)); SymExpr* LHS = toSymExpr(parent->get(1)); CallExpr* rhs = toCallExpr(parent->get(2)); INT_ASSERT(rhs); switch (rhs->primitive->tag) { case PRIM_GET_MEMBER: case PRIM_GET_MEMBER_VALUE: case PRIM_GET_SVEC_MEMBER: case PRIM_GET_SVEC_MEMBER_VALUE: case PRIM_GET_REAL: case PRIM_GET_IMAG: case PRIM_ADDR_OF: case PRIM_SET_REFERENCE: { // If LHS is a reference and is not a const-ref, the reference in 'rhs' // should not be considered a const-ref either. // // For the get-member primitives, I intend this to be a safe approach // until we know what const-ref means for fields. Basically, if any field // might be modified I do not consider the base object to be const-ref. // // Note that the get-*-value primitives may return a reference if the // field is a reference. if (LHS->isRef()) { if (!inferConstRef(LHS->symbol())) { return false; } } } default: break; } return isSafeRefPrimitive(use); }
// // Consider a function that takes a formal of type Record by const ref // and that returns that value from the function. The compiler inserts // a PRIM_MOVE operation. // // This work-around inserts an autoCopy to compensate // void ReturnByRef::updateAssignmentsFromRefArgToValue(FnSymbol* fn) { std::vector<CallExpr*> callExprs; collectCallExprs(fn, callExprs); for (size_t i = 0; i < callExprs.size(); i++) { CallExpr* move = callExprs[i]; if (move->isPrimitive(PRIM_MOVE) == true) { SymExpr* lhs = toSymExpr(move->get(1)); SymExpr* rhs = toSymExpr(move->get(2)); if (lhs != NULL && rhs != NULL) { VarSymbol* symLhs = toVarSymbol(lhs->symbol()); ArgSymbol* symRhs = toArgSymbol(rhs->symbol()); if (symLhs != NULL && symRhs != NULL) { if (isUserDefinedRecord(symLhs->type) == true && symRhs->type == symLhs->type) { if (symLhs->hasFlag(FLAG_ARG_THIS) == false && (symRhs->intent == INTENT_REF || symRhs->intent == INTENT_CONST_REF)) { SET_LINENO(move); CallExpr* autoCopy = NULL; rhs->remove(); autoCopy = new CallExpr(autoCopyMap.get(symRhs->type), rhs); move->insertAtTail(autoCopy); } } } } } } }
// // Functions have an informal epilogue defined by code that // // 1) appears after a common "return label" (if present) // 2) copies values to out/in-out formals // // We must destroy the primaries before (1). // We choose to destroy the primaries before (2). // // This code detects the start of (2) // bool AutoDestroyScope::handlingFormalTemps(const Expr* stmt) const { bool retval = false; if (mLocalsHandled == false) { if (const CallExpr* call = toConstCallExpr(stmt)) { if (FnSymbol* fn = call->resolvedFunction()) { if (fn->hasFlag(FLAG_ASSIGNOP) == true && call->numActuals() == 2) { SymExpr* lhs = toSymExpr(call->get(1)); SymExpr* rhs = toSymExpr(call->get(2)); if (lhs != NULL && rhs != NULL && isArgSymbol(lhs->symbol()) == true && rhs->symbol()->hasFlag(FLAG_FORMAL_TEMP) == true) { retval = true; } } } } } return retval; }
static bool removeIdentityDefs(Symbol* sym) { bool change = false; for_defs(def, defMap, sym) { CallExpr* move = toCallExpr(def->parentExpr); if (move && isMoveOrAssign(move)) { SymExpr* rhs = toSymExpr(move->get(2)); if (rhs && def->symbol() == rhs->symbol()) { move->remove(); change = true; } } }
// Find the block stmt that encloses the target of this gotoStmt static BlockStmt* findBlockForTarget(GotoStmt* stmt) { BlockStmt* retval = NULL; if (stmt != NULL && stmt->isGotoReturn() == false) { SymExpr* labelSymExpr = toSymExpr(stmt->label); Expr* ptr = labelSymExpr->symbol()->defPoint; while (ptr != NULL && isBlockStmt(ptr) == false) { ptr = ptr->parentExpr; } retval = toBlockStmt(ptr); INT_ASSERT(retval); } return retval; }
forv_Vec(CallExpr, call, gCallExprs) { if (call->isPrimitive(PRIM_CHECK_ERROR)) { SET_LINENO(call); SymExpr* errSe = toSymExpr(call->get(1)); Symbol* errorVar= errSe->symbol(); VarSymbol* errorExistsVar = newTemp("errorExists", dtBool); DefExpr* def = new DefExpr(errorExistsVar); CallExpr* errorExists = new CallExpr(PRIM_NOTEQUAL, errorVar, gNil); CallExpr* move = new CallExpr(PRIM_MOVE, errorExistsVar, errorExists); Expr* stmt = call->getStmtExpr(); stmt->insertBefore(def); def->insertAfter(move); call->replace(new SymExpr(errorExistsVar)); } }
static QualifiedType returnInfoGetMember(CallExpr* call) { AggregateType* ct = toAggregateType(call->get(1)->typeInfo()); if (ct->symbol->hasFlag(FLAG_REF)) ct = toAggregateType(ct->getValType()); if (!ct) INT_FATAL(call, "bad member primitive"); SymExpr* sym = toSymExpr(call->get(2)); if (!sym) INT_FATAL(call, "bad member primitive"); VarSymbol* var = toVarSymbol(sym->symbol()); if (!var) INT_FATAL(call, "bad member primitive"); if (var->immediate) { const char* name = var->immediate->v_string; for_fields(field, ct) { if (!strcmp(field->name, name)) return field->qualType(); } } else return sym->qualType();
// Walk backwards from the current statement to determine if a sequence of // moves have copied a variable that is marked for auto destruction in to // the dedicated return-temp within the current scope. // // Note that the value we are concerned about may be copied in to one or // more temporary variables between being copied to the return temp. static VarSymbol* variableToExclude(FnSymbol* fn, Expr* refStmt) { VarSymbol* retVar = toVarSymbol(fn->getReturnSymbol()); VarSymbol* retval = NULL; if (retVar != NULL) { if (isUserDefinedRecord(retVar) == true || fn->hasFlag(FLAG_INIT_COPY_FN) == true) { VarSymbol* needle = retVar; Expr* expr = refStmt; // Walk backwards looking for the variable that is being returned while (retval == NULL && expr != NULL && needle != NULL) { if (CallExpr* move = toCallExpr(expr)) { if (move->isPrimitive(PRIM_MOVE) == true) { SymExpr* lhs = toSymExpr(move->get(1)); VarSymbol* lhsVar = toVarSymbol(lhs->symbol()); if (needle == lhsVar) { if (SymExpr* rhs = toSymExpr(move->get(2))) { VarSymbol* rhsVar = toVarSymbol(rhs->symbol()); if (isAutoDestroyedVariable(rhsVar) == true) { retval = rhsVar; } else { needle = rhsVar; } } else { needle = NULL; } } } } expr = expr->prev; } } } return retval; }
// This routine looks for loops in which the condition variable is *not* // updated within the body of the loop, and issues a warning for places // in the code where that occurs. void WhileStmt::checkConstLoops() { SymExpr* tmpVar = condExprForTmpVariableGet(); // Get the loop condition variable. if (VarSymbol* condSym = toVarSymbol(tmpVar->symbol())) { // Look for definitions of the loop condition variable // within the body of the loop. if (SymExpr* condDef = getWhileCondDef(condSym)) { // Get the call expression that updates the condition variable. if (CallExpr* outerCall = toCallExpr(condDef->parentExpr)) { // Assume the outer call is a move expression and that its LHS is // the (SymExpr that contains the) loop condition variable. if (outerCall->get(1) == condDef) { if (outerCall->isPrimitive(PRIM_MOVE)) { // Expect the update to be the result of a call to _cond_test. if (CallExpr* innerCall = toCallExpr(outerCall->get(2))) { FnSymbol* fn = innerCall->resolvedFunction(); if (innerCall->numActuals() == 1 && strcmp(fn->name, "_cond_test") == 0) { checkWhileLoopCondition(innerCall->get(1)); } else { INT_FATAL(innerCall, "Expected the update of a loop conditional " "to be piped through _cond_test()."); } } // The RHS of the move can also be a SymExpr as the result of param // folding ... else if (SymExpr* moveSrc = toSymExpr(outerCall->get(2))) { // ... in which case, the literal should be 'true' or 'false'. if (moveSrc->symbol() == gTrue) { // while true do ... ; -- probably OK. // User said to loop forever ... . } else if (moveSrc->symbol() == gFalse) { // while false do ...; -- probably nothing to worry about // We probably don't get here unless fRemoveUnreachableBlocks // is false. } else { INT_FATAL(moveSrc, "Expected const loop condition variable to be " "true or false."); } } else { // The RHS was neither a CallExpr nor a SymExpr. INT_FATAL(outerCall, "Invalid RHS in a loop condition variable update " "expression."); } } else { INT_FATAL(outerCall, "Expected a loop condition variable update to " "be a MOVE."); } } else { // Note that this being true depends on the compiler inserting a temp // that is the result of applying _cond_test to a more-general loop // conditional expression. // Copy propagation could potentially make this false again.... INT_FATAL(condDef, "Expected loop condition variable to be only " "updated (not read)."); } } else { INT_FATAL(condDef, "The update of a loop condition variable could not " "be converted to a call."); } } else { // There was no update of the loop condition variable in the // body of the loop. // It could be an infinite loop, or it could have a // 'break' or 'return' in it. } } else { INT_FATAL(tmpVar, "The loop condition variable could not be converted " "to a VarSymbol."); } }
static bool inferRefToConst(Symbol* sym) { INT_ASSERT(sym->isRef()); bool isConstRef = sym->qualType().getQual() == QUAL_CONST_REF; const bool wasRefToConst = sym->hasFlag(FLAG_REF_TO_CONST); ConstInfo* info = infoMap[sym]; // If this ref isn't const, then it can't point to a const thing if (info == NULL) { return false; } else if (info->finalizedRefToConst || wasRefToConst || !isConstRef) { return wasRefToConst; } bool isFirstCall = false; if (info->alreadyCalled == false) { isFirstCall = true; info->alreadyCalled = true; } bool isRefToConst = true; if (isArgSymbol(sym)) { // Check each call and set isRefToConst to false if any actual is not a ref // to a const. FnSymbol* fn = toFnSymbol(sym->defPoint->parentSymbol); if (fn->hasFlag(FLAG_VIRTUAL) || fn->hasFlag(FLAG_EXPORT) || fn->hasFlag(FLAG_EXTERN)) { // Not sure how to best handle virtual calls, so simply set false for now // // For export or extern functions, return false because we don't have // all the information about how the function is called. isRefToConst = false; } else { // Need this part to be re-entrant in case of recursive functions while (info->fnUses != NULL && isRefToConst) { SymExpr* se = info->fnUses; info->fnUses = se->symbolSymExprsNext; CallExpr* call = toCallExpr(se->parentExpr); INT_ASSERT(call && call->isResolved()); Symbol* actual = toSymExpr(formal_to_actual(call, sym))->symbol(); if (actual->isRef()) { // I don't think we technically need to skip if the actual is the // same symbol as the formal, but it makes things simpler. if (actual != sym && !inferRefToConst(actual)) { isRefToConst = false; } } else { // Passing a non-ref actual to a reference formal is currently // considered to be the same as an addr-of if (actual->qualType().getQual() != QUAL_CONST_VAL) { isRefToConst = false; } } } } } while (info->hasMore() && isRefToConst) { SymExpr* use = info->next(); CallExpr* call = toCallExpr(use->parentExpr); if (call == NULL) continue; if (isMoveOrAssign(call)) { if (use == call->get(1)) { if (SymExpr* se = toSymExpr(call->get(2))) { if (se->isRef() && !inferRefToConst(se->symbol())) { isRefToConst = false; } } else { CallExpr* RHS = toCallExpr(call->get(2)); INT_ASSERT(RHS); if (RHS->isPrimitive(PRIM_ADDR_OF) || RHS->isPrimitive(PRIM_SET_REFERENCE)) { SymExpr* src = toSymExpr(RHS->get(1)); if (src->isRef()) { if (!inferRefToConst(src->symbol())) { isRefToConst = false; } } else { if (src->symbol()->qualType().getQual() != QUAL_CONST_VAL) { isRefToConst = false; } } } else { isRefToConst = false; } } } } else if (call->isResolved()) { isRefToConst = true; } else { isRefToConst = isSafeRefPrimitive(use); } } if (isFirstCall) { if (isRefToConst) { INT_ASSERT(info->finalizedRefToConst == false); sym->addFlag(FLAG_REF_TO_CONST); } info->reset(); info->finalizedRefToConst = true; } else if (!isRefToConst) { info->finalizedRefToConst = true; } return isRefToConst; }
// // Returns 'true' if 'sym' is (or should be) a const-ref. // If 'sym' can be a const-ref, but is not, this function will change either // the intent or qual of the Symbol to const-ref. // static bool inferConstRef(Symbol* sym) { INT_ASSERT(sym->isRef()); bool wasConstRef = sym->qualType().getQual() == QUAL_CONST_REF; if (sym->defPoint->parentSymbol->hasFlag(FLAG_EXTERN)) { return wasConstRef; } ConstInfo* info = infoMap[sym]; // 'info' may be null if the argument is never used. In that case we can // consider 'sym' to be a const-ref. By letting the rest of the function // proceed, we'll fix up the qualifier for such symbols at the end. if (info == NULL) { return true; } else if (info->finalizedConstness || wasConstRef) { return wasConstRef; } bool isFirstCall = false; if (info->alreadyCalled == false) { isFirstCall = true; info->alreadyCalled = true; } bool isConstRef = true; while (info->hasMore() && isConstRef) { SymExpr* use = info->next(); CallExpr* call = toCallExpr(use->parentExpr); INT_ASSERT(call); CallExpr* parent = toCallExpr(call->parentExpr); if (call->isResolved()) { ArgSymbol* form = actual_to_formal(use); if (form->isRef() && !inferConstRef(form)) { isConstRef = false; } } else if (parent && isMoveOrAssign(parent)) { if (!canRHSBeConstRef(parent, use)) { isConstRef = false; } } else if (call->isPrimitive(PRIM_MOVE)) { // // Handles three cases: // 1) MOVE use value - writing to a reference, so 'use' cannot be const // 2) MOVE ref use - if the LHS is not const, use cannot be const either // 3) MOVE value use - a dereference of 'use' // if (use == call->get(1)) { // CASE 1 if (!call->get(2)->isRef()) { isConstRef = false; } } else { // 'use' is the RHS of a MOVE if (call->get(1)->isRef()) { // CASE 2 SymExpr* se = toSymExpr(call->get(1)); INT_ASSERT(se); if (!inferConstRef(se->symbol())) { isConstRef = false; } } // else CASE 3: do nothing because isConstRef is already true } } else if (call->isPrimitive(PRIM_ASSIGN)) { if (use == call->get(1)) { isConstRef = false; } } else if (call->isPrimitive(PRIM_SET_MEMBER) || call->isPrimitive(PRIM_SET_SVEC_MEMBER)) { // BHARSH 2016-11-02 // In the expr (set_member base member rhs), // If use == base, I take the conservative approach and decide that 'use' // is not a const-ref. I'm not sure that we've decided what const means // for fields yet, so this seems safest. // // If use == rhs, then we would need to do analysis for the member field. // That's beyond the scope of what I'm attempting at the moment, so to // be safe we'll return false for that case. if (use == call->get(1) || use == call->get(3)) { isConstRef = false; } else { // use == member // If 'rhs' is not a ref, then we're writing into 'use'. Otherwise it's // a pointer copy and we don't care if 'rhs' is writable. if (!call->get(3)->isRef()) { isConstRef = false; } } } else { // To be safe, exit the loop with 'false' if we're unsure of how to // handle a primitive. isConstRef = false; } } if (isFirstCall) { if (isConstRef) { INT_ASSERT(info->finalizedConstness == false); if (ArgSymbol* arg = toArgSymbol(sym)) { arg->intent = INTENT_CONST_REF; } else { INT_ASSERT(isVarSymbol(sym)); sym->qual = QUAL_CONST_REF; } } info->reset(); info->finalizedConstness = true; } else if (!isConstRef) { info->finalizedConstness = true; } return isConstRef; }
static bool symExprIsUsedAsConstRef(SymExpr* use) { if (CallExpr* call = toCallExpr(use->parentExpr)) { if (FnSymbol* calledFn = call->resolvedFunction()) { ArgSymbol* formal = actual_to_formal(use); // generally, use const-ref-return if passing to const ref formal if (formal->intent == INTENT_CONST_REF) { // but make an exception for initCopy calls if (calledFn->hasFlag(FLAG_INIT_COPY_FN)) return false; // TODO: tuples of types with blank intent // being 'in' should perhaps use the value version. return true; } } else if (call->isPrimitive(PRIM_RETURN) || call->isPrimitive(PRIM_YIELD)) { FnSymbol* inFn = toFnSymbol(call->parentSymbol); // use const-ref-return if returning by const ref intent if (inFn->retTag == RET_CONST_REF) return true; } else if (call->isPrimitive(PRIM_WIDE_GET_LOCALE) || call->isPrimitive(PRIM_WIDE_GET_NODE)) { // If we are extracting a field from the wide pointer, // we need to keep it as a pointer. // use const-ref-return if querying locale return true; } else { // Check for the case that sym is moved to a compiler-introduced // variable, possibly with PRIM_MOVE tmp, PRIM_ADDR_OF sym if (call->isPrimitive(PRIM_ADDR_OF) || call->isPrimitive(PRIM_SET_REFERENCE) || call->isPrimitive(PRIM_GET_MEMBER) || call->isPrimitive(PRIM_GET_SVEC_MEMBER)) call = toCallExpr(call->parentExpr); if (call->isPrimitive(PRIM_MOVE)) { SymExpr* lhs = toSymExpr(call->get(1)); Symbol* lhsSymbol = lhs->symbol(); if (lhsSymbol->hasFlag(FLAG_REF_VAR)) { // intended to handle 'const ref' // it would be an error to reach this point if it is not const INT_ASSERT(lhsSymbol->hasFlag(FLAG_CONST)); return true; } if (lhs != use && lhsSymbol->isRef() && symbolIsUsedAsConstRef(lhsSymbol)) return true; } } } return false; }
void ReturnByRef::updateAssignmentsFromRefTypeToValue(FnSymbol* fn) { std::vector<CallExpr*> callExprs; collectCallExprs(fn, callExprs); Map<Symbol*,Vec<SymExpr*>*> defMap; Map<Symbol*,Vec<SymExpr*>*> useMap; buildDefUseMaps(fn, defMap, useMap); for (size_t i = 0; i < callExprs.size(); i++) { CallExpr* move = callExprs[i]; if (move->isPrimitive(PRIM_MOVE) == true) { SymExpr* symLhs = toSymExpr (move->get(1)); CallExpr* callRhs = toCallExpr(move->get(2)); if (symLhs && callRhs && callRhs->isPrimitive(PRIM_DEREF)) { VarSymbol* varLhs = toVarSymbol(symLhs->symbol()); SymExpr* symRhs = toSymExpr(callRhs->get(1)); VarSymbol* varRhs = toVarSymbol(symRhs->symbol()); // MPF 2016-10-02: It seems to me that this code should also handle the // case that symRhs is an ArgSymbol, but adding that caused problems // in the handling of out argument intents. if (varLhs != NULL && varRhs != NULL) { if (isUserDefinedRecord(varLhs->type) == true && varRhs->type == varLhs->type->refType) { // HARSHBARGER 2015-12-11: // `init_untyped_var` in the `normalize` pass may insert an // initCopy, which means that we should not insert an autocopy // for that same variable. bool initCopied = false; for_uses(use, useMap, varLhs) { if (CallExpr* call = toCallExpr(use->parentExpr)) { if (FnSymbol* parentFn = call->isResolved()) { if (parentFn->hasFlag(FLAG_INIT_COPY_FN)) { initCopied = true; break; } } } } if (!initCopied) { SET_LINENO(move); SymExpr* lhsCopy0 = symLhs->copy(); SymExpr* lhsCopy1 = symLhs->copy(); FnSymbol* autoCopy = autoCopyMap.get(varLhs->type); CallExpr* copyExpr = new CallExpr(autoCopy, lhsCopy0); CallExpr* moveExpr = new CallExpr(PRIM_MOVE,lhsCopy1, copyExpr); move->insertAfter(moveExpr); } } } }
bool CallInfo::isWellFormed(CallExpr* callExpr) { bool retval = true; call = callExpr; if (SymExpr* se = toSymExpr(call->baseExpr)) { name = se->symbol()->name; } else if (UnresolvedSymExpr* use = toUnresolvedSymExpr(call->baseExpr)) { name = use->unresolved; } if (call->numActuals() >= 2) { if (SymExpr* se = toSymExpr(call->get(1))) { if (se->symbol() == gModuleToken) { se->remove(); se = toSymExpr(call->get(1)); INT_ASSERT(se); ModuleSymbol* mod = toModuleSymbol(se->symbol()); INT_ASSERT(mod); se->remove(); scope = mod->block; } } } for (int i = 1; i <= call->numActuals() && retval == true; i++) { Expr* actual = call->get(i); if (NamedExpr* named = toNamedExpr(actual)) { actualNames.add(named->name); actual = named->actual; } else { actualNames.add(NULL); } SymExpr* se = toSymExpr(actual); INT_ASSERT(se); Symbol* sym = se->symbol(); Type* t = sym->type; if (t == dtUnknown && sym->hasFlag(FLAG_TYPE_VARIABLE) == false) { retval = false; } else if (t->symbol->hasFlag(FLAG_GENERIC) == true) { // The _this actual to an initializer may be generic if (strcmp(name, "init") == 0 && i == 2) { actuals.add(sym); } else { retval = false; } } else { actuals.add(sym); } } return retval; }