void TypeSymbol::codegenMetadata() { #ifdef HAVE_LLVM // Don't do anything if we've already visited this type. if( llvmTbaaNode ) return; GenInfo* info = gGenInfo; llvm::LLVMContext& ctx = info->module->getContext(); // Create the TBAA root node if necessary. if( ! info->tbaaRootNode ) { LLVM_METADATA_OPERAND_TYPE* Ops[1]; Ops[0] = llvm::MDString::get(ctx, "Chapel types"); info->tbaaRootNode = llvm::MDNode::get(ctx, Ops); } // Set the llvmTbaaNode to non-NULL so that we can // avoid recursing. llvmTbaaNode = info->tbaaRootNode; AggregateType* ct = toAggregateType(type); Type* superType = NULL; // Recursively generate the TBAA nodes for this type. if( ct ) { for_fields(field, ct) { AggregateType* fct = toAggregateType(field->type); if(fct && field->hasFlag(FLAG_SUPER_CLASS)) { superType = field->type; } field->type->symbol->codegenMetadata(); } }
// // clear back pointers to dead ast instances // forv_Vec(TypeSymbol, ts, gTypeSymbols) { for (int i = 0; i < ts->type->methods.n; i++) { FnSymbol* method = ts->type->methods.v[i]; if (method && !isAliveQuick(method)) { ts->type->methods.v[i] = NULL; } if (AggregateType* ct = toAggregateType(ts->type)) { if (ct->defaultInitializer != NULL && isAliveQuick(ct->defaultInitializer) == false) { ct->defaultInitializer = NULL; } if (ct->hasDestructor() == true && isAliveQuick(ct->getDestructor()) == false) { ct->setDestructor(NULL); } } } if (AggregateType* at = toAggregateType(ts->type)) { for (int i = 0; i < at->dispatchChildren.n; i++) { if (AggregateType* type = at->dispatchChildren.v[i]) { if (isAlive(type) == false) { at->dispatchChildren.v[i] = NULL; } } } } }
static Type* returnInfoVal(CallExpr* call) { AggregateType* ct = toAggregateType(call->get(1)->typeInfo()); if (!ct || !ct->symbol->hasFlag(FLAG_REF)) INT_FATAL(call, "attempt to get value type of non-reference type"); return ct->getField(1)->type; }
bool ReturnByRef::isTransformableFunction(FnSymbol* fn) { bool retval = false; if (AggregateType* type = toAggregateType(fn->retType)) { if (fn->hasFlag(FLAG_INIT_COPY_FN) == true) retval = false; else if (fn->hasFlag(FLAG_AUTO_COPY_FN) == true) retval = false; // Function is an iterator "helper" else if (fn->hasFlag(FLAG_AUTO_II) == true) retval = false; // Can't transform extern functions else if (fn->hasFlag(FLAG_EXTERN) == true) retval = false; // Noakes: 2016/02/24. Only "user defined records" for now else if (isUserDefinedRecord(type) == true) retval = true; else retval = false; } return retval; }
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 {
static QualifiedType returnInfoVal(CallExpr* call) { AggregateType* ct = toAggregateType(call->get(1)->typeInfo()); if (ct) { if (call->get(1)->isRef()) { if(ct->symbol->hasFlag(FLAG_REF)) { return QualifiedType(ct->getField(1)->type, QUAL_VAL); } else { return QualifiedType(ct, QUAL_VAL); } } else if (call->get(1)->isWideRef()) { if(ct->symbol->hasFlag(FLAG_WIDE_REF)) { return QualifiedType(ct->getField(2)->type, QUAL_VAL); } else { return QualifiedType(ct, QUAL_VAL); } } else if (ct->symbol->hasFlag(FLAG_WIDE_CLASS)) { // insertWideReferences will sometimes insert a PRIM_DEREF to a // wide class. There should probably be a better way of expressing the // desired pattern... return QualifiedType(ct, QUAL_VAL); } } INT_FATAL(call, "attempt to get value type of non-reference type"); return QualifiedType(NULL); }
/* To implement task reduce intents, we follow the steps in propagateExtraLeaderArgs() and setupOneReduceIntent() I.e. add the following to the AST: * before 'call' def globalOp = new reduceType(origSym.type); * pass globalOp to call(); corresponding formal in 'fn': parentOp * inside 'fn' def currOp = parentOp.clone() def symReplace = currOp.identify; ... currOp.accumulate(symReplace); parentOp.combine(currOp); delete currOp; * after 'call' and its _waitEndCount() origSym = parentOp.generate(); delete parentOp; Put in a different way, a coforall like this: var x: int; coforall ITER with (OP reduce x) { BODY(x); // will typically include: x OP= something; } with its corresponding task-function representation: var x: int; proc coforall_fn() { BODY(x); } call coforall_fn(); is transformed into var x: int; var globalOp = new OP_SCAN_REDUCE_CLASS(x.type); proc coforall_fn(parentOp) { var currOp = parentOp.clone() var symReplace = currOp.identify; BODY(symReplace); currOp.accumulate(symReplace); parentOp.combine(currOp); delete currOp; } call coforall_fn(globalOp); // wait for endCount - not shown x = globalOp.generate(); delete globalOp; Todo: to support cobegin constructs, need to share 'globalOp' across all fn+call pairs for the same construct. */ static void addReduceIntentSupport(FnSymbol* fn, CallExpr* call, TypeSymbol* reduceType, Symbol* origSym, ArgSymbol*& newFormal, Symbol*& newActual, Symbol*& symReplace, bool isCoforall, Expr*& redRef1, Expr*& redRef2) { setupRedRefs(fn, true, redRef1, redRef2); VarSymbol* globalOp = new VarSymbol("reduceGlobal"); globalOp->addFlag(FLAG_NO_CAPTURE_FOR_TASKING); newActual = globalOp; VarSymbol* eltType = newTemp("redEltType"); eltType->addFlag(FLAG_MAYBE_TYPE); Expr* headAnchor = call; if (isCoforall) headAnchor = headAnchor->parentExpr; headAnchor->insertBefore(new DefExpr(eltType)); headAnchor->insertBefore("'move'(%S, 'typeof'(%S))", eltType, origSym); headAnchor->insertBefore(new DefExpr(globalOp)); AggregateType* reduceAt = toAggregateType(reduceType->type); INT_ASSERT(reduceAt); CallExpr* newOp = new CallExpr(reduceAt->defaultInitializer->name, new NamedExpr("eltType", new SymExpr(eltType))); headAnchor->insertBefore(new CallExpr(PRIM_MOVE, globalOp, newOp)); Expr* tailAnchor = findTailInsertionPoint(call, isCoforall); // Doing insertAfter() calls in reverse order. // Can't insertBefore() on tailAnchor->next - that can be NULL. tailAnchor->insertAfter("'delete'(%S)", globalOp); tailAnchor->insertAfter("'='(%S, generate(%S,%S))", origSym, gMethodToken, globalOp); ArgSymbol* parentOp = new ArgSymbol(INTENT_BLANK, "reduceParent", dtUnknown); newFormal = parentOp; VarSymbol* currOp = new VarSymbol("reduceCurr"); VarSymbol* svar = new VarSymbol(origSym->name, origSym->type); symReplace = svar; redRef1->insertBefore(new DefExpr(currOp)); redRef1->insertBefore("'move'(%S, clone(%S,%S))", // init currOp, gMethodToken, parentOp); redRef1->insertBefore(new DefExpr(svar)); redRef1->insertBefore("'move'(%S, identity(%S,%S))", // init svar, gMethodToken, currOp); redRef2->insertBefore(new CallExpr("accumulate", gMethodToken, currOp, svar)); redRef2->insertBefore(new CallExpr("chpl__reduceCombine", parentOp, currOp)); redRef2->insertBefore(new CallExpr("chpl__cleanupLocalOp", parentOp, currOp)); }
void flattenClasses(void) { // // collect nested classes // Vec<AggregateType*> nestedClasses; forv_Vec(TypeSymbol, ts, gTypeSymbols) { if (AggregateType* ct = toAggregateType(ts->type)) if (toAggregateType(ct->symbol->defPoint->parentSymbol->type)) nestedClasses.add(ct); } // // move nested classes to module level // forv_Vec(AggregateType, ct, nestedClasses) { ModuleSymbol* mod = ct->getModule(); DefExpr *def = ct->symbol->defPoint; def->remove(); mod->block->insertAtTail(def); }
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();
static void genClassIDs(Vec<TypeSymbol*> & typeSymbols) { genComment("Class Type Identification Numbers"); int count=0; forv_Vec(TypeSymbol, ts, typeSymbols) { if (AggregateType* ct = toAggregateType(ts->type)) { if (!isReferenceType(ct) && isClass(ct)) { genGlobalDefClassId(ts->cname, count); count++; } } } }
static QualifiedType returnInfoVal(CallExpr* call) { AggregateType* ct = toAggregateType(call->get(1)->typeInfo()); if (ct) { if(ct->symbol->hasFlag(FLAG_REF)) { return QualifiedType(ct->getField(1)->type, QUAL_VAL); } else if(ct->symbol->hasFlag(FLAG_WIDE_REF)) { return QualifiedType(ct->getField(2)->type, QUAL_VAL); } } INT_FATAL(call, "attempt to get value type of non-reference type"); return QualifiedType(NULL); }
// // compute topological order for types; this functions assumes that // there are no cycles and that the typeOrder map is initialized to -1 // for all class types // static int computeOrder(AggregateType* ct) { if (typeOrder.get(ct) != -1) return typeOrder.get(ct); typeOrder.put(ct, -2); int order = 0; for_fields(field, ct) { if (AggregateType* fct = toAggregateType(field->type)) { int fieldOrder = computeOrder(fct); if (fieldOrder >= order) order = fieldOrder+1; } } typeOrder.put(ct, order); return order; }
// // clear back pointers to dead ast instances // forv_Vec(TypeSymbol, ts, gTypeSymbols) { for(int i = 0; i < ts->type->methods.n; i++) { FnSymbol* method = ts->type->methods.v[i]; if (method && !isAliveQuick(method)) ts->type->methods.v[i] = NULL; if (AggregateType* ct = toAggregateType(ts->type)) { if (ct->defaultInitializer && !isAliveQuick(ct->defaultInitializer)) ct->defaultInitializer = NULL; if (ct->destructor && !isAliveQuick(ct->destructor)) ct->destructor = NULL; } } for(int i = 0; i < ts->type->dispatchChildren.n; i++) { Type* type = ts->type->dispatchChildren.v[i]; if (type && !isAlive(type)) ts->type->dispatchChildren.v[i] = NULL; } }
void cleanAst() { cleanModuleList(); // // clear back pointers to dead ast instances // forv_Vec(TypeSymbol, ts, gTypeSymbols) { for(int i = 0; i < ts->type->methods.n; i++) { FnSymbol* method = ts->type->methods.v[i]; if (method && !isAliveQuick(method)) ts->type->methods.v[i] = NULL; if (AggregateType* ct = toAggregateType(ts->type)) { if (ct->defaultInitializer && !isAliveQuick(ct->defaultInitializer)) ct->defaultInitializer = NULL; if (ct->destructor && !isAliveQuick(ct->destructor)) ct->destructor = NULL; } } for(int i = 0; i < ts->type->dispatchChildren.n; i++) { Type* type = ts->type->dispatchChildren.v[i]; if (type && !isAlive(type)) ts->type->dispatchChildren.v[i] = NULL; } } // check iterator-resume-label/goto data before nodes are free'd verifyNcleanRemovedIterResumeGotos(); verifyNcleanCopiedIterResumeGotos(); // clean the other module vectors, without deleting the ast instances (they // will be deleted with the clean_gvec call for ModuleSymbols.) clean_modvec(allModules); clean_modvec(userModules); clean_modvec(mainModules); // // clean global vectors and delete dead ast instances // foreach_ast(clean_gvec); }
// // DefExpr // bool AstDumpToHtml::enterDefExpr(DefExpr* node) { bool retval = true; if (isBlockStmt(node->parentExpr)) { fprintf(mFP, "<DL>\n"); } fprintf(mFP, " "); if (FnSymbol* fn = toFnSymbol(node->sym)) { fprintf(mFP, "<UL CLASS =\"mktree\">\n<LI>"); adjacent_passes(fn); fprintf(mFP, "<CHPLTAG=\"FN%d\">\n", fn->id); fprintf(mFP, "<B>function "); writeFnSymbol(fn); fprintf(mFP, "</B><UL>\n"); } else if (isTypeSymbol(node->sym)) { if (toAggregateType(node->sym->type)) { fprintf(mFP, "<UL CLASS =\"mktree\">\n"); fprintf(mFP, "<LI>"); if (node->sym->hasFlag(FLAG_SYNC)) fprintf(mFP, "<B>sync</B> "); if (node->sym->hasFlag(FLAG_SINGLE)) fprintf(mFP, "<B>single</B> "); fprintf(mFP, "<B>type "); writeSymbol(node->sym, true); fprintf(mFP, "</B><UL>\n"); } else { fprintf(mFP, "<B>type </B> "); writeSymbol(node->sym, true); } } else if (VarSymbol* vs = toVarSymbol(node->sym)) { if (vs->type->symbol->hasFlag(FLAG_SYNC)) fprintf(mFP, "<B>sync </B>"); if (vs->type->symbol->hasFlag(FLAG_SINGLE)) fprintf(mFP, "<B>single </B>"); fprintf(mFP, "<B>var </B> "); writeSymbol(node->sym, true); } else if (ArgSymbol* s = toArgSymbol(node->sym)) { switch (s->intent) { case INTENT_IN: fprintf(mFP, "<B>in</B> "); break; case INTENT_INOUT: fprintf(mFP, "<B>inout</B> "); break; case INTENT_OUT: fprintf(mFP, "<B>out</B> "); break; case INTENT_CONST: fprintf(mFP, "<B>const</B> "); break; case INTENT_CONST_IN: fprintf(mFP, "<B>const in</B> "); break; case INTENT_CONST_REF: fprintf(mFP, "<B>const ref</B> "); break; case INTENT_REF: fprintf(mFP, "<B>ref</B> "); break; case INTENT_PARAM: fprintf(mFP, "<B>param</B> "); break; case INTENT_TYPE: fprintf(mFP, "<B>type</B> "); break; case INTENT_BLANK: break; } fprintf(mFP, "<B>arg</B> "); writeSymbol(node->sym, true); } else if (isLabelSymbol(node->sym)) { fprintf(mFP, "<B>label</B> "); writeSymbol(node->sym, true); } else if (isModuleSymbol(node->sym)) { fprintf(mFP, "</DL>\n"); // Don't process nested modules -- they'll be handled at the top-level retval = false; } else { fprintf(mFP, "<B>def</B> "); writeSymbol(node->sym, true); } return retval; }
const char* CallInfo::toString() { bool method = false; bool _this = false; int start = 0; const char* retval = ""; if (actuals.n > 1 && actuals.head()->type == dtMethodToken) { method = true; start = 2; } if (name == astrThis) { _this = true; method = false; start = 2; } if (method == true) { if (actuals.v[1] && actuals.v[1]->hasFlag(FLAG_TYPE_VARIABLE)) { retval = astr(retval, "type ", ::toString(actuals.v[1]->type), "."); } else { retval = astr(retval, ::toString(actuals.v[1]->type), "."); } } if (developer == false && strncmp("_type_construct_", name, 16) == 0) { retval = astr(retval, name+16); } else if (developer == false && strncmp("_construct_", name, 11) == 0) { retval = astr(retval, name + 11); retval = astr(retval, ".init"); } else if (_this == false) { retval = astr(retval, name); } if (call->methodTag == false) { if (call->square == true) { retval = astr(retval, "["); } else { retval = astr(retval, "("); } } for (int i = start; i < actuals.n; i++) { Symbol* sym = actuals.v[i]; VarSymbol* var = toVarSymbol(sym); Type* type = sym->type; AggregateType* at = toAggregateType(type); IteratorInfo* ii = (at != NULL) ? at->iteratorInfo : NULL; if (i > start) { retval = astr(retval, ", "); } if (actualNames.v[i] != NULL) { retval = astr(retval, actualNames.v[i], "="); } if (type->symbol->hasFlag(FLAG_ITERATOR_RECORD) == true && ii->iterator->hasFlag(FLAG_PROMOTION_WRAPPER) == true) { retval = astr(retval, "promoted expression"); } else if (sym->hasFlag(FLAG_TYPE_VARIABLE) == true) { retval = astr(retval, "type ", ::toString(type)); } else if (var != NULL && var->immediate != NULL) { if (var->immediate->const_kind == CONST_KIND_STRING) { retval = astr(retval, "\"", var->immediate->v_string, "\""); } else { const size_t bufSize = 512; char buff[bufSize]; snprint_imm(buff, bufSize, *var->immediate); retval = astr(retval, buff); } } else { retval = astr(retval, ::toString(type)); } } if (call->methodTag == false) { if (call->square == true) { retval = astr(retval, "]"); } else { retval = astr(retval, ")"); } } return retval; }
void VarSymbol::codegenDef() { GenInfo* info = gGenInfo; if (id == breakOnCodegenID) gdbShouldBreakHere(); // Local variable symbols should never be // generated for extern or void types if (this->hasFlag(FLAG_EXTERN)) return; if (type == dtVoid) return; if( info->cfile ) { codegenDefC(); } else { #ifdef HAVE_LLVM if(isImmediate()) { llvm::GlobalVariable *globalValue = llvm::cast<llvm::GlobalVariable>( info->module->getOrInsertGlobal(cname, type->codegen().type)); globalValue->setConstant(true); if(immediate->const_kind == CONST_KIND_STRING) { if(llvm::Value *constString = codegenImmediateLLVM(immediate)) { llvm::GlobalVariable *globalString = llvm::cast<llvm::GlobalVariable>(constString); globalValue->setInitializer(llvm::cast<llvm::Constant>( info->builder->CreateConstInBoundsGEP2_32( #if HAVE_LLVM_VER >= 37 NULL, #endif globalString, 0, 0))); } else { llvm::GlobalVariable *globalString = new llvm::GlobalVariable( *info->module, llvm::IntegerType::getInt8Ty(info->module->getContext()), true, llvm::GlobalVariable::PrivateLinkage, NULL, "string"); globalString->setInitializer(llvm::Constant::getNullValue( llvm::IntegerType::getInt8Ty(info->module->getContext()))); globalValue->setInitializer(llvm::cast<llvm::Constant>( info->builder->CreateConstInBoundsGEP1_32( #if HAVE_LLVM_VER >= 37 NULL, #endif globalString, 0))); } } else { globalValue->setInitializer(llvm::cast<llvm::Constant>( codegenImmediateLLVM(immediate))); } info->lvt->addGlobalValue(cname, globalValue, GEN_VAL, ! is_signed(type)); } llvm::Type *varType = type->codegen().type; llvm::Value *varAlloca = createTempVarLLVM(varType, cname); info->lvt->addValue(cname, varAlloca, GEN_PTR, ! is_signed(type)); if(AggregateType *ctype = toAggregateType(type)) { if(ctype->isClass() || ctype->symbol->hasFlag(FLAG_WIDE_REF) || ctype->symbol->hasFlag(FLAG_WIDE_CLASS)) { if(isFnSymbol(defPoint->parentSymbol)) { info->builder->CreateStore( llvm::Constant::getNullValue(varType), varAlloca); } } } if(debug_info){ debug_info->get_variable(this); } #endif } }
void VarSymbol::codegenDefC(bool global, bool isHeader) { GenInfo* info = gGenInfo; if (this->hasFlag(FLAG_EXTERN)) return; if (type == dtVoid) return; AggregateType* ct = toAggregateType(type); QualifiedType qt = qualType(); if (qt.isRef() && !qt.isRefType()) { Type* refType = getOrMakeRefTypeDuringCodegen(type); ct = toAggregateType(refType); } if (qt.isWideRef() && !qt.isWideRefType()) { Type* refType = getOrMakeRefTypeDuringCodegen(type); Type* wideType = getOrMakeWideTypeDuringCodegen(refType); ct = toAggregateType(wideType); } Type* useType = type; if (ct) useType = ct; std::string typestr = (this->hasFlag(FLAG_SUPER_CLASS) ? std::string(toAggregateType(useType)->classStructName(true)) : useType->codegen().c); // // a variable can be codegen'd as static if it is global and neither // exported nor external. // std::string str; if(fIncrementalCompilation) { bool addExtern = global && isHeader; str = (addExtern ? "extern " : "") + typestr + " " + cname; } else { bool isStatic = global && !hasFlag(FLAG_EXPORT) && !hasFlag(FLAG_EXTERN); str = (isStatic ? "static " : "") + typestr + " " + cname; } if (ct) { if (ct->isClass()) { if (isFnSymbol(defPoint->parentSymbol)) { str += " = NULL"; } } else if (ct->symbol->hasFlag(FLAG_WIDE_REF) || ct->symbol->hasFlag(FLAG_WIDE_CLASS)) { if (isFnSymbol(defPoint->parentSymbol)) { if (widePointersStruct) { // // CHPL_LOCALEID_T_INIT is #defined in the chpl-locale-model.h // file in the runtime, for the selected locale model. // str += " = {CHPL_LOCALEID_T_INIT, NULL}"; } else { str += " = ((wide_ptr_t) NULL)"; } } } } if (fGenIDS) str = idCommentTemp(this) + str; if (printCppLineno && !isHeader && !isTypeSymbol(defPoint->parentSymbol)) str = zlineToString(this) + str; info->cLocalDecls.push_back(str); }
void buildDefaultFunctions() { build_chpl_entry_points(); SET_LINENO(rootModule); // todo - remove reset_ast_loc() calls below? std::vector<BaseAST*> asts; collect_asts(rootModule, asts); for_vector(BaseAST, ast, asts) { if (TypeSymbol* type = toTypeSymbol(ast)) { // Here we build default functions that are always generated (even when // the type symbol has FLAG_NO_DEFAULT_FUNCTIONS attached). if (AggregateType* ct = toAggregateType(type->type)) { buildFieldAccessorFunctions(ct); if (!ct->symbol->hasFlag(FLAG_REF)) buildDefaultDestructor(ct); // Classes should use the nil:<type> _defaultOf method unless they // do not inherit from object. For those types and records, call // we need a more complicated _defaultOf method generated by the // compiler if (!ct->isClass() || ct->symbol->hasFlag(FLAG_NO_OBJECT)) build_record_init_function(ct); } if (type->hasFlag(FLAG_NO_DEFAULT_FUNCTIONS)) continue; // Here we build default functions that respect the "no default // functions" pragma. if (AggregateType* ct = toAggregateType(type->type)) { buildDefaultReadWriteFunctions(ct); if (isRecord(ct)) { if (!isRecordWrappedType(ct)) { build_record_equality_function(ct); build_record_inequality_function(ct); } build_record_assignment_function(ct); build_record_cast_function(ct); build_record_copy_function(ct); build_record_hash_function(ct); } if (isUnion(ct)) build_union_assignment_function(ct); } else if (EnumType* et = toEnumType(type->type)) { //buildDefaultReadFunction(et); buildStringCastFunction(et); build_enum_cast_function(et); build_enum_assignment_function(et); build_enum_first_function(et); build_enum_enumerate_function(et); } else { // The type is a simple type. // Other simple types are handled explicitly in the module code. // But to avoid putting a catch-all case there to implement assignment // for extern types that are simple (as far as we can tell), we build // definitions for those assignments here. if (type->hasFlag(FLAG_EXTERN)) { build_extern_init_function(type->type); build_extern_assignment_function(type->type); } } } } }