/* * RESOLVE.C - Resolve parser tree * * (c)Copyright 1993-2014, Matthew Dillon, All Rights Reserved. See the * COPYRIGHT file at the base of the distribution. * * This module is responsible for resolving unresolved types and * classes, generating offsets for DeclBlock's and declarations, and * resolving type-rules for expressions in the expression tree. * * We can reach the entire tree by resolving the top-level import * statement's DeclBlock. */ #include "defs.h" #include static void ResolveClasses(Stmt *st); static void ResolveTypes(SemGroup *isg, Stmt *st); static void ResolveStorage(Stmt *st); static void errorDottedId(string_t *ary, const char *ctl, ...); static Type *resolveReturnType(SemGroup *sg); static Type *resolveArgsType(SemGroup *sg); static void resolveDecl(Declaration *d); static Exp *resolveExp(SemGroup *isg, SemGroup *sg, Exp *exp, Type *itype, int autocast); static Exp *resolveConstExp(SemGroup *isg, SemGroup *sg, Exp *exp); static Exp *resolveCompoundExp(SemGroup *isg, SemGroup *sg, Exp *exp, Type *itype); static Exp *resolveExpCast(SemGroup *isg, SemGroup *sg, Exp *exp, Type *ltype); static Exp *resolveExpOper(SemGroup *isg, SemGroup *sg, Exp *exp, Type *itype); static int resolveSemGroup1(SemGroup *sg); static void resolveSemGroup2(SemGroup *sg); static Type *resolveSuperClass(Type *super); static void resolveStorageDeclExp(Declaration *d, runesize_t *offset, runesize_t *goffset); static runesize_t resolveStorageExpOnly(Exp *exp, runesize_t *offset); static void resolveStorageExpSub(Exp *exp, runesize_t *offset); static void resolveStorageExp(Exp *exp, runesize_t *offset); static Declaration *findOper(Type *btype, string_t id, Type *ltype, Type *rtype); static Declaration *findExpOper(Exp *exp); static Declaration *findCast(Type *btype, Type *ltype, Type *rtype); static void loadExpCopy(Exp *copy, Exp *exp); static void saveExpCopy(Exp *copy, Exp *exp); static Exp *dupExpCopy(Exp *copy, Exp *exp); static void resolveStorageType(Type *type); static void resolveStorageSemGroup(SemGroup *sg, runesize_t noffset, runesize_t ngoffset); #define exLhs copy.ex_Lhs #define exRhs copy.ex_Rhs #define exFlags copy.ex_Flags #define exType copy.ex_Type #define exToken copy.ex_Token #define exDecl copy.ex_Decl #define exVisibility copy.ex_Visibility #define exId copy.ex_Id #define exArgId copy.ex_ArgId #define ADD_LVALUE(type) resolveType(AddTypeQual((type), SF_LVALUE)) #define DEL_LVALUE(type) resolveType(DelTypeQual((type), SF_LVALUE)) void ResolveProject(Parse *p, Stmt *st) { dassert_stmt(st, st->st_Op == ST_Import); ResolveClasses(st); ResolveTypes(st->st_MyGroup, st); ResolveStorage(st); p->p_Format = PFMT_RESOLVED; } /* * ResolveClasses() - Resolve superclasses and do class merge * * This code implements the most complex feature of the language: * subclassing and refinement. * * The hardest thing we have to do is 'dup' declarations and code in * order to implement subclassing and refinement. For example, a * procedure defined in Integer must be dup'd for each subclass of * Integer. We have to do this because storage requirements will * change due to both subclassing and refinement. Even auto variables * may wind up with different types between superclass and subclass. * * We must scan ST_Import and ST_Class statements. */ static void ResolveClasses(Stmt *st) { SemGroup *sg = st->st_MyGroup; /* * Resolver interlock. Assert that we aren't looping. A loop can * occur if class A embeds class B and class B embeds class A * (verses a pointer to A). */ dassert_stmt(st, (st->st_Flags & STF_RESOLVING) == 0); if (st->st_Flags & STF_RESOLVED) return; st->st_Flags |= STF_RESOLVING; /* * If this is a subclass, integrate the superclass into it */ if (st->st_Op == ST_Class && st->st_ClassStmt.es_Super) { Type *super = st->st_ClassStmt.es_Super; Stmt *sst; Declaration *d; Declaration *rd; SemGroup *tsg; /* * Locate the superclass. 'super' does not appear in any * other list.. this is a unique Type structure. */ dassert_stmt(st, super->ty_Op == TY_UNRESOLVED); do { super = resolveSuperClass(super); if (super == NULL) { fprintf(stderr, "Unable to resolve superclass of %s\n", st->st_ClassStmt.es_Decl->d_Id); dassert_stmt(st, 0); } } while (super->ty_Op == TY_UNRESOLVED); dassert_stmt(st, super->ty_Op == TY_CLASS); /* * Cleanup (XXX free qualified segments??) */ st->st_ClassStmt.es_Super = super; st->st_ClassStmt.es_Decl->d_ClassDecl.ed_Super = super; /* * Inherit internal unsigned integer and floating point flags. */ sg->sg_Flags |= super->ty_ClassType.et_SemGroup->sg_Flags & (SGF_ISUNSIGNED | SGF_ISFLOATING); /* * Locate the class statement associated with the superclass * and resolve it. */ sst = super->ty_ClassType.et_SemGroup->sg_Stmt; dassert_stmt(st, sst != NULL); dassert_stmt(st, sst->st_Op == ST_Class); ResolveClasses(sst); /* * Assign the sg_Level for the subclass. This is used * for semantic searches when a subclass is passed to a * procedure expecting the superclass. */ sg->sg_Level = sst->st_MyGroup->sg_Level + 1; /* * First move all the declarations from sg to tsg so we * can merge the superclass properly (keep all the d_Index's * correct). Note that tsg is not 100% integrated so we can * only use it for search purposes. We absolutely CANNOT * DupDeclaration() into tsg! */ tsg = AllocSemGroup(sg->sg_Parse, NULL, sg->sg_Stmt); while ((d = getHead(&sg->sg_DeclList)) != NULL) { RenameDecl(d, tsg); } sg->sg_DeclCount = 0; /* reset */ /* * Merge the superclass into this class, in sequence. * Iterate through declarations in the superclass and pull * them into the subclass. Figure out compatibility between * super and subclasses. */ for ( d = getHead(&sst->st_MyGroup->sg_DeclList); d; d = getSucc(&sst->st_MyGroup->sg_DeclList, &d->d_Node) ) { Declaration *nd; int eno = 0; dassert(d->d_Level != NULL && d->d_Level->sg_Level < sg->sg_Level); /* * See if the superclass decl conflicts with a * subclass decl. If there is no conflict pull it * into the subclass and adjust the visibility. * Note that the superclass may have duplicate ids, * but they will be at different levels if so. * * The super linkage is required when findDecl() * checks visibility of a declaration hidden relative * to our subclass, but not necessarily hidden * relative to the superclass. * * XXX overloading */ rd = FindDeclId(tsg, d->d_Id, &eno); if (rd == NULL) { nd = DupDeclaration(sg, d); dassert(d->d_Index == nd->d_Index); nd->d_ScopeFlags &= ~SCOPE_ALL_VISIBLE | super->ty_Visibility; nd->d_ScopeFlags &= ~SCOPE_REFINE; continue; } /* * If there is a conflict and we are not refining * the superclass entity, then pull it in and make * it invisible to sg_Level searches. This could * bring in multiple levels of the same id. * * Note that this may result in multiple ids, but * they will be at different levels. In this case * rd will be at the current level and nd will be * at some prior level. */ if ((rd->d_ScopeFlags & SCOPE_REFINE) == 0) { nd = DupDeclaration(sg, d); dassert(d->d_Index == nd->d_Index); nd->d_ScopeFlags &= ~(SCOPE_ALL_VISIBLE | SCOPE_REFINE); #if 0 printf(" conflict, not refined, overload\n"); #endif continue; } /* * Ok, we need to refine. But the superclass may * contain multiple levels of the same id. We only * refine the one that is visible to us. None of * these other declarations will be at the same level. */ if ((d->d_ScopeFlags & SCOPE_ALL_VISIBLE) == 0) { nd = DupDeclaration(sg, d); dassert(d->d_Index == nd->d_Index); nd->d_ScopeFlags &= ~(SCOPE_ALL_VISIBLE | SCOPE_REFINE); #if 0 printf(" conflict, refined (skip this one)\n"); #endif continue; } /* * Whew! Finally, we found the superclass decl * that we wish to refine. We had better not have * already refined it or there's something wrong * with the algorithm. * * Since we inherit the superclass method's level * our method will run in the superclass instead * of the original, but d_Super still must be set * for findDecl() to track down visibility relative * to the superclass methods. */ RenameDecl(rd, sg); dassert_decl(rd, rd->d_Super == NULL); dassert(d->d_Index == rd->d_Index); rd->d_Level = d->d_Level; rd->d_Super = d; /* * This is for the superclass method access special * case below. */ if (d->d_Op == DOP_PROC) { d->d_Flags |= DF_SUPERCOPY; } } /* * Deal with any remaining elements in tsg. These are * 'extensions' to the superclass. There may also be * invisible DOP_PROC's to handle the special superclass * method call case descibed above. */ while ((rd = getHead(&tsg->sg_DeclList)) != NULL) { if (rd->d_ScopeFlags & SCOPE_REFINE) { if (rd->d_Super == NULL) { fprintf(stderr, "Unable to refine %s, it " "does not exist in " "superclass\n", rd->d_Id); dassert_decl(rd, 0); } } RenameDecl(rd, sg); } FreeSemGroup(tsg); /* * We have to special case super.method() for a refined method. * Normally this makes the original method inaccessible (for * storage), but we want it to work for a procedure so we make * a copy in tsg. (we can't dup it directly into sg because it * will screw up the d_Index). * * We must not only clear the scope visibility and the * temporary refine flag, we also have to clear * constructor/destructor scope in the copy so only the * refined constructor/destructor is called, not both the * refined and the superclass constructor/destructor. */ for ( d = getHead(&sst->st_MyGroup->sg_DeclList); d; d = getSucc(&sst->st_MyGroup->sg_DeclList, &d->d_Node) ) { Declaration *nd; if (d->d_Flags & DF_SUPERCOPY) { d->d_Flags &= ~DF_SUPERCOPY; nd = DupDeclaration(sg, d); nd->d_ScopeFlags &= ~(SCOPE_ALL_VISIBLE | SCOPE_REFINE | SCOPE_CONSTRUCTOR | SCOPE_DESTRUCTOR); } } } else if (st->st_Op == ST_Class) { sg->sg_Level = 0; } st->st_Flags &= ~STF_RESOLVING; st->st_Flags |= STF_RESOLVED; /* * If this is an ST_Import we must recurse through it. The only * statements under an Import should be Modules. Well, really just * one module. And under that module we only care about ST_Import * and ST_Class statements. * * If this is a shared import the statement list will be empty (later * it may be used for import refinement, I dunno). This is what we * want since we should only resolve a shared import once. */ if (st->st_Op == ST_Import) { Stmt *scan; for ( scan = getHead(&st->st_List); scan; scan = getSucc(&st->st_List, &scan->st_Node) ) { Stmt *scan2; dassert_stmt(scan, scan->st_Op == ST_Module); for ( scan2 = getHead(&scan->st_List); scan2; scan2 = getSucc(&scan->st_List, &scan2->st_Node) ) { if (scan2->st_Op == ST_Import || scan2->st_Op == ST_Class) { ResolveClasses(scan2); } } } if (st->st_ImportStmt.es_DLL) { void (*func)(void) = dlsym(st->st_ImportStmt.es_DLL, "resolveClasses"); if (func) func(); } } } /* * ResolveTypes() - Resolve all types, declarations, and semantic refs * * Resolves all types, declarations, and identifiers. Additionally * this function resolves intermediate types for expressions. Storage * sizes are resolved but offsets are not assigned to declarations. */ void ResolveTypes(SemGroup *isg, Stmt *st) { /* * Deal with unresolved types here */ if (st->st_Flags & STF_SEMANTIC) { SemGroup *sg = st->st_MyGroup; Type *type; for ( type = getHead(&sg->sg_ClassList); type; type = getSucc(&sg->sg_ClassList, &type->ty_Node) ) { if (type->ty_Op == TY_UNRESOLVED) { if (resolveSuperClass(type) == NULL) { errorDottedId( type->ty_UnresType.et_DottedId, "Unable to resolve class"); dassert_stmt(st, 0); } } } } /* * Resolve statements. Don't worry about declarations, those are * handled after this switch. */ switch(st->st_Op) { case ST_Import: case ST_Module: /* * Recursively resolve contents */ { Stmt *scan; for ( scan = getHead(&st->st_List); scan; scan = getSucc(&st->st_List, &scan->st_Node) ) { /* * XXX pass isg for import, st_MyGroup for * module?? */ ResolveTypes(st->st_MyGroup, scan); } if (st->st_Op == ST_Import && st->st_ImportStmt.es_DLL) { void (*func)(void) = dlsym(st->st_ImportStmt.es_DLL, "resolveTypes"); if (func) func(); } } break; case ST_Class: resolveDecl(st->st_ClassStmt.es_Decl); break; case ST_Typedef: resolveDecl(st->st_TypedefStmt.es_Decl); break; case ST_Decl: { Declaration *d = st->st_DeclStmt.es_Decl; int i; for (i = 0; i < st->st_DeclStmt.es_DeclCount; ++i) { resolveDecl(d); d = getSucc(&d->d_MyGroup->sg_DeclList, &d->d_Node); } } break; case ST_Block: { Stmt *scan; for ( scan = getHead(&st->st_List); scan; scan = getSucc(&st->st_List, &scan->st_Node) ) { ResolveTypes(isg, scan); } } break; case ST_Nop: break; case ST_Loop: if (st->st_LoopStmt.es_Init) ResolveTypes(isg, st->st_LoopStmt.es_Init); if (st->st_LoopStmt.es_BCond) { /* * NOTE: BoolType global implies an rvalue. */ st->st_LoopStmt.es_BCond = resolveExp(isg, st->st_MyGroup, st->st_LoopStmt.es_BCond, &BoolType, 1); } if (st->st_LoopStmt.es_ACond) { /* * NOTE: BoolType global implies an rvalue. */ st->st_LoopStmt.es_ACond = resolveExp(isg, st->st_MyGroup, st->st_LoopStmt.es_ACond, &BoolType, 1); } if (st->st_LoopStmt.es_AExp) { /* * NOTE: VoidType global implies an rvalue. */ st->st_LoopStmt.es_AExp = resolveExp(isg, st->st_MyGroup, st->st_LoopStmt.es_AExp, &VoidType, 1); } if (st->st_LoopStmt.es_Body) { ResolveTypes(isg, st->st_LoopStmt.es_Body); } break; case ST_BreakCont: break; case ST_Bad: break; case ST_IfElse: /* * NOTE: BoolType global implies an rvalue. */ st->st_IfStmt.es_Exp = resolveExp(isg, st->st_MyGroup, st->st_IfStmt.es_Exp, &BoolType, 1); ResolveTypes(isg, st->st_IfStmt.es_TrueStmt); if (st->st_IfStmt.es_FalseStmt) ResolveTypes(isg, st->st_IfStmt.es_FalseStmt); break; case ST_Return: /* * NOTE: lvalue/rvalue depends on return type. */ st->st_RetStmt.es_ProcRetType = resolveReturnType(st->st_MyGroup); if (st->st_RetStmt.es_Exp) { st->st_RetStmt.es_Exp = resolveExp(isg, st->st_MyGroup, st->st_RetStmt.es_Exp, st->st_RetStmt.es_ProcRetType, 1); } break; case ST_Result: /* * NOTE: lvalue/rvalue depends on return type. */ st->st_ResStmt.es_ProcRetType = resolveReturnType(st->st_MyGroup); if (st->st_ResStmt.es_Exp) { st->st_ResStmt.es_Exp = resolveExp(isg, st->st_MyGroup, st->st_ResStmt.es_Exp, st->st_ResStmt.es_ProcRetType, 1); } break; case ST_Switch: /* * NOTE: Switch type must be an rvalue. * * NOTE: It is possible to switch on a type. See ST_Case * below for more detail. */ st->st_SwStmt.es_Exp->ex_Flags |= EXF_REQ_TYPE; st->st_SwStmt.es_Exp = resolveExp(isg, st->st_MyGroup, st->st_SwStmt.es_Exp, NULL, 0); /* * Switch-on-expression() expects an rvalue. */ if ((st->st_SwStmt.es_Exp->ex_Flags & EXF_RET_TYPE) == 0) { st->st_SwStmt.es_Exp->ex_Type = DEL_LVALUE(st->st_SwStmt.es_Exp->ex_Type); } { Stmt *scan; for ( scan = getHead(&st->st_List); scan; scan = getSucc(&st->st_List, &scan->st_Node) ) { ResolveTypes(isg, scan); } } break; case ST_Case: /* * Handle a case/default. Note that when switching on a type, * each case expression must return a type. * * NOTE: Case type must be an rvalue. We use the switch type * to cast, so it will be. */ { Stmt *scan; Exp *exp; Type *type; /* * Set type to cast cases to if we are switching on * an expression, otherwise we are switching on a * type and should not try to coerce the cases (it * doesn't make sense to). */ dassert_stmt(st, st->st_Parent->st_Op == ST_Switch); if (st->st_Parent->st_SwStmt.es_Exp->ex_Flags & EXF_RET_TYPE) type = NULL; else type = st->st_Parent->st_SwStmt.es_Exp->ex_Type; /* * case: (if es_Exp is NULL, this is a default: ) */ if ((exp = st->st_CaseStmt.es_Exp) != NULL) { if (type == NULL) exp->ex_Flags |= EXF_REQ_TYPE; exp = resolveExp(isg, st->st_MyGroup, exp, type, 1); if (type == NULL) dassert(exp->ex_Flags & EXF_RET_TYPE); st->st_CaseStmt.es_Exp = exp; } /* * Elements of the case/default */ for ( scan = getHead(&st->st_List); scan; scan = getSucc(&st->st_List, &scan->st_Node) ) { ResolveTypes(isg, scan); } } break; case ST_Exp: /* * NOTE: VoidType global implies an rvalue. * * NOTE: If resolveExp() doesn't cast to void for * us, we will do it here. */ { Exp *exp; exp = resolveExp(isg, st->st_MyGroup, st->st_ExpStmt.es_Exp, &VoidType, 1); if (exp->ex_Type != &VoidType) { exp = resolveExpCast(isg, st->st_MyGroup, exp, &VoidType); } st->st_ExpStmt.es_Exp = exp; } break; case ST_Proc: { Stmt *scan; for ( scan = getHead(&st->st_List); scan; scan = getSucc(&st->st_List, &scan->st_Node) ) { ResolveTypes(isg, scan); } } break; case ST_ThreadSched: break; default: dassert_stmt(st, 0); } } /* * Locate the ST_Proc statement and resolve & return its return type */ static Type * resolveReturnType(SemGroup *sg) { Declaration *d; Type *type; Stmt *st; /* * Locate the ST_Proc statement */ while (sg && (sg->sg_Stmt == NULL || sg->sg_Stmt->st_Op != ST_Proc)) sg = sg->sg_Parent; dassert(sg != NULL); st = sg->sg_Stmt; d = st->st_ProcStmt.es_Decl; /* decl is already resolved */ dassert_decl(d, d->d_Op == DOP_PROC); dassert_decl(d, d->d_Flags & (DF_RESOLVING|DF_RESOLVED)); type = d->d_ProcDecl.ed_Type; dassert_decl(d, type->ty_Op == TY_PROC); return(type->ty_ProcType.et_RetType); } Type * resolveArgsType(SemGroup *sg) { Declaration *d; Type *type; Stmt *st; /* * Locate the ST_Proc statement */ while (sg && (sg->sg_Stmt == NULL || sg->sg_Stmt->st_Op != ST_Proc)) sg = sg->sg_Parent; dassert(sg != NULL); st = sg->sg_Stmt; d = st->st_ProcStmt.es_Decl; /* decl is already resolved */ dassert_decl(d, d->d_Op == DOP_PROC); dassert_decl(d, d->d_Flags & (DF_RESOLVING|DF_RESOLVED)); type = d->d_ProcDecl.ed_Type; dassert_decl(d, type->ty_Op == TY_PROC); return(type->ty_ProcType.et_ArgsType); } /* * resolveDecl() - resolve a declaration * * Note: we do not resolve d_Offset here. * * If the declaration represents a procedure argument, special * processing of LVALUE scope is required to pass the declaration * by reference instead of by value. Note that the size of the * underlying type DOES NOT CHANGE... it may be much larger. */ void resolveDecl(Declaration *d) { Type *type; Stmt *st; SemGroup *sg = NULL; if (d->d_Flags & DF_RESOLVED) return; dassert_decl(d, (d->d_Flags & DF_RESOLVING) == 0); d->d_Flags |= DF_RESOLVING; switch(d->d_Op) { case DOP_CLASS: if (d->d_ClassDecl.ed_Super) { d->d_ClassDecl.ed_Super = resolveType(d->d_ClassDecl.ed_Super); } sg = d->d_ClassDecl.ed_SemGroup; if (resolveSemGroup1(sg)) resolveSemGroup2(sg); d->d_Bytes = d->d_ClassDecl.ed_SemGroup->sg_Bytes; d->d_AlignMask = d->d_ClassDecl.ed_SemGroup->sg_AlignMask; break; case DOP_ALIAS: /* * Alias access is a barrier and always returns an rvalue. */ type = d->d_AliasDecl.ed_Type = resolveType(d->d_AliasDecl.ed_Type); if (d->d_AliasDecl.ed_AssExp) { d->d_AliasDecl.ed_AssExp = resolveExp(d->d_ImportSemGroup, d->d_MyGroup, d->d_AliasDecl.ed_AssExp, DEL_LVALUE(type), 1); } /* handled in DOT and STRIND resolver */ SetDupExp(NULL, d->d_AliasDecl.ed_AssExp); break; case DOP_TYPEDEF: d->d_Flags |= DF_RESOLVED; type = resolveType(d->d_TypedefDecl.ed_Type); d->d_Flags &= ~DF_RESOLVED; d->d_TypedefDecl.ed_Type = type; break; case DOP_IMPORT: /* * This only occurs when resolving an import's semantic group. * Since we are scanning statements in that context we do not * have to recurse here, ResolveTypes() will do it for us. */ break; case DOP_PROC: /* * XXX global procedure, later on, make the argument a * type instead of storage? */ d->d_ProcDecl.ed_Type = resolveType(d->d_ProcDecl.ed_Type); /* * Deal with constructor/destructor chaining. The chaining * winds up being reversed and will be corrected by the caller. */ if (d->d_ScopeFlags & SCOPE_GLOBAL) { if (d->d_ScopeFlags & (SCOPE_CONSTRUCTOR | SCOPE_DESTRUCTOR)) { d->d_GNext = d->d_MyGroup->sg_GBase; d->d_MyGroup->sg_GBase = d; } } else { if (d->d_ScopeFlags & SCOPE_CONSTRUCTOR) { d->d_CNext = d->d_MyGroup->sg_CBase; d->d_MyGroup->sg_CBase = d; } if (d->d_ScopeFlags & SCOPE_DESTRUCTOR) { d->d_DNext = d->d_MyGroup->sg_DBase; d->d_MyGroup->sg_DBase = d; } } /* * If this procedure is bound to a DLL we have to resolve * it here. */ if (d->d_ScopeFlags & SCOPE_CLANG) { d->d_ProcDecl.ed_DLLFunc = FindDLLSymbol(NULL, d->d_ImportSemGroup, d->d_Id); } break; case DOP_ARGS_STORAGE: case DOP_STACK_STORAGE: case DOP_GLOBAL_STORAGE: case DOP_GROUP_STORAGE: type = resolveType(d->d_StorDecl.ed_Type); d->d_StorDecl.ed_Type = type; /* * Promote the lvalue storage qualifier (e.g. from a typedef) * into the declaration's scope. This is what ultimately * controls lvalue vs rvalue arguments to procedures and such. */ if ((type->ty_SQFlags & SF_LVALUE) && (d->d_ScopeFlags & SCOPE_LVALUE) == 0 ) { d->d_ScopeFlags |= SCOPE_LVALUE; } /* * Default assignment handling expects an rvalue. */ if (d->d_StorDecl.ed_AssExp) { d->d_StorDecl.ed_AssExp = resolveExp(d->d_ImportSemGroup, d->d_MyGroup, d->d_StorDecl.ed_AssExp, DEL_LVALUE(type), 1); } if (d->d_ScopeFlags & SCOPE_LVALUE) { /* * Object is passed as a LValueStor structure. Note * that d_Bytes is going to be different then the * underlying type (which represents the actual * object). */ d->d_Bytes = sizeof(LValueStor); d->d_AlignMask = LVALUE_ALIGN; } else { /* * Object is passed by value. */ d->d_AlignMask = type->ty_AlignMask; d->d_Bytes = type->ty_Bytes; } /* * If the declaration represents or contains an * argument-lvalue or a pointer we have to add it to * the SemGroup's SRBase list to properly reference or * dereference the elements. XXX only do this for non-global * storage. * * If the declaration has LVALUE scope we must do the same * because the ref is tracked. */ if (d->d_Op != DOP_GLOBAL_STORAGE && (type->ty_Flags & TF_HASLVPTR)) { d->d_SRNext = d->d_MyGroup->sg_SRBase; d->d_MyGroup->sg_SRBase = d; } else if (d->d_ScopeFlags & SCOPE_LVALUE) { d->d_SRNext = d->d_MyGroup->sg_SRBase; d->d_MyGroup->sg_SRBase = d; } /* * Deal with constructor/destructor chaining. The chaining * winds up being reversed and will be corrected by the * caller. */ if (type->ty_Flags & TF_HASCONSTRUCT) { d->d_CNext = d->d_MyGroup->sg_CBase; d->d_MyGroup->sg_CBase = d; } if (type->ty_Flags & TF_HASDESTRUCT) { d->d_DNext = d->d_MyGroup->sg_DBase; d->d_MyGroup->sg_DBase = d; } if (type->ty_Flags & (TF_HASGCONSTRUCT|TF_HASGDESTRUCT)) { d->d_GNext = d->d_MyGroup->sg_GBase; d->d_MyGroup->sg_GBase = d; } break; default: dassert_decl(d, 0); } d->d_Flags &= ~DF_RESOLVING; d->d_Flags |= DF_RESOLVED; /* * Post resolution flag resolving (to handle recursion) */ switch(d->d_Op) { case DOP_PROC: /* * Create copies of procedures as they are needed (thus * avoiding an XxY matrix effect). */ if ((st = d->d_ProcDecl.ed_OrigBody) == NULL) { Declaration *super = d->d_Super; while (super && super->d_ProcDecl.ed_OrigBody == NULL) { super = super->d_Super; } if (super) { st = super->d_ProcDecl.ed_OrigBody; if (super->d_MyGroup->sg_Stmt->st_Op == ST_Class) { /* * Copy-down a procedure from a * superclass. The procedure must * still be linked into its superclass * for semantic searches to work as * expected, hence the use of super-> * d_MyGroup and st->st_Parent. * * Note that this code is not reached * in the case of a nested procedure, * since nested procedures are copied * with the parent. */ st = DupStmt(super->d_MyGroup, st->st_Parent, st); } else { /* * Copy-down a nested procedure. * The procedure must be linked * into the copy of the parent * prodedure, not the original * parent procedure, or it will * never be resolved. */ st = DupStmt(d->d_Stmt->st_MyGroup, d->d_Stmt, st); } } else { /* * Internal procedure (we do not need to do * anything), there is no statement body * to duplicate. */ } } else { /* * Procedure is being used in the primary class it * was defined in. */ st = DupStmt(d->d_MyGroup, st->st_Parent, st); } if (st) { /* * Link the procedure body to the declaration and * resolve the procedure body. */ dassert_stmt(st, d->d_ProcDecl.ed_ProcBody == NULL); d->d_ProcDecl.ed_ProcBody = st; st->st_ProcStmt.es_Decl = d; st->st_ProcStmt.es_Scope = d->d_Scope; ResolveTypes(d->d_ImportSemGroup, st); } break; default: break; } /* * __align(%d) scope qualifier, override the type's alignment */ if ((d->d_Scope.s_Flags & SCOPE_ALIGN) && d->d_Scope.s_AlignOverride) d->d_AlignMask = d->d_Scope.s_AlignOverride - 1; } /* * resolveExp() - resolve expression * * Resolve an expression. We are expected to resolve all ex_Type's * for the expression tree as well as expected to track down * operators and base identifiers. * * itype is a type hint. If non-NULL, the caller would like our * expression to return the specified type. There are a few special * cases: * * EXF_REQ_ARRAY - when OBRACKET requests an arry optimization it * passes a post-array-indexed typehint (as if * you had done the optimization). You must ignore * itype if you are unable to do the optimization. * * NOTE: Even rvalues may have refstor side-effects at run-time. */ Exp * resolveExp(SemGroup *isg, SemGroup *sg, Exp *exp, Type *itype, int autocast) { Exp copy; loadExpCopy(©, exp); /* * note: certain cases below call other resolver functions and assume * that ex* variables are unchanged. */ dassert((exFlags & EXF_DUPEXP) || (exFlags & EXF_RESOLVED) == 0); switch(exToken) { case TOK_ASS: /* * An assignment. Note that we optimize void returns * (such as when an assignment is a statement like 'a = 4;' * ... the result of the assignment is cast to void. * * NOTE: Left-hand-side must be an LVALUE, return type * inherits this feature unless the parent turns off * the bit so the TOK_ASS run-time must deal with that. */ exLhs = resolveExp(isg, sg, exLhs, NULL, 0); dassert_exp(exLhs, exLhs->ex_Type->ty_SQFlags & SF_LVALUE); exRhs = resolveExp(isg, sg, exRhs, DEL_LVALUE(exLhs->ex_Type), 1); /* AssExp handles this optimization */ if (itype == &VoidType) { exType = itype; exFlags |= EXF_RET_VOID; } else { exType = exLhs->ex_Type; } #if 0 /* * Check @ref assignment compatibility. */ if (exLhs->ex_Type->ty_Op == TY_REFTO) { switch(MatchType(exLhs->ex_Type, exRhs->ex_Type)) { case SG_COMPAT_FULL: printf("assign %s compatibility FULL\n", exLhs->ex_Id); break; case SG_COMPAT_PART: printf("assign %s compatibility PART\n", exLhs->ex_Id); break; case SG_COMPAT_SUBCLASS: printf("assign %s compatibility SUBCL\n", exLhs->ex_Id); break; case SG_COMPAT_FAIL: printf("assign %s compatibility FAIL\n", exLhs->ex_Id); break; } } #endif break; case TOK_ANDAND: /* * NOTE: BoolType global implies an rvalue. */ exLhs = resolveExp(isg, sg, exLhs, &BoolType, 1); exRhs = resolveExp(isg, sg, exRhs, &BoolType, 1); exType = &BoolType; break; case TOK_OROR: /* * NOTE: BoolType global implies an rvalue. */ exLhs = resolveExp(isg, sg, exLhs, &BoolType, 1); exRhs = resolveExp(isg, sg, exRhs, &BoolType, 1); exType = &BoolType; break; case TOK_DECL: /* * This synthesized token occurs when we are able to collapse * a structural indirection or dotted element into a * declaration. For example, 'module.routine'. */ break; case TOK_DOT: case TOK_STRIND: /* * Structual field access. The left hand side may be an object * (class or compound), a class type, or a compound type. * * A dotted access requires an lvalue on the left hand side * if the left hand side represents storage. * * The result will be an lvalue if the right hand side * represents storage. We only loop if the right hand side * is an alias replacement. */ { string_t id; Declaration *d; SemGroup *sg2; Type *type; int globalOnly = 0; int s; int visibility; int isRefTo = 0; int procedureOnly = 0; int eno = TOK_ERR_ID_NOT_FOUND; /* * NOTE: Hint must 'always happen' since we may be * modifying an expression that will later be * Dup'd. * * NOTE: Lhs is always an lvalue for TOK_DOT, but * does not have to be for TOK_STRIND. */ exLhs->ex_Flags |= EXF_REQ_TYPE; exLhs = resolveExp(isg, sg, exLhs, NULL, 0); /* * The RHS may have been turned into a TOK_SEMGRP_ID * in a previous duplicate. The change is considered * permanent. */ if (exRhs->ex_Token != TOK_SEMGRP_ID) { dassert_exp(exRhs, exRhs->ex_Token == TOK_STRUCT_ID); exRhs = resolveExp(isg, sg, exRhs, NULL, 0); } id = exRhs->ex_Id; type = exLhs->ex_Type; /* * Calculate scope and SemGroup to search. Note * that it is legal to do a structural '.' selection * on a pointer, but it works differently then * indirecting through a pointer via '->'. In the * case of '.' on a pointer, we first search the * system Pointer class. */ if (exLhs->ex_Flags & EXF_RET_TYPE) { globalOnly = 1; } /* * Figure out the base type used to look-up the * identifier. An identifier that resolves into a * procedure winds up only being a hint for a * reference type. */ if (exToken == TOK_STRIND) { switch(type->ty_Op) { case TY_CPTRTO: type = type->ty_CPtrType.et_Type; break; case TY_PTRTO: type = type->ty_PtrType.et_Type; break; case TY_REFTO: type = type->ty_RefType.et_Type; isRefTo = 1; break; default: dassert_exp(exp, 0); /* not reached */ } } again: switch(type->ty_Op) { case TY_CLASS: sg2 = type->ty_ClassType.et_SemGroup; break; case TY_COMPOUND: sg2 = type->ty_CompType.et_SemGroup; break; case TY_ARGS: sg2 = type->ty_ArgsType.et_SemGroup; break; case TY_VAR: sg2 = type->ty_VarType.et_SemGroup; break; case TY_IMPORT: sg2 = type->ty_ImportType.et_SemGroup; break; case TY_CPTRTO: /* YYY */ dassert_exp(exp, PointerType.ty_Op == TY_CLASS); sg2 = PointerType.ty_ClassType.et_SemGroup; break; case TY_PTRTO: dassert_exp(exp, PointerType.ty_Op == TY_CLASS); sg2 = PointerType.ty_ClassType.et_SemGroup; break; case TY_REFTO: /* YYY */ dassert_exp(exp, PointerType.ty_Op == TY_CLASS); sg2 = PointerType.ty_ClassType.et_SemGroup; break; default: /* * Possibly a pointer, aka ptr.NULL */ sg2 = NULL; } visibility = exLhs->ex_Visibility; /* * Locate the identifier normally, via its type. * ty_TypeVisbility is the initial visibility (scope) * that the semantic search should use in locating * the identifier. */ if (sg2) { string_t ary[2] = { id, NULL }; int level; if (exLhs->ex_Token == TOK_ID || exLhs->ex_Token == TOK_DECL) { if (exLhs->ex_Decl->d_Search) { level = exLhs->ex_Decl-> d_Search->sg_Level; } else { level = sg2->sg_Level; } /* * XXX BIG HACK */ if (exLhs->ex_Flags & EXF_SUPER) { if (isRefTo) { fprintf(stderr, "Can't super with reference type\n"); dassert_exp(exp, 0); } if (level == 0) { fprintf(stderr, "No superclass available\n"); dassert_exp(exp, 0); } --level; } } else { level = sg2->sg_Level; /* may be -1 */ } visibility &= type->ty_Visibility; d = FindDeclPath(&exp->ex_LexRef, NULL, sg2, NULL, ary, FDC_NOBACK, &visibility, level, &eno); /* * XXX more hack. If the super is visible * and a procedure we just found our own * refinement, not the superclass method. * This is because there is no 'superclass * method' per say, refinements *REPLACE* * superclass declarations and inherit the * superclass's level. However, we still * want to be able to chain method calls so * what we do instead is go through and find * the procedure that we smacked when we did * the refinement. This procedure has * already been conveniently brought into * the subclass context as an 'invisible' * entity at the same d_Level. */ if ((exLhs->ex_Flags & EXF_SUPER) && d && d->d_Op == DOP_PROC && (d->d_ScopeFlags & SCOPE_ALL_VISIBLE) ) { string_t id2 = d->d_Id; SemGroup *olevel = d->d_Level; dassert_exp(exp, isRefTo == 0); while ((d = getSucc(&d->d_MyGroup->sg_DeclList, &d->d_Node)) != NULL) { if (d->d_Id == id2 && d->d_Level == olevel && d->d_Op == DOP_PROC) { break; } } } } else { d = NULL; } if (d && procedureOnly && d->d_Op != DOP_PROC) { fprintf(stderr, "PTR.ELEMENT may be used for special " "pointer method calls, but not to " "access storage elements. " "Use PTR->ELEMENT instead\n"); dassert_exp(exp, 0); } /* * If referencing actual storage the storage must be * declared global. */ if (d && globalOnly && (d->d_Op & DOPF_STORAGE) && (d->d_ScopeFlags & SCOPE_GLOBAL) == 0 ) { fprintf(stderr, "%s is not global. Only globals " "can be accessed through a type\n", d->d_Id); dassert_exp(exp, 0); } if (d) { /* * Identifier found. Note that if we are * going through a reference type the * declaration is not the actual one we * use at run time. It's just a template. */ resolveDecl(d); exDecl = d; exVisibility = visibility; switch(d->d_Op) { case DOP_PROC: exType = d->d_ProcDecl.ed_Type; if (d->d_ProcDecl.ed_Type->ty_SQFlags & SF_METHOD) { /* * Method call, do not * collapse the expression into * a direct declaration because * the object is needed later. */ if (exLhs->ex_Flags & EXF_RET_TYPE) ExpPrintError(exLhs, TOK_ERR_METHOD_REQUIRES_OBJ); dassert((exLhs->ex_Flags & EXF_RET_TYPE) == 0); } else if (isRefTo) { /* * Call via reference. The * lhs is required to evaluate * the actual method call at * run-time. */ } else { /* * Global method call or normal * call. For the global method * case the lhs is not needed * because the parser entered * the first argument as a * type already. * * Degenerate into a TOK_DECL. * We depend on this later. */ exFlags &= ~EXF_BINARY; exLhs = NULL; exRhs = NULL; exToken = TOK_DECL; } break; case DOP_ALIAS: exType = DEL_LVALUE(d->d_AliasDecl.ed_Type); dassert_decl(d, d->d_AliasDecl.ed_AssExp != NULL); /* * NOTE: exLhs must be NULL if exp is * unresolved. exp tree duplications * do not duplicate the alias's exLHS * even though UNARY is set. */ dassert_exp(exp, exRhs->ex_Lhs == NULL); exRhs->ex_Flags |= EXF_ALIAS | EXF_UNARY; exRhs->ex_Lhs = DupExp(sg2, d->d_AliasDecl.ed_AssExp); exRhs->ex_Lhs = resolveExp(isg, sg2, exRhs->ex_Lhs, exType, 1); break; case DOP_ARGS_STORAGE: case DOP_STACK_STORAGE: case DOP_GLOBAL_STORAGE: case DOP_GROUP_STORAGE: /* * Set type. The Rhs is a STRUCT_ID * and does not require a type to be * assigned to it. * * Return type is always an LVALUE, * parent may adjust. */ exType = ADD_LVALUE(d->d_StorDecl.ed_Type); break; case DOP_TYPEDEF: /* * XXX make sure this is only used * in the lhs of a structural * reference. XXX * * XXX what if we went through a * TY_RETO type? This type will * be wrong. * * collapse the exp node. */ exType = d->d_TypedefDecl.ed_Type; exToken = TOK_DECL; exFlags &= ~EXF_BINARY; break; case DOP_IMPORT: /* * Do not collapse an import, we * require more resolution. e.g. * import. will be collapsed, * but 'import' cannot be. */ if (exFlags & EXF_REQ_TYPE) { exType = AllocImportType( &d->d_ImportDecl.ed_SemGroup->sg_ClassList, d->d_ImportDecl.ed_SemGroup, visibility); exFlags |= EXF_RET_TYPE; break; } break; case DOP_CLASS: /* * Do not collapse a class, we require * more resolution. e.g. class. * will be collapsed, but 'class' * cannot be. */ if (exFlags & EXF_REQ_TYPE) { exType = AllocClassType( &d->d_ClassDecl.ed_SemGroup->sg_ClassList, d->d_ClassDecl.ed_Super, d->d_ClassDecl.ed_SemGroup, visibility); exFlags |= EXF_RET_TYPE; break; } break; default: dassert_exp(exp, 0); break; } } else if ((s = StrTableSpecial(id)) & SPECIALF_SEMGROUP) { /* * Identifier not found, check for a special * identifier. */ exRhs->ex_Token = TOK_SEMGRP_ID; exRhs->ex_Int32 = s; exDecl = NULL; switch(s) { case SPECIAL_NULL: dassert(type->ty_Op == TY_PTRTO || type->ty_Op == TY_REFTO || type->ty_Op == TY_CPTRTO); /* NULL is not an lvalue */ exType = DEL_LVALUE(type); exFlags |= EXF_NULL; break; case SPECIAL_COUNT: dassert(type->ty_Op != TY_PTRTO && type->ty_Op != TY_REFTO && type->ty_Op != TY_CPTRTO); exType = &Int32Type; break; case SPECIAL_DATA: /* * typeof(self.__data[]) vs * (cast)self.__data[] */ dassert(type->ty_Op != TY_PTRTO && type->ty_Op != TY_REFTO && type->ty_Op != TY_CPTRTO); dassert(exFlags & EXF_REQ_ARRAY); exFlags |= EXF_RET_ARRAY; if (exFlags & EXF_REQ_TYPE) { exFlags |= EXF_RET_TYPE; exType = &DynamicLValueType; } else if (itype) { exType = itype; } else { /* * dynamic data must be cast */ dassert_exp(exp, 0); exType = &DynamicLValueType; } break; case SPECIAL_VAR_COUNT: dassert(type->ty_Op != TY_PTRTO && type->ty_Op != TY_REFTO && type->ty_Op != TY_CPTRTO); exType = &Int32Type; break; case SPECIAL_VAR_DATA: /* * typeof(self.__vardata[]) vs * (cast)self.__vardata[] */ dassert(type->ty_Op != TY_PTRTO && type->ty_Op != TY_REFTO && type->ty_Op != TY_CPTRTO); dassert(exFlags & EXF_REQ_ARRAY); exFlags |= EXF_RET_ARRAY; if (exFlags & EXF_REQ_TYPE) { exFlags |= EXF_RET_TYPE; exType = &DynamicLValueType; } else if (itype) { exType = itype; } else { /* * dynamic data must be cast */ dassert_exp(exp, 0); exType = &DynamicLValueType; } break; case SPECIAL_TYPEID: exType = &Int32Type; break; case SPECIAL_TYPESTR: exType = &StrType; break; default: dassert_exp(exRhs, 0); break; } } else { /* * This is nasty, I admit. If we have a * pointer or reference type try again. */ exDecl = NULL; if (type->ty_Op == TY_PTRTO) { type = type->ty_PtrType.et_Type; procedureOnly = 1; goto again; } if (type->ty_Op == TY_REFTO) { type = type->ty_RefType.et_Type; procedureOnly = 1; goto again; } if (type->ty_Op == TY_CPTRTO) { type = type->ty_CPtrType.et_Type; procedureOnly = 1; goto again; } ExpPrintError(exRhs, eno); dassert(0); } } dassert_exp(exp, exType != NULL); break; case TOK_STRUCT_ID: /* * NOTE: unresolved identifiers should not have alias * expression sub-tree duplications attached to them. * assert it. */ dassert_exp(exp, exLhs == NULL); break; case TOK_OPER: /* * NOTE: LVALUE/RVALUE for elements and return type depends * on the operator. Operator functions normally * self-optimize the cases at run-time. */ saveExpCopy(©, exp); exp = resolveExpOper(isg, sg, exp, itype); loadExpCopy(©, exp); break; case TOK_PTRIND: /* * Indirect through an expression. * * Return type is typically an LVALUE (if representing * storage). Exp parent might turn it off so run-time * must test. Lhs may or may not be. */ { Type *type; exLhs = resolveExp(isg, sg, exLhs, NULL, 0); type = exLhs->ex_Type; switch(type->ty_Op) { case TY_REFTO: if ((exFlags & EXF_INDREF) == 0) { fprintf(stderr, "You cannot use '*' on a reference type\n"); dassert_exp(exLhs, 0); } exType = ADD_LVALUE(type->ty_RefType.et_Type); break; case TY_PTRTO: exType = ADD_LVALUE(type->ty_PtrType.et_Type); break; case TY_CPTRTO: exType = ADD_LVALUE(type->ty_CPtrType.et_Type); break; default: dassert_exp(exLhs, 0); break; } } break; case TOK_ADDR: /* * Take the address of an (LVALUE) expression. Returns an * RVALUE. Allow for a short-cut optimization which replaces * the TOK_ADDR sequence with its argument in the &ary[n] * case. */ { Type *type; /* * note: hint must 'always happen' since we may be * modifying an expression that will later be Dup'd. */ exLhs->ex_Flags |= EXF_REQ_ADDROF; exLhs = resolveExp(isg, sg, exLhs, NULL, 0); if (exLhs->ex_Flags & EXF_RET_ADDROF) { exp = exLhs; loadExpCopy(©, exp); } else { type = exLhs->ex_Type; dassert_exp(exLhs, type->ty_SQFlags & SF_LVALUE); exType = resolveType(TypeToPtrType(type)); /* DEL_LVALUE() not needed here */ } } break; case TOK_OBRACKET: /* * Array index, takes an RVALUE, returns an LVALUE. * * Note: we have to convert the special __data[exp] case. * * Note: ex_Flags hints must 'always happen' since we may be * modifying an expression that will later be Dup'd. */ exRhs = resolveExp(isg, sg, exRhs, NULL, 0); exLhs->ex_Flags |= EXF_REQ_ARRAY | (exFlags & EXF_REQ_TYPE); exLhs->ex_AuxExp = exRhs; exLhs = resolveExp(isg, sg, exLhs, itype, 0); if (MatchType(&IntegralType, exRhs->ex_Type) <= SG_COMPAT_PART) { ExpPrintError(exRhs, TOK_ERR_EXPECTED_INTEGRAL_TYPE); dassert_exp(exp, 0); } if (exLhs->ex_Flags & EXF_RET_ARRAY) { /* * __data and __vardata specials */ exp->ex_Token = TOK_ERR_EXP_REMOVED; return(exLhs); } else if (exFlags & EXF_REQ_ADDROF) { /* * &ary[i] optimization - allows us to create * a bounded pointer (returns an RVALUE). */ Type *type; exFlags |= EXF_RET_ADDROF; dassert((exLhs->ex_Flags & EXF_RET_TYPE) == 0); exLhs->ex_AuxExp = NULL; type = exLhs->ex_Type; switch(type->ty_Op) { case TY_ARYOF: type = type->ty_AryType.et_Type; break; case TY_CPTRTO: type = type->ty_CPtrType.et_Type; break; case TY_PTRTO: type = type->ty_PtrType.et_Type; break; case TY_REFTO: /* Cannot take address of a reference type */ dassert_exp(exp, 0); break; } exType = resolveType(TypeToPtrType(type)); /* returns an RVALUE */ } else { /* * Unoptimized array lookup, returns an lvalue */ Type *type; dassert((exLhs->ex_Flags & EXF_RET_TYPE) == 0); exLhs->ex_AuxExp = NULL; type = exLhs->ex_Type; switch(type->ty_Op) { case TY_ARYOF: type = type->ty_AryType.et_Type; break; case TY_CPTRTO: type = type->ty_CPtrType.et_Type; break; case TY_PTRTO: type = type->ty_PtrType.et_Type; break; case TY_REFTO: fprintf(stderr, "Cannot index a reference type\n"); dassert_exp(exp, 0); break; } exType = ADD_LVALUE(type); /* returns an LVALUE */ } break; case TOK_OPAREN: dassert_exp(exp, 0); /* XXX */ break; case TOK_DSTRING: /* * XXX we should return a bounded pointer here. */ exType = &StrType; exId = StrTableEscapeQuotedString(exId, strlen(exId), 1); ReplaceStrTable(&exp->ex_Id, exId); break; case TOK_SSTRING: exType = &CharType; exId = StrTableEscapeQuotedString(exId, strlen(exId), 0); dassert(StrTableLen(exId) == 1); ReplaceStrTable(&exp->ex_Id, exId); break; case TOK_BSTRING: /* reserved for inline execution or something else */ dassert_exp(exp, 0); /* XXX */ break; case TOK_INTEGER: { char *ptr; strtol(exp->ex_Id, &ptr, 0); if (*ptr == '.') { int doUnsigned = 0; ++ptr; if (*ptr == 'U') { ++ptr; doUnsigned = 1; exFlags |= EXF_UNSIGNED; } if (*ptr == 'B') { exType = (doUnsigned) ? &UInt8Type : &Int8Type; } else if (*ptr == 'W') { exType = (doUnsigned) ? &UInt16Type : &Int16Type; } else if (*ptr == 'L') { exType = (doUnsigned) ? &UInt64Type : &Int64Type; } else if (*ptr == 0) { exType = (doUnsigned) ? &UInt32Type : &Int32Type; } else { dassert_exp(exp, 0); } } else { exType = &Int32Type; } } break; case TOK_FLOAT: { char *ptr; strtod(exp->ex_Id, &ptr); if (*ptr == '.') { ++ptr; if (*ptr == 'F') { exType = &Float32Type; } else if (*ptr == 'D') { exType = &Float64Type; } else if (*ptr == 'X') { exType = &Float128Type; } else { dassert_exp(exp, 0); } } else { exType = &Float64Type; } } break; case TOK_VOIDEXP: exType = &VoidType; break; case TOK_SELF: /* * The self identifier represents the current procedure's * arguments. A varargs procedure will actually be called * with an extended version of this type, but for resolution * purposes we can use this time. * * This is an LVALUE to support things like self.new() XXX. */ exType = ADD_LVALUE(resolveArgsType(sg)); break; case TOK_DOLLAR: /* * The '$' identifier represents the current procedure's * return storage. */ exType = ADD_LVALUE(resolveReturnType(sg)); break; case TOK_ID: case TOK_CLASSID: /* * Lookup the identifier. The returned declaration could * represent a class, typedef, module, or storage, but for * this case we only allow storage or a constant. Since * we are starting from our own semantic group, visibility * is initially ALL (private, library, and public). * * The identifier might represent something at a higher scoping * layer. For example, a nested procedure accessing a variable * in the parent procedure or a method procedure in a class * accessing an element of the object. * * This returns an LVALUE if the id represents storage. */ { string_t ary[2]; int eno = TOK_ERR_ID_NOT_FOUND; exDecl = NULL; /* * Special case 'super'. XXX TY_REFTO * * Make an in-place change to the expression * structure. 'super' is actually 'this' with the * EXF_SUPER flag set. */ if (exId == String_Super) { exId = String_This; ReplaceStrTable(&exp->ex_Id, exId); exFlags |= EXF_SUPER; } ary[0] = exp->ex_Id; ary[1] = NULL; exDecl = FindDeclPath(&exp->ex_LexRef, isg, sg, NULL, ary, FDC_NULL, &exVisibility, -1, &eno); if (exDecl == NULL) { ExpPrintError(exp, eno); dassert_exp(exp, 0); } } switch(exDecl->d_Op) { case DOP_ARGS_STORAGE: case DOP_STACK_STORAGE: case DOP_GLOBAL_STORAGE: case DOP_GROUP_STORAGE: /* * Storage identifiers are lvalues. */ exType = ADD_LVALUE(exDecl->d_StorDecl.ed_Type); break; case DOP_ALIAS: /* * Aliases are rvalues (even if they could be lvalues). */ exType = DEL_LVALUE(exDecl->d_AliasDecl.ed_Type); exFlags |= EXF_ALIAS | EXF_UNARY; /* * NOTE: exLhs must be NULL if exp is unresolved. * exp tree duplications do not duplicate * the alias's exLHS even though UNARY is set. */ dassert_exp(exp, exLhs == NULL); exLhs = DupExp(sg, exDecl->d_AliasDecl.ed_AssExp); exLhs = resolveExp(isg, sg, exLhs, exType, 1); /* * Inherit EXF_NULL (NULL pointer special) through * the alias, otherwise it will not be assignable * to arbitrary pointers. */ exFlags |= exLhs->ex_Flags & EXF_NULL; break; case DOP_PROC: /* * A procedural identifier. * * Note: procedural pointers cannot be changed so * they are not lvalues. */ dassert_exp(exp, (exFlags & EXF_REQ_PROC)); exType = exDecl->d_ProcDecl.ed_Type; break; case DOP_TYPEDEF: if (exFlags & EXF_REQ_TYPE) { exType = exDecl->d_TypedefDecl.ed_Type; exFlags |= EXF_RET_TYPE; break; } dassert_exp(exp, 0); break; case DOP_CLASS: if (exFlags & EXF_REQ_TYPE) { exType = AllocClassType( &exDecl->d_ClassDecl.ed_SemGroup->sg_ClassList, exDecl->d_ClassDecl.ed_Super, exDecl->d_ClassDecl.ed_SemGroup, exVisibility); exFlags |= EXF_RET_TYPE; break; } dassert_exp(exp, 0); break; case DOP_IMPORT: if (exFlags & EXF_REQ_TYPE) { exType = AllocImportType( &exDecl->d_ImportDecl.ed_SemGroup->sg_ClassList, exDecl->d_ImportDecl.ed_SemGroup, exVisibility); exFlags |= EXF_RET_TYPE; break; } dassert_exp(exp, 0); break; default: dassert_exp(exp, 0); } break; case TOK_NOT: /* * NOTE: BoolType global implies an rvalue. */ exLhs = resolveExp(isg, sg, exLhs, &BoolType, 1); break; case TOK_TYPE: if (exFlags & EXF_REQ_TYPE) { exType = resolveType(exType); exFlags |= EXF_RET_TYPE; } else { dassert_exp(exp, 0); } break; case TOK_CAST: /* * User cast (or maybe the parser inserted it). Try to * resolve the expression with the requested type hint * but tell resolveExp() not to force the cast. * * Then check the result. If resolveExp() was not able to * optimize the requested cast then resolve the cast. * * If the types are compatible we still keep the TOK_CAST * node in place for the moment. XXX we really need to * formalized how ex_Type is set Similar vs Exact. * * NOTE: Cast results are always an RVALUE. XXX validate here. */ if ((exFlags & EXF_PARSE_TYPE) == 0) { exRhs->ex_Flags |= EXF_REQ_TYPE; exRhs = resolveExp(isg, sg, exRhs, NULL, 0); exType = exRhs->ex_Type; } exLhs = resolveExp(isg, sg, exLhs, exType, 0); if (SimilarType(exType, exLhs->ex_Type) == 0) { saveExpCopy(©, exp); exp = resolveExpCast(isg, sg, exLhs, exType); loadExpCopy(©, exp); } #if 0 /* propagate NULL flag to allow cast to any pointer type */ if (exLhs->ex_Flags & EXF_NULL) printf("LHS NULL\n"); exp->ex_Flags |= exLhs->ex_Flags & EXF_NULL; #endif break; case TOK_CALL: /* * Calls require the RHS to be a compound expression * representing the procedure arguments. * * XXX deal with pointer-to-function verses function * XXX the lhs must at the moment resolve to the procedure * itself. * * In regards to procedure pointers, the declaration * will require a pointer to the procedure's statement * body. XXX this pointer can be the physical storage * associated with the lhs data but thus requires the * type to be a pointer. We do not support the 'C' * (*ptr_to_func)(...) form. You have to use ptr_to_func(...). */ { Type *ltype; dassert_exp(exRhs, exRhs->ex_Token == TOK_COMPOUND); /* * Note: ex_Flags hints must 'always happen' since * we may be modifying an expression that will later * be Dup'd. */ exLhs->ex_Flags |= EXF_REQ_PROC; exLhs = resolveExp(isg, sg, exLhs, NULL, 0); ltype = exLhs->ex_Type; #if 0 if (ltype->ty_Op == TY_PTRTO) ltype = type->ty_PtrType.et_Type; /* XXX */ #endif #if 0 dassert_exp(exLhs, exLhs->ex_Token == TOK_DECL || exLhs->ex_Token == TOK_ID); #endif dassert_exp(exLhs, ltype != NULL && ltype->ty_Op == TY_PROC); dassert_exp(exLhs, exLhs->ex_Decl != NULL); dassert_exp(exRhs, exRhs->ex_Token == TOK_COMPOUND); /* * If the lhs type indicates a method procedure, then * it's lhs is the object we wish to pass as the * first argument to the method. We move the lhs lhs * exp. For a STRIND TY_PTRTO method call we * indirect the element and convert it to a TOK_DOT * lvalue argument of the underlying object. * * A method call via a reference object is a very * weird case. * * Since the method called through an object winds up * being a method taylored for that object, and we * are calling through a reference to an object, * the actual method will be looked up at run time * and will match the object. Thus we can safely * indirect through the reference object for this * one case. Since (*ref_obj) is not normally * allowed this will be special-cased at * compile/run-time. * * Note that this occurs before we evaluate the * compound expression on the right hand side. Also * note that since the resolver can be called multiple * times on a shared expression, we have to be * careful to shift the arguments around only once. */ if ((ltype->ty_SQFlags & SF_METHOD) && (exRhs->ex_Flags & EXF_CALL_CONV) == 0 ) { Exp *obj; exRhs->ex_Flags |= EXF_CALL_CONV; switch(exLhs->ex_Token) { case TOK_STRIND: /* indirect */ obj = exLhs->ex_Lhs; if (obj->ex_Type->ty_Op == TY_PTRTO) { Exp *nexp = AllocExp(NULL); nexp->ex_Lhs = obj; nexp->ex_Token = TOK_PTRIND; nexp->ex_Type = ADD_LVALUE(obj->ex_Type->ty_PtrType.et_Type); nexp->ex_Flags |= EXF_RESOLVED | EXF_UNARY; LexDupRef(&obj->ex_LexRef, &nexp->ex_LexRef); exLhs->ex_Token = TOK_DOT; obj = nexp; } else if (obj->ex_Type->ty_Op == TY_CPTRTO) { Exp *nexp = AllocExp(NULL); nexp->ex_Lhs = obj; nexp->ex_Token = TOK_PTRIND; nexp->ex_Type = ADD_LVALUE(obj->ex_Type->ty_CPtrType.et_Type); nexp->ex_Flags |= EXF_RESOLVED | EXF_UNARY; LexDupRef(&obj->ex_LexRef, &nexp->ex_LexRef); exLhs->ex_Token = TOK_DOT; obj = nexp; } else if (obj->ex_Type->ty_Op == TY_REFTO) { Exp *nexp = AllocExp(NULL); nexp->ex_Lhs = obj; nexp->ex_Token = TOK_PTRIND; nexp->ex_Type = ADD_LVALUE(obj->ex_Type->ty_RefType.et_Type); nexp->ex_Flags |= EXF_RESOLVED | EXF_UNARY | EXF_INDREF; LexDupRef(&obj->ex_LexRef, &nexp->ex_LexRef); obj = nexp; } else { dassert_exp(obj, 0); } break; case TOK_DOT: /* pass directly as lvalue */ obj = exLhs->ex_Lhs; break; default: dassert_exp(exp, 0); obj = NULL; break; } /* * Leave the lhs intact, but set the * duplication flag in case things get * nasty later. */ exLhs->ex_Lhs = SetDupExp(sg, exLhs->ex_Lhs); obj->ex_Next = exRhs->ex_Lhs; exRhs->ex_Lhs = obj; } /* * Resolve the right hand side, which are the * procedure arguments as a compound type. This * can get tricky. XXX * * NOTE: We inherit the SF_LVALUE flag from the * return type. Parent might turn it off. */ /*d = exLhs->ex_Decl;*/ exRhs = resolveExp(isg, sg, exRhs, ltype->ty_ProcType.et_ArgsType, 1); exType = ltype->ty_ProcType.et_RetType; } break; case TOK_COMPOUND: /* * (NOTE EARLY RETURN) * * A compound expression should always be an RVALUE, but * might contain LVALUEs (XXX). */ exp = resolveCompoundExp(isg, sg, exp, itype); return(exp); /* not reached */ case TOK_TYPEOF: /* * The caller must be able to handle a type return when * typeof() is used. */ dassert_exp(exp, exFlags & EXF_REQ_TYPE); /* fall through */ case TOK_SIZEOF: case TOK_ARYSIZE: /* * If an expression was supplied, convert it to a type. * * NOTE: ex_Flags hints must 'always happen' since we may be * modifying an expression that will later be Dup'd. */ if ((exFlags & EXF_RET_TYPE) == 0) { dassert(exLhs != NULL); exLhs->ex_Flags |= EXF_REQ_TYPE; exLhs = resolveExp(isg, sg, exLhs, NULL, 0); exType = exLhs->ex_Type; exFlags &= ~EXF_UNARY; exFlags |= EXF_RET_TYPE; /* XXX delete the lhs */ } else { exType = resolveType(exType); } switch(exToken) { case TOK_SIZEOF: exId = StrTableInt(exType->ty_Bytes); exp->ex_Token = exToken = TOK_INTEGER; exType = &SizeType; exFlags &= ~EXF_RET_TYPE; break; case TOK_ARYSIZE: dassert_exp(exp, exType->ty_Op == TY_ARYOF); if (exType->ty_AryType.et_Type->ty_Bytes) { exId = StrTableInt(exType->ty_Bytes / exType->ty_AryType.et_Type->ty_Bytes); } else { exId = StrTableInt(0); } exp->ex_Token = exToken = TOK_INTEGER; exType = &SizeType; exFlags &= ~EXF_RET_TYPE; break; case TOK_TYPEOF: /* type is returned */ break; } break; default: dassert_exp(exp, 0); break; } /* * Resolve the type. */ if (exType) { exType = resolveType(exType); /* XXX exType was ex_Type */ /* * If we can autocast and itype is compound and the * expression turns out to not be compound, then the Rune * language converts the expression to compound. This way * (int x, int y = 2) = ( 7 ); works because ( 7 ) is * parsed as a standard expression and thus not compound. * * (another way to do it would be to require that the element * be named, i.e. (x:7), which is compound after standard * parsing). */ if (autocast && itype && itype->ty_Op == TY_COMPOUND && exType->ty_Op != TY_COMPOUND) { saveExpCopy(©, exp); exp = ExpToCompoundExp(exp, TOK_COMPOUND); exp = resolveCompoundExp(isg, sg, exp, itype); loadExpCopy(©, exp); } /* * If the type hint did not succeed we may have to cast the * expression to the requested type. Note that if the itype * was set as part of an array optimization request which could * not be handled, we must ignore itype. * * Note that SimilarType() will allow exp->ex_Type to be a * var-args TY_ARGS, and since the original Rhs of a call * is set to the procedure arguments type, VarType.et_Type * should match exactly. */ if (itype && (exFlags & (EXF_REQ_ARRAY|EXF_RET_ARRAY)) != EXF_REQ_ARRAY ) { if ((itype->ty_Flags & TF_RESOLVED) == 0) itype = resolveType(itype); if ((itype->ty_SQFlags & SF_LVALUE) && (exType->ty_SQFlags & SF_LVALUE) == 0 ) { /* XXX */ fprintf(stderr, "Exp must be an lvalue here\n"); dassert_exp(exp, 0); } if (!SimilarType(itype, exType) && autocast) { if (exp->ex_Flags & EXF_DUPEXP) { Exp *nexp = AllocExp(NULL); nexp->u = exp->u; LexDupRef(&exp->ex_LexRef, &nexp->ex_LexRef); exp = nexp; exFlags &= ~EXF_DUPEXP; /*exp = DupExp(sg, exp);*/ } exFlags |= EXF_RESOLVED; exp = dupExpCopy(©, exp); exp = resolveExpCast(isg, sg, exp, itype); loadExpCopy(©, exp); } } } exFlags |= EXF_RESOLVED; exp = dupExpCopy(©, exp); return(exp); } Exp * resolveConstExp(SemGroup *isg, SemGroup *sg, Exp *exp) { Declaration *d; exp = resolveExp(isg, sg, exp, NULL, 0); if (exp->ex_Token != TOK_ID) goto done; d = exp->ex_Decl; if ((d->d_StorDecl.ed_Type->ty_SQFlags & SF_CONST) == 0) goto done; switch(d->d_Op) { case DOP_ARGS_STORAGE: case DOP_STACK_STORAGE: case DOP_GLOBAL_STORAGE: case DOP_GROUP_STORAGE: exp = d->d_StorDecl.ed_AssExp; break; default: break; } done: if (exp->ex_Token != TOK_INTEGER) { ExpPrintError(exp, TOK_ERR_EXPECTED_INTEGRER_CONST); dassert_exp(exp, 0); } return exp; } /* * resolveCompoundExp() - resolve a compound expression (called from * resolveExp() and resolveExpOper()). * * Resolve a compound expression. Compound expressions require * a compound type to normalize against. This will work for * direct assignments, return values, casts, and procedure arguments * only. * * NOTE: We can't use itype if EXF_REQ_ARRAY is specified because * its hinting for the array optimization case, which we cannot do. * * Compound expressions may be used in conjuction with types * reprsenting classes, compound types, and procedure arguments. The * compound expression may contain subclasses of the superclasses expected * by itype. This is only allowed if the procedure's body has not yet been * generated (for example, a method call in a subclass). * * Partially resolved operators are typically converted into procedure calls * and method calls are also partially resolved, so some elements may already * be resolved. * * XXX named initialization, missing elements (structural * initialization), and so forth needs to be dealt with. */ Exp * resolveCompoundExp(SemGroup *isg, SemGroup *sg, Exp *exp, Type *itype) { Exp **pscan; Exp *scan; Declaration *d; SemGroup *sg2; int varargs = 0; Type *type; /* * Expression dup()ing */ if (exp->ex_Flags & EXF_DUPEXP) { #if DUPEXP_DEBUG static int count; fprintf(stderr, "DUPEXPC %d\n", ++count); #endif exp = DupExp(sg, exp); } if (itype && (exp->ex_Flags & EXF_REQ_ARRAY) == 0) exp->ex_Type = itype; /* * If we don't have a SemGroup to normalize against, XXX how should * we normalize the compound expression? */ if (exp->ex_Type == NULL) { dassert_exp(exp, 0); } /* * Normalize the compound expression based on the * argument types expected by the procedure. We have * to resolve the type before we start the scan in order * to ensure that d_Offset is properly assigned. * * Use the declarations found in the compound type * semantic group to coerce the procedure arguments to * generate the correct compound type. Note that resolveExp() * recursion must still use the SemGroup that was passed to us. * * XXX deal with defaults and pre-resolved arguments. XXX */ type = exp->ex_Type = resolveType(exp->ex_Type); switch(type->ty_Op) { case TY_ARGS: sg2 = type->ty_ArgsType.et_SemGroup; break; case TY_VAR: sg2 = type->ty_VarType.et_SemGroup; break; case TY_COMPOUND: sg2 = type->ty_CompType.et_SemGroup; break; case TY_CLASS: sg2 = type->ty_ClassType.et_SemGroup; break; default: dassert_exp(exp, 0); sg2 = NULL; /* NOT REACHED */ break; } pscan = &exp->ex_Lhs; /* * Scan the compound expression and match it up against the compound * type. */ d = getHead(&sg2->sg_DeclList); while ((scan = *pscan) != NULL) { if (scan->ex_ArgId != NULL) { /* * Named argument, find it * * (Overloading not allowed) */ int eno = TOK_ERR_ID_NOT_FOUND; d = FindDeclId(sg2, scan->ex_ArgId, &eno); if (d == NULL) { ExpPrintError(scan, eno); dassert(0); } } else { /* * Unnamed argument, run through sequentially. Skip * any non-storage or global storage. */ while (d && d->d_Op != DOP_ARGS_STORAGE && d->d_Op != DOP_STACK_STORAGE && d->d_Op != DOP_GROUP_STORAGE ) { d = getSucc(&sg2->sg_DeclList, &d->d_Node); } /* * Ran out of storage declarations. If this is a * var-args SemGroup then we actually create a new * SemGroup (and eventually a new type) to represent * it. * * We then extend the varargs SemGroup. This isn't * pretty. */ if (d == NULL) { if (varargs == 0 && (sg2->sg_Flags & SGF_VARARGS)) { sg2 = DupSemGroup(sg2->sg_Parent, NULL, sg2, 1); #if 0 if (resolveSemGroup1(sg3)) resolveSemGroup2(sg3); sg2 = sg3; #endif varargs = 1; } if (varargs == 0) { fprintf(stderr, "Too many arguments in " "expression\n"); dassert_exp(scan, 0); } } } /* * Unlink the expression from the compound list temporarily * so we can safely resolve it. Either cast the expression * to the compound element, or create a compound element * (e.g. varargs call) to match the expression. * * Due to the resolver moving things around, the elements of * a compound expression are sometimes resolved multiple times. */ *pscan = scan->ex_Next; scan->ex_Next = NULL; if (d) { Type *stype = d->d_StorDecl.ed_Type; int autocast; /* * HACK! XXX YYY */ if (SimilarType(stype, &PointerType) && (stype->ty_SQFlags & SF_LVALUE) == SF_LVALUE ) { stype = NULL; autocast = 0; } else { autocast = 1; } if ((scan->ex_Flags & EXF_RESOLVED) == 0) { scan = resolveExp(isg, sg, scan, stype, autocast); } else if (stype) { /* * Since we have already resolved the * expression we need to do the same sanity * checking that it would do to cast. */ dassert_exp(scan, (stype->ty_SQFlags & SF_LVALUE) == 0 || (scan->ex_Type->ty_SQFlags & SF_LVALUE)); if (!SimilarType(stype, scan->ex_Type)) { scan = resolveExpCast(isg, sg, scan, stype); } } } else { Scope tscope = INIT_SCOPE(0); if ((scan->ex_Flags & EXF_RESOLVED) == 0) scan = resolveExp(isg, sg, scan, NULL, 0); dassert(varargs != 0); d = AllocDeclaration(sg2, DOP_ARGS_STORAGE, &tscope); d->d_StorDecl.ed_Type = DEL_LVALUE(scan->ex_Type); ++sg2->sg_VarCount; d->d_Bytes = scan->ex_Type->ty_Bytes; d->d_AlignMask = scan->ex_Type->ty_AlignMask; /* * __align(%d) scope qualifier, override the type's * alignment */ if ((d->d_Scope.s_Flags & SCOPE_ALIGN) && d->d_Scope.s_AlignOverride) { d->d_AlignMask = d->d_Scope.s_AlignOverride - 1; } #if 0 sg2->sg_Bytes = (sg2->sg_Bytes + d->d_AlignMask) & ~d->d_AlignMask; #endif d->d_Offset = sg2->sg_Bytes; #if 0 sg2->sg_Bytes += d->d_Bytes; if (sg2->sg_AlignMask < d->d_AlignMask) sg2->sg_AlignMask = d->d_AlignMask; #endif } scan->ex_Next = *pscan; *pscan = scan; /* * If the declaration requires an LVALUE, assert that * we have an lvalue. Otherwise set the direct-store * request (also see InterpCompoundExp). */ if (d->d_ScopeFlags & SCOPE_LVALUE) { if ((scan->ex_Type->ty_SQFlags & SF_LVALUE) == 0) fprintf(stderr, "argument must be an lvalue\n"); dassert_exp(scan, scan->ex_Type->ty_SQFlags & SF_LVALUE); } /* * accounting */ d = getSucc(&sg2->sg_DeclList, &d->d_Node); pscan = &scan->ex_Next; } /* * Resolve the varargs sg2 after building it. */ if (varargs) { if (resolveSemGroup1(sg2)) resolveSemGroup2(sg2); resolveStorageSemGroup(sg2, 0, 0); } /* * If we made a var-args call, adjust the expression's type */ if (varargs) { dassert(type->ty_Op == TY_ARGS); exp->ex_Type = resolveType(TypeToVarType(type, sg2)); } exp->ex_Flags |= EXF_RESOLVED; return(exp); } /* * resolveExpCast() - Cast the expression to the specified type and return * the cast expression. * * Note that expression nodes depend on their ex_Type being correct, * and also expressions may be shared, so be careful not to modify the * ex_Type (or anything else) in the existing expression. * * This code is somewhat different then resolveExpOper() and friends. * The Exp argument has already been resolved so do not resolve it * again, and the cast type already has SF_LVALUE set or cleared as * appropriate (had better be cleared!) * * As with operators we have to locate the cast declaration matching * the cast we want to do. */ static Exp * resolveExpCast(SemGroup *isg, SemGroup *sg, Exp *exp, Type *ltype) { Type *rtype; Declaration *d; rtype = exp->ex_Type; dassert(rtype && ltype); /* * XXX attempt to cast from subclass to superclass? */ /* * XXX look in our local semantic hierarchy for a compatible cast ? */ /* * Look in the right hand (source) type for the cast */ d = findCast(rtype, ltype, rtype); /* * If that fails then look in the left hand (destination) type for * the cast. */ if (d == NULL) { d = findCast(ltype, ltype, rtype); } #if 1 if (d == NULL && (rtype->ty_Op == TY_PTRTO || rtype->ty_Op == TY_REFTO || rtype->ty_Op == TY_CPTRTO)) { d = findCast(&PointerType, ltype, rtype); } #endif if (d == NULL) { /* * We could not find a specific cast operator. There are * some inherent casts that we can do. We run through these * in attempt to come up with matching types. */ if (ltype->ty_Op != rtype->ty_Op && (ltype->ty_Op == TY_PTRTO || ltype->ty_Op == TY_CPTRTO || ltype->ty_Op == TY_ARYOF) && (rtype->ty_Op == TY_PTRTO || rtype->ty_Op == TY_CPTRTO || rtype->ty_Op == TY_ARYOF)) { /* * Pointers, C pointers, or arrays can be cast to * pointers, C pointers, or arrays of the same type. * * Cast the right hand type to an equivalent * pointer/cpointer/array of the right hand type * and re-resolve the cast. */ exp = ExpToCastExp(exp, resolveType(ChangeType(rtype, ltype->ty_Op))); return(resolveExpCast(isg, sg, exp, ltype)); } else if (MatchType(ltype, rtype) <= SG_COMPAT_PART) { /* * If the types are compatible (casting rtype->ltype), * we can cast trivially. */ exp = ExpToCastExp(exp, ltype); } else if (MatchType(&NumericType, ltype) <= SG_COMPAT_PART && MatchType(&NumericType, rtype) <= SG_COMPAT_PART) { /* * Casting from one numeric type to another must be * supported by the interpreter/compiler. */ exp = ExpToCastExp(exp, ltype); } else if (SimilarType(&VoidType, ltype)) { /* * Casting anything to void is allowed (throwing the * object away). E.g. statement-expressions. */ exp = ExpToCastExp(exp, ltype); } else if (SimilarType(&VoidPtrType, ltype)) { /* * Casting a pointer to a (void *) is trivial, but is * only allowed if the underlying structure does not * contain any pointers. * * NOTE: Generally only used when a pointer is being * cast to an integer. Rune does not allow * casting back to other pointer types. * * XXX validate integral # of objects fit in pointer * range. */ dassert_exp(exp, (rtype->ty_PtrType.et_Type->ty_Flags & TF_HASLVPTR) == 0); exp = ExpToCastExp(exp, ltype); } else if (SimilarType(&VoidRefType, ltype)) { /* * Casting a pointer to a (void @) is trivial. * * NOTE: Generally only used when a pointer is being * cast to an integer. Rune does not allow * casting back to other pointer types. * * XXX validate integral # of objects fit in pointer * range. */ dassert_exp(exp, (rtype->ty_RefType.et_Type->ty_Flags & TF_HASLVPTR) == 0); exp = ExpToCastExp(exp, ltype); } else if (SimilarType(rtype, &VoidPtrType)) { /* * Casting from a void pointer may not be trivial * but we leave it up to the interpreter/compiler. * * Only allow if the target does not contain any * pointers or if the right-hand-side is NULL. * * XXX validate integral # of objects fit in pointer * range. */ switch(ltype->ty_Op) { case TY_REFTO: if ((exp->ex_Flags & EXF_NULL) == 0) dassert_exp(exp, (ltype->ty_RefType.et_Type->ty_Flags & TF_HASLVPTR) == 0); break; case TY_PTRTO: if ((exp->ex_Flags & EXF_NULL) == 0) dassert_exp(exp, (ltype->ty_PtrType.et_Type->ty_Flags & TF_HASLVPTR) == 0); break; case TY_CPTRTO: break; default: break; } exp = ExpToCastExp(exp, ltype); } else if (ltype->ty_Op == rtype->ty_Op && (ltype->ty_Op == TY_PTRTO || ltype->ty_Op == TY_CPTRTO || ltype->ty_Op == TY_ARYOF)) { /* * We allow casts of pointers to similar numeric * types if they are the same size, though this is * really rather a hack. This is mainly to handle * the signed<->unsigned cast case. XXX */ int ok = 0; switch(ltype->ty_Op) { case TY_PTRTO: if (MatchType(&NumericType, ltype->ty_PtrType.et_Type) <= SG_COMPAT_SUBCLASS && MatchType(&NumericType, rtype->ty_PtrType.et_Type) <= SG_COMPAT_SUBCLASS && ltype->ty_Bytes == rtype->ty_Bytes ) { exp = ExpToCastExp(exp, ltype); ok = 1; } break; case TY_CPTRTO: if (MatchType(&NumericType, ltype->ty_CPtrType.et_Type) <= SG_COMPAT_SUBCLASS && MatchType(&NumericType, rtype->ty_CPtrType.et_Type) <= SG_COMPAT_SUBCLASS && ltype->ty_Bytes == rtype->ty_Bytes ) { exp = ExpToCastExp(exp, ltype); ok = 1; } break; case TY_ARYOF: if (MatchType(&NumericType, ltype->ty_AryType.et_Type) <= SG_COMPAT_SUBCLASS && MatchType(&NumericType, rtype->ty_AryType.et_Type) <= SG_COMPAT_SUBCLASS && ltype->ty_Bytes == rtype->ty_Bytes ) { exp = ExpToCastExp(exp, ltype); ok = 1; } break; } if (ok == 0) { fprintf(stderr, "Unable to resolve cast from pointers " "to dissimilar numeric types " "%s to %s\n", TypeToStr(rtype, NULL), TypeToStr(ltype, NULL)); dassert_exp(exp, 0); } } else { fprintf(stderr, "Unable to resolve cast from %s to %s\n", TypeToStr(rtype, NULL), TypeToStr(ltype, NULL)); dassert_exp(exp, 0); } } else if (d->d_ScopeFlags & SCOPE_INTERNAL) { /* * We found a cast operator and it is an internal operator */ exp = ExpToCastExp(exp, ltype); exp->ex_Decl = d; } else { /* * We found a cast operator and it is a Rune cast procedure. We * must convert the cast to a procedure call. If we want * resolveCompoundExp() to be able to generate a compatible * procedure (in a subclass) we have to tell it about the * procedure. */ Exp *sexp; sexp = ExpToCompoundExp(exp, TOK_COMPOUND); if (d->d_ProcDecl.ed_ProcBody == NULL) sexp->ex_Decl = d; sexp = resolveCompoundExp(isg, sg, sexp, d->d_ProcDecl.ed_Type->ty_ProcType.et_ArgsType); exp = AllocExp(NULL); exp->ex_Lhs = AllocExp(NULL); exp->ex_Lhs->ex_Token = TOK_DECL; exp->ex_Lhs->ex_Id = d->d_Id; exp->ex_Lhs->ex_Decl = d; exp->ex_Lhs->ex_Type = d->d_ProcDecl.ed_Type; exp->ex_Lhs->ex_Flags |= EXF_RESOLVED; exp->ex_Rhs = sexp; exp->ex_Flags |= EXF_BINARY; exp->ex_Token = TOK_CALL; /* XXX use ltype or procedure's rettype? */ exp->ex_Type = ltype; LexDupRef(&sexp->ex_LexRef, &exp->ex_LexRef); LexDupRef(&sexp->ex_LexRef, &exp->ex_Lhs->ex_LexRef); } exp->ex_Flags |= EXF_RESOLVED; return(exp); } static Declaration * findCast(Type *btype, Type *ltype, Type *rtype) { SemGroup *sg; Declaration *d; /* * Locate the base type. If the base type does not have a SemGroup * there are no casts. (XXX put system operators here) */ sg = BaseType(&btype); if (sg == NULL) return(NULL); /* * Look for the cast in the SemGroup */ for (d = getHead(&sg->sg_DeclList); d; d = getSucc(&sg->sg_DeclList, &d->d_Node) ) { if (d->d_Op == DOP_PROC && (d->d_ScopeFlags & SCOPE_CAST)) { if (MatchCastTypes(d, ltype, rtype)) return(d); } } /* * Failed. If the base type is a compound type, look for the * cast in the SemGroup for each element making up the compound * type. e.g. so (mycustomtype, double) would find the cast * in mycustomtype. */ if (btype->ty_Op == TY_COMPOUND) { for (d = getHead(&sg->sg_DeclList); d; d = getSucc(&sg->sg_DeclList, &d->d_Node) ) { Declaration *d2; if (d->d_Op & DOPF_STORAGE) { d2 = findCast(d->d_StorDecl.ed_Type, ltype, rtype); } else if (d->d_Op == DOP_TYPEDEF) { d2 = findCast(d->d_TypedefDecl.ed_Type, ltype, rtype); } else { d2 = NULL; } if (d2) return(d2); } } return(NULL); } /* * resolveExpOper() - resolve an operator * * This is complex enough that it is broken out into its own procedure. * Normally we just look the operator up but we have to special case * pointer arithmatic because we do will not know until now that we * have to do it. * * itype is a return-type hint only. resolveExpOper() can ignore it * if it wishes. We currently use it to detect cast-to-void, such as * when an expression like "++i" is used in a for() loop or as a * standalone statement. This allows us to optimize the case. */ static Exp * resolveExpOper(SemGroup *isg, SemGroup *sg, Exp *exp, Type *itype) { Declaration *d; int isPointerOp = 0; Exp copy; loadExpCopy(©, exp); dassert_exp(exp, exp->ex_Id != NULL); if (exFlags & EXF_BINARY) { exLhs = resolveExp(isg, sg, exLhs, NULL, 0); exRhs = resolveExp(isg, sg, exRhs, NULL, 0); } else if (exFlags & EXF_UNARY) { exLhs = resolveExp(isg, sg, exLhs, NULL, 0); } else { dassert_exp(exp, 0); } /* * If the lhs is a pointer look the operator up in the Pointer * class first. Operators in the Pointer class are special-cased. * A second pointer argument or a pointer return value must match * the lhs pointer. * * If this fails, or if the ltype is not a pointer, then look * the operator up normally. */ if (exLhs->ex_Type->ty_Op == TY_PTRTO || exLhs->ex_Type->ty_Op == TY_REFTO || exLhs->ex_Type->ty_Op == TY_CPTRTO ) { Type *ltype; Type *rtype; if (exFlags & EXF_BINARY) { rtype = exRhs->ex_Type; ltype = exLhs->ex_Type; } else { dassert(exFlags & EXF_UNARY); rtype = NULL; ltype = exLhs->ex_Type; } d = findOper(&PointerType, exp->ex_Id, ltype, rtype); if (d) isPointerOp = 1; else d = findExpOper(©); } else { d = findExpOper(©); } /* * Fall through to finish up resolving the operator. We just set * ex_Decl for internal operators, and construct a call for * non-internal procedural operators. */ if (d) { Declaration *d2; Type *type; SemGroup *sg2; int count = 0; dassert_exp(exp, d != NULL); dassert_exp(exp, d->d_Op == DOP_PROC); dassert_exp(exp, d->d_ProcDecl.ed_Type->ty_Op == TY_PROC); type = d->d_ProcDecl.ed_Type; exType = type->ty_ProcType.et_RetType; /* * Special case for internal Pointer ops. The return type is * the left-hand type (we may still optimize it to void later). */ if (isPointerOp && (d->d_ScopeFlags & SCOPE_INTERNAL) && SimilarType(&VoidRefType, exType) ) { if (exType->ty_SQFlags & SF_LVALUE) exType = ADD_LVALUE(exLhs->ex_Type); else exType = DEL_LVALUE(exLhs->ex_Type); } type = d->d_ProcDecl.ed_Type->ty_ProcType.et_ArgsType; dassert(type->ty_Op == TY_ARGS); sg2 = type->ty_ArgsType.et_SemGroup; /* * Assert that LVALUE requirements are met. XXX MatchType() * code should disallow the non-lvalue-cast-to-lvalue case * so we don't have to do a check here. */ for ( d2 = getHead(&sg2->sg_DeclList); d2; d2 = getSucc(&sg2->sg_DeclList, &d2->d_Node) ) { if ((d2->d_Op & DOPF_STORAGE) && d2->d_Op != DOP_GLOBAL_STORAGE) { if (count == 0) { if ((d2->d_ScopeFlags & SCOPE_LVALUE) && (exLhs->ex_Type->ty_SQFlags & SF_LVALUE) == 0 ) { fprintf(stderr, "lhs of exp must be " "lvalue\n"); dassert_exp(exp, 0); } } else if (count == 1) { if ((d2->d_ScopeFlags & SCOPE_LVALUE) && (exRhs->ex_Type->ty_SQFlags & SF_LVALUE) == 0 ) { fprintf(stderr, "rhs of exp must be " "lvalue\n"); dassert_exp(exp, 0); } } ++count; } } if (d->d_ScopeFlags & SCOPE_INTERNAL) { /* * Internal operator. Optimize any cast to void * by having the internal function deal with it. * (since we aren't setting exType the optimization * currently doesn't do anything, see ST_Exp) */ exDecl = d; if (itype == &VoidType) { /*exType = itype;*/ exFlags |= EXF_RET_VOID; } } else { /* * Normal procedural operator. Convert the left and * right hand sides to a compound expression and * convert exp to a TOK_CALL. NOTE! ex_Rhs may be * NULL (unary op). * * The compound expression may need to rewrite a * subclass procedure, which it can do if the * procedure's body has not yet been created (or * duplicated from the superclass). ex_Decl must * be set in this case. * * Note that the expression structure may be shared. * The conversion is permanent so that is ok. * * XXX keep the type intact? */ exLhs->ex_Next = exRhs; exRhs = exLhs; exRhs = ExpToCompoundExp(exRhs, TOK_COMPOUND); if (d->d_ProcDecl.ed_ProcBody == NULL) exRhs->ex_Decl = d; exRhs = resolveCompoundExp(isg, sg, exRhs, type); exLhs = AllocExp(NULL); LexDupRef(&exp->ex_LexRef, &exLhs->ex_LexRef); exLhs->ex_Token = TOK_ID; exLhs->ex_Id = d->d_Id; exLhs->ex_Decl = d; exLhs->ex_Type = d->d_ProcDecl.ed_Type; exLhs->ex_Flags |= EXF_RESOLVED; exToken = exp->ex_Token = TOK_CALL; exFlags = EXF_BINARY; } } if (d == NULL) { fprintf(stderr, "Unable to resolve operator: %s\n", exp->ex_Id); dassert_exp(exp, 0); } exFlags |= EXF_RESOLVED; return(dupExpCopy(©, exp)); } static void loadExpCopy(Exp *copy, Exp *exp) { copy->ex_Lhs = exp->ex_Lhs; copy->ex_Rhs = exp->ex_Rhs; copy->ex_Flags = exp->ex_Flags; copy->ex_Type = exp->ex_Type; copy->ex_Decl = exp->ex_Decl; copy->ex_Token = exp->ex_Token; copy->ex_Visibility = exp->ex_Visibility; copy->ex_Id = exp->ex_Id; copy->ex_ArgId = exp->ex_ArgId; copy->ex_AuxExp = exp->ex_AuxExp; } static void saveExpCopy(Exp *copy, Exp *exp) { exp->ex_Lhs = copy->ex_Lhs; exp->ex_Rhs = copy->ex_Rhs; exp->ex_Flags = copy->ex_Flags; exp->ex_Type = copy->ex_Type; exp->ex_Decl = copy->ex_Decl; exp->ex_Token = copy->ex_Token; exp->ex_Visibility = copy->ex_Visibility; exp->ex_Id = copy->ex_Id; exp->ex_ArgId = copy->ex_ArgId; exp->ex_AuxExp = copy->ex_AuxExp; } #define EXF_DUPSHAREFLAGS -1 static Exp * dupExpCopy(Exp *copy, Exp *exp) { Exp *nexp; if ((exp->ex_Flags & EXF_DUPEXP) == 0) { saveExpCopy(copy, exp); return(exp); } /* * We can share the expression or subexpression across duplicated * elements in distinct subclasses if these conditions are met. * * Note: 0x80000000 for ex_Visibility simply means that the visibility * was never modified and can be changed for the first time. * Superclasses are resolved first so we should be ok. */ if (exp->ex_Lhs == copy->ex_Lhs && exp->ex_Rhs == copy->ex_Rhs && ((exp->ex_Flags ^ copy->ex_Flags) & ~EXF_DUPSHAREFLAGS) == 0 && (exp->ex_Type == copy->ex_Type || exp->ex_Type == NULL) && exp->ex_Token == copy->ex_Token && (exp->ex_Decl == copy->ex_Decl || exp->ex_Decl == NULL) && (exp->ex_Id == copy->ex_Id || exp->ex_Id == NULL) && (exp->ex_ArgId == copy->ex_ArgId || exp->ex_ArgId == NULL) && (exp->ex_AuxExp == copy->ex_AuxExp || exp->ex_AuxExp == NULL) && (exp->ex_Visibility == copy->ex_Visibility || (exp->ex_Visibility & 0x80000000)) ) { #if DUPEXP_DEBUG static int count; fprintf(stderr, "DUPEXPB COMPARE %d %p\n", ++count, exp); #endif saveExpCopy(copy, exp); return(exp); } nexp = AllocExp(NULL); LexDupRef(&exp->ex_LexRef, &nexp->ex_LexRef); #if DUPEXP_DEBUG { static int count; fprintf(stderr, "DUPEXPB %d %p lhs (%p,%p) rhs (%p,%p) " "flags (%08x,%08x) types (%p, %p) " "tokens (%04x, %04x), decl (%p, %p) " "vis (%d, %d)\n", ++count, exp, exp->ex_Lhs, copy->ex_Lhs, exp->ex_Rhs, copy->ex_Rhs, exp->ex_Flags & ~EXF_DUPSHAREFLAGS, copy->ex_Flags & ~EXF_DUPSHAREFLAGS, exp->ex_Type, copy->ex_Type, exp->ex_Token, copy->ex_Token, exp->ex_Decl, copy->ex_Decl, exp->ex_Visibility, copy->ex_Visibility ); ExpPrintError(exp, 0); } #endif copy->ex_Flags &= ~EXF_DUPEXP; saveExpCopy(copy, nexp); nexp->u = exp->u; return(nexp); } /* * resolveType() - Resolve a type * * This routine is responsible for resolving the size and alignment * features of a type. Note that we do not special-case LVALUE * qualified types here. * * This routine is also rsponsible for resolving the visibility of * a type's elements. Visibility is inherited from sub-types. Base * classes's visibility should already be set by resolveSuperClass() */ Type * resolveType(Type *type) { SemGroup *sg = NULL; Type *otype = type; int do2 = 0; if (type->ty_Flags & TF_RESOLVED) return(type); dassert_type(type, (type->ty_Flags & TF_RESOLVING) == 0); type->ty_Flags |= TF_RESOLVING; switch(type->ty_Op) { case TY_CLASS: /* * NOTE: TF_HASLVPTR inherited as appropriate after switch. */ sg = type->ty_ClassType.et_SemGroup; do2 = resolveSemGroup1(sg); type->ty_Bytes = sg->sg_Bytes; type->ty_AlignMask = sg->sg_AlignMask; /* visibility already determined by resolveSuperClass? */ /* * Type inherits ISUNSIGNED from class. This is a special * flag helper for the interpreter and code generator. */ if (sg->sg_Flags & SGF_ISUNSIGNED) type->ty_Flags |= TF_ISUNSIGNED; if (sg->sg_Flags & SGF_ISFLOATING) type->ty_Flags |= TF_ISFLOATING; dassert(type->ty_Visibility != 0); break; case TY_CPTRTO: /* * NOTE: Do not set TF_HASLVPTR, C pointers are not tracked. */ type->ty_Bytes = sizeof(void *); type->ty_AlignMask = POINTER_ALIGN; type->ty_CPtrType.et_Type = resolveType(type->ty_CPtrType.et_Type); type->ty_Visibility = type->ty_CPtrType.et_Type->ty_Visibility; break; case TY_PTRTO: /* * Set TF_HASLVPTR, pointers are tracked. */ type->ty_Bytes = sizeof(PointerStor); type->ty_AlignMask = POINTER_ALIGN; type->ty_PtrType.et_Type = resolveType(type->ty_PtrType.et_Type); type->ty_Visibility = type->ty_PtrType.et_Type->ty_Visibility; type->ty_Flags |= TF_HASLVPTR; break; case TY_REFTO: /* * Set TF_HASLVPTR, references are tracked. */ type->ty_Bytes = sizeof(PointerStor); type->ty_AlignMask = POINTER_ALIGN; type->ty_RefType.et_Type = resolveType(type->ty_RefType.et_Type); type->ty_Visibility = type->ty_RefType.et_Type->ty_Visibility; type->ty_Flags |= TF_HASLVPTR; break; case TY_ARYOF: /* * Inherit TF_HASLVPTR (if array type is or contains something * which needs to be tracked). * * XXX allow ArySize resolve to local expression? * XXX use interpreter to resolve constant expression? * XXX SemGroup passed as NULL. * * XXX hack, ex_ArySize can be shared amoungst qualified * array types. Only resolve it once. XXX * * YYY hack what is our ImportSemGroup ??? */ { Exp *exp = type->ty_AryType.et_ArySize; if ((exp->ex_Flags & EXF_RESOLVED) == 0) { exp = resolveConstExp(NULL, type->ty_AryType.et_SemGroup, exp); } type->ty_AryType.et_ArySize = exp; type->ty_AryType.et_Type = resolveType(type->ty_AryType.et_Type); dassert(exp->ex_Token == TOK_INTEGER); type->ty_AryType.et_Count = strtoq(exp->ex_Id, NULL, 0); type->ty_AlignMask = type->ty_AryType.et_Type->ty_AlignMask; type->ty_Bytes = type->ty_AryType.et_Type->ty_Bytes * type->ty_AryType.et_Count; } type->ty_Visibility = type->ty_AryType.et_Type->ty_Visibility; type->ty_Flags |= type->ty_AryType.et_Type->ty_Flags & (TF_HASLVPTR | TF_HASCONSTRUCT | TF_HASDESTRUCT | TF_HASGCONSTRUCT | TF_HASGDESTRUCT); break; case TY_COMPOUND: /* * NOTE: TF_HASLVPTR inherited as appropriate after switch. */ sg = type->ty_CompType.et_SemGroup; do2 = resolveSemGroup1(sg); type->ty_Bytes = sg->sg_Bytes; type->ty_AlignMask = sg->sg_AlignMask; type->ty_Visibility = SCOPE_ALL_VISIBLE; break; case TY_VAR: /* * NOTE: TF_HASLVPTR inherited as appropriate after switch. */ sg = type->ty_VarType.et_SemGroup; do2 = resolveSemGroup1(sg); type->ty_Bytes = sg->sg_Bytes; type->ty_AlignMask = sg->sg_AlignMask; type->ty_Visibility = SCOPE_ALL_VISIBLE; break; case TY_ARGS: /* * NOTE: TF_HASLVPTR inherited as appropriate after switch. */ sg = type->ty_ArgsType.et_SemGroup; do2 = resolveSemGroup1(sg); type->ty_Bytes = sg->sg_Bytes; type->ty_AlignMask = sg->sg_AlignMask; type->ty_Visibility = SCOPE_ALL_VISIBLE; break; case TY_PROC: /* * NOTE: Storage not tracked. */ type->ty_ProcType.et_ArgsType = resolveType(type->ty_ProcType.et_ArgsType); type->ty_ProcType.et_RetType = resolveType(type->ty_ProcType.et_RetType); type->ty_Bytes = 0; type->ty_AlignMask = 0; type->ty_Visibility = SCOPE_ALL_VISIBLE; break; case TY_STORAGE: /* * NOTE: Base storage is not tracked. */ type->ty_Bytes = type->ty_StorType.et_Bytes; /* XXX check pwr of 2 */ if (type->ty_Bytes) type->ty_AlignMask = type->ty_Bytes - 1; type->ty_Visibility = SCOPE_ALL_VISIBLE; break; case TY_UNRESOLVED: /* * NOTE: Tracking set by resolveType(). */ type->ty_Flags &= ~TF_RESOLVING; type = resolveSuperClass(type); if (type == NULL) { errorDottedId(otype->ty_UnresType.et_DottedId, "Unable to resolve class"); dassert(0); } type = resolveType(type); type->ty_Flags |= TF_RESOLVING; /* visibility set by resolveSuperClass() */ break; case TY_DYNAMIC: /* * NOTE: Tracking unknown (must be handled at run-time). */ type->ty_Visibility = SCOPE_ALL_VISIBLE; break; case TY_IMPORT: /* * NOTE: Storage is persistent, so wrapper is not tracked. */ type->ty_Visibility = SCOPE_ALL_VISIBLE; /* XXX */ break; default: dassert(0); } type->ty_Flags &= ~TF_RESOLVING; type->ty_Flags |= TF_RESOLVED; if (do2 && sg) resolveSemGroup2(sg); /* * Resolve tracking flags so the run-time can take appropriate * action. */ if (sg) { if (sg->sg_SRBase) type->ty_Flags |= TF_HASLVPTR; if (sg->sg_Flags & SGF_VARARGS) type->ty_Flags |= TF_HASLVPTR; /* XXX TF_VARARGS */ if (sg->sg_CBase) type->ty_Flags |= TF_HASCONSTRUCT; if (sg->sg_DBase) type->ty_Flags |= TF_HASDESTRUCT; /* * Combine constructor/destructor hint flags for globals * because we have just one linked list for global constructors * and destructors (no need to optimize heavily). */ if (sg->sg_GBase) type->ty_Flags |= TF_HASGCONSTRUCT|TF_HASGDESTRUCT; } /* * Resolve the default expression for the type, if any. * * XXX qualified types just copy the exp. bad bad YYY * * YYY resolveExp() no ISG (import sem group) */ if (type->ty_AssExp) { if ((type->ty_AssExp->ex_Flags & EXF_RESOLVED) == 0) { type->ty_AssExp = resolveExp(NULL, sg, type->ty_AssExp, DEL_LVALUE(type), 1); } } return(type); } /* * resolveSuperClass() - resolve an unresolved dotted id sequence into a class * * Unresolved type identifier sequences must be resolved. We are also * responsible for setting the visibility of the type's elements. */ Type * resolveSuperClass(Type *super) { string_t *dottedId; SemGroup *sg; Declaration *d; int visibility = SCOPE_ALL_VISIBLE; int eno = 0; dassert_type(super, super->ty_Op == TY_UNRESOLVED); dottedId = super->ty_UnresType.et_DottedId; sg = super->ty_UnresType.et_SemGroup; d = FindDeclPath(NULL, super->ty_UnresType.et_ImportSemGroup, sg, super, dottedId, FDC_NULL, &visibility, -1, &eno); if (d == NULL) { return(NULL); } /* * Resolve the unresolved type. Note that this occurs during class * resolution and we can't call resolveType() here without getting into * a loop, so we do not yet know storage requirements (ty_Bytes and * ty_Align). */ switch(d->d_Op) { case DOP_CLASS: super->ty_Op = TY_CLASS; super->ty_ClassType.et_SemGroup = d->d_ClassDecl.ed_SemGroup; super->ty_ClassType.et_Super = d->d_ClassDecl.ed_Super; super->ty_Visibility = visibility; dassert(visibility); /* * XXX should we move the class from the unresolved list to * the new SemGroup's actual list? */ break; case DOP_TYPEDEF: { dassert_type(super, d->d_TypedefDecl.ed_Type != super); TypeToQualType( d->d_TypedefDecl.ed_Type, super, super->ty_SQFlags | d->d_TypedefDecl.ed_Type->ty_SQFlags, super->ty_AssExp ); } super->ty_Visibility = visibility; break; default: errorDottedId(dottedId, "identifier is not a class or typedef"); dassert_type(super, 0); } return(super); } /* * Resolve the declarations in a semantic group. We have to do this if * (a) The declaration(s) would not otherwise be resolved by our statement * scan, or (b) We need definitive size/alignment information now rather * then later. * * SemGroup resolution occurs in two stages in order to deal with * resolver loops. The first stage does just enough to figure out * how large the SemGroup is (e.g. doesn't run through pointers). * The second stage completes the process. * * NOTE! This code does not resolve declarations related to executable * semantic groups, such as sub-blocks within a procedure, but it does * have to resolve procedure definitions found in Class's and such. * * NOTE! This code handles the last stage of subclass refinement, by * checking the validity of the refinement and setting sg_Compat properly. */ int resolveSemGroup1(SemGroup *sg) { Declaration *d; if (sg->sg_Flags & SGF_RESOLVED) return(0); if (sg->sg_Flags & SGF_RESOLVING) { fprintf(stderr, "Unable to resolve embedded recursive reference\n"); dassert_semgrp(sg, (sg->sg_Flags & SGF_RESOLVING) == 0); } sg->sg_Flags |= SGF_RESOLVING; /* * First pass - resolve storage only. Note that this specifically * does not try to handle the multiple semantic layers inside a * procedure. See ResolveStorage() for that. */ for ( d = getHead(&sg->sg_DeclList); d; d = getSucc(&sg->sg_DeclList, &d->d_Node) ) { switch(d->d_Op) { case DOP_CLASS: case DOP_TYPEDEF: case DOP_ALIAS: case DOP_IMPORT: case DOP_PROC: break; case DOP_STACK_STORAGE: /* * can't happen. Stack storage is only used in * executable contexts. */ dassert_decl(d, 0); case DOP_ARGS_STORAGE: case DOP_GROUP_STORAGE: switch(d->d_StorDecl.ed_Type->ty_Op) { case TY_CPTRTO: d->d_Bytes = sizeof(void *); d->d_AlignMask = POINTER_ALIGN; if ((d->d_Scope.s_Flags & SCOPE_ALIGN) && d->d_Scope.s_AlignOverride) { d->d_AlignMask = d->d_Scope.s_AlignOverride - 1; } break; case TY_PTRTO: case TY_REFTO: if (d->d_ScopeFlags & SCOPE_LVALUE) { /* * NOTE: d->d_Bytes is different from * the size of the underlying * type that the LValueStor * points to. */ dassert(d->d_Op == DOP_ARGS_STORAGE); d->d_Bytes = sizeof(LValueStor); d->d_AlignMask = LVALUE_ALIGN; } else { d->d_Bytes = sizeof(PointerStor); d->d_AlignMask = POINTER_ALIGN; } if ((d->d_Scope.s_Flags & SCOPE_ALIGN) && d->d_Scope.s_AlignOverride) { d->d_AlignMask = d->d_Scope.s_AlignOverride - 1; } break; default: resolveDecl(d); break; } if (sg->sg_AlignMask < d->d_AlignMask) sg->sg_AlignMask = d->d_AlignMask; sg->sg_Bytes = (sg->sg_Bytes + d->d_AlignMask) & ~d->d_AlignMask; d->d_Offset = sg->sg_Bytes; sg->sg_Bytes += d->d_Bytes; break; case DOP_GLOBAL_STORAGE: switch(d->d_StorDecl.ed_Type->ty_Op) { case TY_CPTRTO: d->d_Bytes = sizeof(void *); d->d_AlignMask = POINTER_ALIGN; if ((d->d_Scope.s_Flags & SCOPE_ALIGN) && d->d_Scope.s_AlignOverride) { d->d_AlignMask = d->d_Scope.s_AlignOverride - 1; } break; case TY_PTRTO: case TY_REFTO: d->d_Bytes = sizeof(PointerStor); d->d_AlignMask = POINTER_ALIGN; if ((d->d_Scope.s_Flags & SCOPE_ALIGN) && d->d_Scope.s_AlignOverride) { d->d_AlignMask = d->d_Scope.s_AlignOverride - 1; } break; default: resolveDecl(d); break; } if (sg->sg_GlobalAlignMask < d->d_AlignMask) sg->sg_GlobalAlignMask = d->d_AlignMask; sg->sg_GlobalBytes = (sg->sg_GlobalBytes + d->d_AlignMask) & ~d->d_AlignMask; d->d_Offset = sg->sg_GlobalBytes; sg->sg_GlobalBytes += d->d_Bytes; break; default: dassert_semgrp(sg, 0); break; } } /* * Final alignment */ sg->sg_Bytes = (sg->sg_Bytes + sg->sg_AlignMask) & ~sg->sg_AlignMask; sg->sg_GlobalBytes = (sg->sg_GlobalBytes + sg->sg_GlobalAlignMask) & ~sg->sg_GlobalAlignMask; sg->sg_Flags |= SGF_RESOLVED; return(1); } void resolveSemGroup2(SemGroup *sg) { Declaration *d; /* * Second pass, resolve non-storage entities after setting * SGF_RESOLVED, these entities may legally embed this class (if * this is a class). * * Resolve pointers that were only partially resolved in the first * pass. * * Refinements have been resolved but we have to check them for * legality and set sg_Compat. * * Note that this is what allows us to typedef a subclass in its * superclass. The typedef itself is safe. * * Also resolve storage pointer entities that were skipped in the * first pass. Such pointers could point to ourselves. */ for ( d = getHead(&sg->sg_DeclList); d; d = getSucc(&sg->sg_DeclList, &d->d_Node) ) { if (d->d_ScopeFlags & SCOPE_REFINE) { resolveDecl(d->d_Super); resolveDecl(d); RefineDeclaration(sg, d->d_Super, d); } switch(d->d_Op) { case DOP_CLASS: case DOP_TYPEDEF: case DOP_ALIAS: case DOP_IMPORT: case DOP_PROC: resolveDecl(d); break; case DOP_ARGS_STORAGE: case DOP_GROUP_STORAGE: /*case DOP_STACK_STORAGE:*/ case DOP_GLOBAL_STORAGE: switch(d->d_StorDecl.ed_Type->ty_Op) { case TY_CPTRTO: case TY_PTRTO: case TY_REFTO: resolveDecl(d); break; } break; default: dassert_semgrp(sg, 0); break; } } sg->sg_Flags &= ~SGF_RESOLVING; } /* * findExpOper() - Find operator declaration matching expression * * Locate the operator declaration (a DOP_PROCDEF) that matches * the expression or NULL if no match could be found. The expression's * left and right hand sides must already be resolved. * * NOTE! A temporary 'copy' Exp may be passed, not all fields are valid. */ static Declaration * findExpOper(Exp *exp) { Type *ltype; Type *rtype; Declaration *d; if (exp->ex_Flags & EXF_BINARY) { rtype = exp->ex_Rhs->ex_Type; ltype = exp->ex_Lhs->ex_Type; } else { dassert(exp->ex_Flags & EXF_UNARY); rtype = NULL; ltype = exp->ex_Lhs->ex_Type; } /* * XXX look in our local semantic hierarchy for a compatible operator ? */ /* * Attempt to find a matching operator from the left hand side */ d = findOper(ltype, exp->ex_Id, ltype, rtype); if (d || (exp->ex_Flags & EXF_BINARY) == 0) return(d); d = findOper(rtype, exp->ex_Id, ltype, rtype); return(d); } static Declaration * findOper(Type *btype, string_t id, Type *ltype, Type *rtype) { SemGroup *sg; Declaration *d; int args = (rtype != NULL) ? 2 : 1; /* * Locate the base type. If the base type does not have a SemGroup * there are no operators. (XXX put system operators here) */ sg = BaseType(&btype); if (sg == NULL) return(NULL); /* * Look for the operator in the SemGroup */ for (d = FindOperId(sg, id, args); d; d = d->d_ONext) { resolveDecl(d); if (d->d_MyGroup == sg && d->d_Op == DOP_PROC && d->d_ProcDecl.ed_OperId == id && MatchOperatorTypes(d, ltype, rtype) ) { return(d); } } /* * Failed. If the base type is a compound type, look for the * operator in the SemGroup for each element making up the compound * type. e.g. so (mycustomtype, double) would find the operator * in mycustomtype. */ if (btype->ty_Op == TY_COMPOUND) { for (d = getHead(&sg->sg_DeclList); d; d = getSucc(&sg->sg_DeclList, &d->d_Node) ) { Declaration *d2; if (d->d_Op & DOPF_STORAGE) { d2 = findOper(d->d_StorDecl.ed_Type, id, ltype, rtype); } else if (d->d_Op == DOP_TYPEDEF) { d2 = findOper(d->d_TypedefDecl.ed_Type, id, ltype, rtype); } else { d2 = NULL; } if (d2) return(d2); } } return(NULL); } static void errorDottedId(string_t *ary, const char *ctl, ...) { va_list va; int i; va_start(va, ctl); vfprintf(stderr, ctl, va); va_end(va); fprintf(stderr, ": %s", ary[0]); for (i = 1; ary[i]; ++i) fprintf(stderr, ".%s", ary[i]); fprintf(stderr, "\n"); } /* * ResolveStorage() - Final storage resolution pass * * This pass carefully scans the SemGroup hierarchy and assigns * offsets to declarations. * * PROCEDURES - all the various 'executable' semantic layers in * a procedure are collapsed together for efficiency, so we only * have to manage one context. This means that the d_Offset * assigned to declarations in sub-blocks may exceed the sg_ size * of the sub-block's SemGroup. We do not attempt to resolve * procedure body templates (d_ProcDecl.ed_OrigBody). * * CLASSES - are given offsets in their SemGroup's relative to 0, if * not already resolved. * * IMPORTS - are given offsets in their SemGroup's relative to 0 * * COMPOUND TYPES - (such as procedure arguments) are given offsets * in their SemGroup's relative to 0. * * TEMPORARY STORAGE - expressions may require temporary storage * for intermediate results. That space is reserved here. * * We specifically do not resolve unrelated storage. */ void ResolveStorage(Stmt *st) { runesize_t noffset; runesize_t offset; runesize_t goffset; runesize_t ngoffset; SemGroup *sg = st->st_MyGroup; /* * If this is an executable semantic layer or an import layer then * assign storage to declarations up-front. Of the various * DOP_*_STORAGE ops, we should only see DOP_STACK_STORAGE and * DOP_GLOBAL_STORAGE. * * Note: if this is the root ST_Import STF_SEMANTIC is *NOT* set and * sg will be NULL. */ if ((st->st_Flags & STF_SEMANTIC) && st->st_Op != ST_Class) { Declaration *d; dassert((sg->sg_Flags & (SGF_RESOLVED|SGF_RESOLVING)) == 0); sg->sg_Flags |= SGF_RESOLVING; /* * Procedures consolidate their storage. To make d_Offset's * come out properly. */ if (st->st_Flags & STF_SEMTOP) { dassert(sg->sg_Bytes == 0); offset = 0; } else { offset = sg->sg_Parent->sg_Bytes; } sg->sg_BlkOffset = offset; for (d = getHead(&sg->sg_DeclList); d; d = getSucc(&sg->sg_DeclList, &d->d_Node) ) { switch(d->d_Op) { case DOP_STACK_STORAGE: case DOP_ARGS_STORAGE: case DOP_GROUP_STORAGE: offset = (offset + d->d_AlignMask) & ~d->d_AlignMask; dassert_decl(d, d->d_Offset == (runesize_t)-1); d->d_Offset = offset; offset += d->d_Bytes; break; case DOP_GLOBAL_STORAGE: sg->sg_GlobalBytes = (sg->sg_GlobalBytes + d->d_AlignMask) & ~d->d_AlignMask; dassert_decl(d, d->d_Offset == (runesize_t)-1); d->d_Offset = sg->sg_GlobalBytes; sg->sg_GlobalBytes += d->d_Bytes; break; default: break; } } /* * Final alignment so, for example, arrays work properly. We * incorporate the storage into our parent at the end. */ sg->sg_Bytes = (offset + sg->sg_AlignMask) & ~sg->sg_AlignMask; sg->sg_GlobalBytes = (sg->sg_GlobalBytes + sg->sg_GlobalAlignMask) & ~sg->sg_GlobalAlignMask; sg->sg_BlkBytes = sg->sg_Bytes - sg->sg_BlkOffset; sg->sg_Flags |= SGF_RESOLVED; sg->sg_Flags &= ~SGF_RESOLVING; } /* * Figure out how much temporary space we need to be able to execute * statements and expressions. Temporary space, like the main * procedural space, must be inherited from and consolidated into * the top-level SemGroup */ if (sg) { offset = sg->sg_TmpBytes; noffset = offset; goffset = sg->sg_GlobalTmpBytes; ngoffset = goffset; } else { /* * Root ST_Import. avoid compiler warnings */ offset = 0; noffset = 0; goffset = 0; ngoffset = 0; } switch(st->st_Op) { case ST_Import: if (st->st_ImportStmt.es_DLL) { void (*func)(void) = dlsym(st->st_ImportStmt.es_DLL, "resolveStorage"); if (func) func(); } break; case ST_Module: case ST_Class: break; case ST_Typedef: { resolveStorageDeclExp(st->st_TypedefStmt.es_Decl, &noffset, &ngoffset); } break; case ST_Decl: /* * Temporary space for declarations are handled here. */ { Declaration *d; int i; d = st->st_DeclStmt.es_Decl; for (i = 0; i < st->st_DeclStmt.es_DeclCount; ++i) { runesize_t xoffset = offset; runesize_t xgoffset = goffset; resolveStorageDeclExp(d, &xoffset, &xgoffset); if (noffset < xoffset) noffset = xoffset; if (ngoffset < xgoffset) ngoffset = xgoffset; d = getSucc(&d->d_MyGroup->sg_DeclList, &d->d_Node); } } break; case ST_Block: break; case ST_Proc: break; case ST_Nop: break; case ST_Loop: { runesize_t xoffset; if (st->st_LoopStmt.es_BCond) { xoffset = offset; resolveStorageExp(st->st_LoopStmt.es_BCond, &xoffset); if (noffset < xoffset) noffset = xoffset; } if (st->st_LoopStmt.es_ACond) { xoffset = offset; resolveStorageExp(st->st_LoopStmt.es_ACond, &xoffset); if (noffset < xoffset) noffset = xoffset; } if (st->st_LoopStmt.es_AExp) { xoffset = offset; resolveStorageExp(st->st_LoopStmt.es_AExp, &xoffset); if (noffset < xoffset) noffset = xoffset; } } break; case ST_BreakCont: break; case ST_Bad: break; case ST_IfElse: resolveStorageExp(st->st_IfStmt.es_Exp, &noffset); break; case ST_Return: if (st->st_RetStmt.es_Exp) resolveStorageExp(st->st_RetStmt.es_Exp, &noffset); break; case ST_Result: if (st->st_ResStmt.es_Exp) resolveStorageExp(st->st_ResStmt.es_Exp, &noffset); break; case ST_Switch: /* * The switch expression's temporary data must be saved while * we are executing the sub-statements (the cases). */ resolveStorageExp(st->st_SwStmt.es_Exp, &noffset); offset = noffset; goffset = ngoffset; break; case ST_Case: if (st->st_CaseStmt.es_Exp) resolveStorageExp(st->st_CaseStmt.es_Exp, &noffset); break; case ST_Exp: resolveStorageExp(st->st_ExpStmt.es_Exp, &noffset); break; case ST_ThreadSched: break; default: dassert_stmt(st, 0); } /* * Calculate storage requirements for substatements. offset * acts as our base. We union the storage for the substatements * together. Note that often scan->sg_MyGroup == sg. */ { Stmt *scan; for ( scan = getHead(&st->st_List); scan; scan = getSucc(&st->st_List, &scan->st_Node) ) { if (scan->st_Op == ST_Class) { ResolveStorage(scan); } else if (scan->st_Op == ST_Proc && scan->st_ProcStmt.es_Decl->d_ProcDecl.ed_OrigBody == scan ) { /* Do not resolve template procedures! */ } else if (scan->st_Flags & STF_SEMTOP) { assert(scan->st_MyGroup != sg); ResolveStorage(scan); } else { /* * This is a bit of a mess. The baseline * sg_TmpBytes needs to be set so calculated * temporary offsets are relative to it, and * then restored. Otherwise we might blow * away the SGF_TMPRESOLVED SemGroup * * XXX */ runesize_t save_offset; runesize_t save_goffset; save_offset = scan->st_MyGroup->sg_TmpBytes; save_goffset = scan->st_MyGroup->sg_GlobalTmpBytes; scan->st_MyGroup->sg_TmpBytes = offset; scan->st_MyGroup->sg_GlobalTmpBytes = goffset; ResolveStorage(scan); if (scan->st_MyGroup->sg_TmpBytes < save_offset) scan->st_MyGroup->sg_TmpBytes = save_offset; if (scan->st_MyGroup->sg_GlobalTmpBytes < save_goffset) { scan->st_MyGroup->sg_GlobalTmpBytes = save_goffset; } if (noffset < scan->st_MyGroup->sg_TmpBytes) noffset = scan->st_MyGroup->sg_TmpBytes; if (ngoffset < scan->st_MyGroup->sg_GlobalTmpBytes) ngoffset = scan->st_MyGroup->sg_GlobalTmpBytes; } } } /* * If this is a new semantic level call resolveStorageSemGroup() to * do the final cleanup of SemGroup issues. This will redundantly * calculate temporary space requirements. Also, due to type/class * references the temporary space for a class may have already been * resolved. Since a class can only contain declarations it had * better match what we calculate here. * * Note that for non-Class executable SemGroup's TmpBytes is * incorporated in a downward fashion while sg_Bytes is incorporated * in an upward fashion. It can become quite confusing. Don't ask * me why I did it that way. */ if (st->st_Flags & STF_SEMANTIC) { if ((sg->sg_Flags & SGF_TMPRESOLVED) == 0) { resolveStorageSemGroup(sg, noffset, ngoffset); } else { dassert(sg->sg_TmpBytes == noffset && sg->sg_GlobalTmpBytes == ngoffset); } } else if (sg) { sg->sg_TmpBytes = noffset; sg->sg_GlobalTmpBytes = ngoffset; } /* else this is the Root st_Import */ if ((st->st_Flags & (STF_SEMANTIC|STF_SEMTOP)) == STF_SEMANTIC) { sg->sg_Parent->sg_Bytes = sg->sg_Bytes; if (sg->sg_Parent->sg_AlignMask < sg->sg_AlignMask) sg->sg_Parent->sg_AlignMask = sg->sg_AlignMask; } } /* * resolveStorageDeclExp() - resolve the storage reservation required to * process an expression. * * This is an expression tree traversal storage resolution procedure. * We have to traverse through declarations to get to default assignments * and such. * * If a declaration has no assign default the underlying type may itself * have an assigned default which must be dealt with. */ static void resolveStorageDeclExp(Declaration *d, runesize_t *offset, runesize_t *goffset) { switch(d->d_Op) { case DOP_CLASS: /* recursion already dealt with */ break; case DOP_ARGS_STORAGE: case DOP_STACK_STORAGE: case DOP_GROUP_STORAGE: { Type *type = d->d_StorDecl.ed_Type; resolveStorageType(type); if (d->d_StorDecl.ed_AssExp) { resolveStorageExp(d->d_StorDecl.ed_AssExp, offset); } } break; case DOP_GLOBAL_STORAGE: { Type *type = d->d_StorDecl.ed_Type; resolveStorageType(type); if (d->d_StorDecl.ed_AssExp) { resolveStorageExp(d->d_StorDecl.ed_AssExp, goffset); } } break; case DOP_ALIAS: /* * Never try to resolve storage considerations for an * alias's assignment in the declaration itself. The * run-time context depends on who and how many other * parts of the program reference the alias and the expression * tree will be duplicated for each. */ #if 0 if (d->d_ScopeFlags & SCOPE_GLOBAL) resolveStorageExp(d->d_AliasDecl.ed_AssExp, NULL); else resolveStorageExp(d->d_AliasDecl.ed_AssExp, NULL); #endif break; case DOP_TYPEDEF: /* XXX what about ty_AssExp ? should be in global space */ break; case DOP_IMPORT: /* recursion already dealt with */ break; case DOP_PROC: break; default: dassert_decl(d, 0); } } /* * resolveStorageExp() - resolve the temporary storage required to * support the expression, if any. * * We do not need to assign storage for expressions which return * lvalues, because they will simply return a pointer into * non-temporary storage. */ static runesize_t resolveStorageExpOnly(Exp *exp, runesize_t *offset) { Type *type; /* * Stop if the expression resolves to a type rather then a value, * e.g. when you do something like switch (typeof(int)) { ... } */ if (exp->ex_Flags & EXF_RET_TYPE) return(*offset); /* * Assign temporary offset. This offset does not overlap temporary * space reserved for sub-expressions. * * We must have an assigned type. Expression sequences like: * 'module.blah' are collapsed into 'blah' long before we get * here, or they should be. We should not encounter any * TOK_TCMV_ID expression tokens. Structural id's (the right hand * side of X.Y) are resolved by their parent expression node and * no typing or temporary space is required. * * Expressions that return lvalues do not need temporary space. */ type = exp->ex_Type; if (type == NULL) { switch(exp->ex_Token) { case TOK_STRUCT_ID: case TOK_SEMGRP_ID: break; default: printf("EXP %p %04x %p\n", exp, exp->ex_Token, exp->ex_Decl); dassert_exp(exp, 0); break; } } else if (type->ty_SQFlags & SF_LVALUE) { /* * Expressive elements which return lvalues do not get * temporary space. Note that this also prevents lvalues * such as large arrays (int ary[999999999]) from reserving * unnecessary stack space. * * NOTE: SF_LVALUE is unrelated to SCOPE_LVALUE. SCOPE_LVALUE * applies to SemGroup storage (LValueStor). SF_LVALUE * merely flags the type for an expression as expecting * or not expecting an lvalue. */ #if 0 /* * XXX removeme, LValueStor only applies to semgroups */ runesize_t lvmask = sizeof(LValueStor) - 1; *offset = (*offset + lvmask) & ~lvmask; exp->ex_TmpOffset = *offset; *offset = *offset + (lvmask + 1); #endif exp->ex_TmpOffset = -1; } else { /* * Reserve temporary space for potential intermediate * results. */ *offset = (*offset + type->ty_AlignMask) & ~type->ty_AlignMask; exp->ex_TmpOffset = *offset; *offset = *offset + type->ty_Bytes; } return (*offset); } static void resolveStorageExpSub(Exp *exp, runesize_t *offset) { /* * Early term */ if (exp->ex_Flags & EXF_RET_TYPE) return; /* * Calculate the overlapping temporary space for sub-trees. */ if (exp->ex_Flags & EXF_BINARY) { /* * Ensure lhs's temporary storage on-return does not intefere * with rhs's or vise-versa. To do this we need to calculate * the initial storage for both sides first. */ runesize_t xoffset; runesize_t roffset; roffset = *offset; xoffset = roffset; resolveStorageExp(exp->ex_Lhs, &xoffset); if (*offset < xoffset) *offset = xoffset; xoffset = roffset; resolveStorageExp(exp->ex_Rhs, &xoffset); if (*offset < xoffset) *offset = xoffset; } else if (exp->ex_Flags & EXF_UNARY) { resolveStorageExp(exp->ex_Lhs, offset); dassert_exp(exp, exp->ex_Lhs->ex_Next == NULL); } else if (exp->ex_Flags & EXF_COMPOUND) { /* * each element will be copied into the compound storage * in turn, so we can union the temporary storage required * for each element. */ Exp *scan; runesize_t noffset = *offset; for (scan = exp->ex_Lhs; scan; scan = scan->ex_Next) { runesize_t xoffset = *offset; dassert_exp(scan, scan->ex_Type != NULL); resolveStorageExp(scan, &xoffset); if (noffset < xoffset) noffset = xoffset; } *offset = noffset; } #if 0 /* XXX what about alias assign-expressions */ else if (exp->ex_Flags & EXF_ALIAS) { #if 0 dassert_decl(exp->ex_Decl, (exp->ex_Decl->d_ScopeFlags & SCOPE_GLOBAL) == 0); #endif resolveStorageExp(exp->ex_Decl->d_AliasDecl.ed_AssExp, offset); } #endif } static void resolveStorageExp(Exp *exp, runesize_t *offset) { runesize_t noffset; runesize_t xoffset; xoffset = *offset; noffset = resolveStorageExpOnly(exp, &xoffset); xoffset = noffset; resolveStorageExpSub(exp, &xoffset); if (*offset < xoffset) *offset = xoffset; } /* * resolveStorageType() - temporary space required to initialize type defaults * * Figure out the temporary space required to initialize a type's * defaults. Note that the space will be figured independantly * for any SemGroup's. */ static void resolveStorageType(Type *type) { switch(type->ty_Op) { case TY_CLASS: resolveStorageSemGroup(type->ty_ClassType.et_SemGroup, 0, 0); break; case TY_ARYOF: resolveStorageType(type->ty_AryType.et_Type); break; case TY_COMPOUND: resolveStorageSemGroup(type->ty_CompType.et_SemGroup, 0, 0); break; case TY_PROC: resolveStorageType(type->ty_ProcType.et_ArgsType); resolveStorageType(type->ty_ProcType.et_RetType); break; case TY_IMPORT: resolveStorageSemGroup(type->ty_ImportType.et_SemGroup, 0, 0); break; case TY_ARGS: resolveStorageSemGroup(type->ty_ArgsType.et_SemGroup, 0, 0); break; case TY_VAR: resolveStorageSemGroup(type->ty_VarType.et_SemGroup, 0, 0); break; case TY_CPTRTO: resolveStorageType(type->ty_CPtrType.et_Type); break; case TY_PTRTO: resolveStorageType(type->ty_PtrType.et_Type); break; case TY_REFTO: resolveStorageType(type->ty_RefType.et_Type); break; case TY_STORAGE: case TY_DYNAMIC: /* * nothing to be done here. */ break; case TY_UNRESOLVED: /* should be no unresolved types at this stage */ default: dassert_type(type, 0); } if (type->ty_AssExp) { if ((type->ty_Flags & TF_ASSEXPSTOR) == 0) { type->ty_Flags |= TF_ASSEXPSTOR; resolveStorageExp(type->ty_AssExp, &type->ty_TmpBytes); } } } /* * This is used to resolve temporary storage requirements for * SemGroup's related to classes and compound types. Temporary storage * requirements are calculated on a SemGroup-by-SemGroup basis and not * aggregated into any parent. * * We also reverse the constructor and destructor lists (sg_CBase and * sg_DBase), and the pointer/lvalue list (SRBase). These lists were * originally constructed by prepending and are thus in the wrong order. */ static void resolveStorageSemGroup(SemGroup *sg, runesize_t noffset, runesize_t ngoffset) { Declaration *d; Declaration *d2; if (sg->sg_Flags & SGF_TMPRESOLVED) return; sg->sg_Flags |= SGF_TMPRESOLVED; for ( d = getHead(&sg->sg_DeclList); d; d = getSucc(&sg->sg_DeclList, &d->d_Node) ) { runesize_t offset = 0; runesize_t goffset = 0; resolveStorageDeclExp(d, &offset, &goffset); if (noffset < offset) noffset = offset; if (ngoffset < goffset) ngoffset = goffset; } if ((d2 = sg->sg_CBase) != NULL) { sg->sg_CBase = NULL; while ((d = d2) != NULL) { d2 = d->d_CNext; d->d_CNext = sg->sg_CBase; sg->sg_CBase = d; } } if ((d2 = sg->sg_DBase) != NULL) { sg->sg_DBase = NULL; while ((d = d2) != NULL) { d2 = d->d_DNext; d->d_DNext = sg->sg_DBase; sg->sg_DBase = d; } } if ((d2 = sg->sg_GBase) != NULL) { sg->sg_GBase = NULL; while ((d = d2) != NULL) { d2 = d->d_GNext; d->d_GNext = sg->sg_GBase; sg->sg_GBase = d; } } if ((d2 = sg->sg_SRBase) != NULL) { sg->sg_SRBase = NULL; while ((d = d2) != NULL) { d2 = d->d_SRNext; d->d_SRNext = sg->sg_SRBase; sg->sg_SRBase = d; } } sg->sg_TmpBytes = noffset; sg->sg_GlobalTmpBytes = ngoffset; }