/* * DECL.C * * (c)Copyright 1993-2014, Matthew Dillon, All Rights Reserved. See the * COPYRIGHT file at the base of the distribution. */ #include "defs.h" #include #define DHSIZE 4096 #define DHMASK (DHSIZE - 1) static Declaration * DeclHashAry[DHSIZE]; static Declaration * OperHashAry[DHSIZE]; void InitDecls(void) { } /* * Allocate a declaration and add it to the tail of the sg's declaration * list. */ Declaration * AllocDeclaration(SemGroup *sg, int dop, Scope *scope) { Parse *p = sg->sg_Parse; Declaration *d = zalloc(sizeof(Declaration)); /* * Default to library scope. If this is a refinement we default to no * scope flags which causes the resolver to inherit the scope from the * superclass. * * ResolveClasses() in librune/resolve.c will inherit the scope from the * superclass if it is not set here, so do not set default scope for a * 'refine' declaration. */ if ((scope->s_Flags & (SCOPE_REFINE | SCOPE_ALL_VISIBLE)) == 0) scope->s_Flags |= SCOPE_LIBRARY; d->d_Parse = p; d->d_Op = dop; d->d_MyGroup = sg; d->d_Index = sg->sg_DeclCount++; d->d_Scope = *scope; d->d_Level = sg; d->d_Offset = -1; d->d_DynIndex = -1; d->d_BackendId = -1; RUNE_INSERT_TAIL(&sg->sg_DeclList, d, d_Node); if (p->p_Format == PFMT_SOURCE) LexInitRef(&d->d_LexRef, &p->p_Token); return (d); } void FreeDeclaration(Parse *p __unused, Declaration *d) { /* XXX free the Type */ /* XXX free d_AssExp */ /* XXX free d_ProcBody */ /* XXX free d_OrigBody */ if (d->d_Id) { UnHashDecl(d); d->d_Id = 0; } if (d->d_Op == DOP_PROC && d->d_ProcDecl.ed_OperId != 0) { UnHashOper(d); d->d_Op = 0; } if (d->d_MyGroup) { RUNE_REMOVE(&d->d_MyGroup->sg_DeclList, d, d_Node); d->d_MyGroup = NULL; } zfree(d, sizeof(Declaration)); } Declaration * AllocImportDecl(Stmt *st, Scope *scope) { Declaration *d; dassert(st->st_Op == ST_Import); d = AllocDeclaration(st->st_MyGroup, DOP_IMPORT, scope); LexDupRef(&st->st_LexRef, &d->d_LexRef); /* * st_MyGroup may be empty if the import is shared. Use the SemGroup * initialized by the parser. (If the import is not shared es_SemGroup * will be the same as st_MyGroup). * * Note that ParseModule() will handle the d->d_ImportDecl.ed_SemGroup * and st->st_ImportStmt.es_SemGroup glue. */ HashDecl(d, st->st_ImportStmt.es_AsId); return (d); } Declaration * AllocClassDecl(Stmt *st, runeid_t id, Type *super, Scope *scope) { Declaration *d; dassert(st->st_Op == ST_Class); d = AllocDeclaration(st->st_MyGroup->sg_Parent, DOP_CLASS, scope); LexDupRef(&st->st_LexRef, &d->d_LexRef); d->d_ClassDecl.ed_Super = super; d->d_ClassDecl.ed_SemGroup = st->st_MyGroup; HashDecl(d, id); return (d); } SemGroup * AllocSemGroup(sg_type_t type, Parse *p, SemGroup *par, Stmt *st) { SemGroup *sg = zalloc(sizeof(SemGroup)); if (par) { sg->sg_Parent = par; sg->sg_NestLevel = par->sg_NestLevel; sg->sg_NestSize = par->sg_NestSize; RUNE_INSERT_TAIL(&par->sg_SemList, sg, sg_Node); } sg->sg_Op = type; sg->sg_Parse = p; sg->sg_Stmt = st; sg->sg_Level = -1; RUNE_INIT(&sg->sg_SemList); RUNE_INIT(&sg->sg_DeclList); RUNE_INIT(&sg->sg_ClassList); return (sg); } void FreeSemGroup(SemGroup *sg) { if (sg->sg_Parent) { RUNE_REMOVE(&sg->sg_Parent->sg_SemList, sg, sg_Node); sg->sg_Parent = NULL; } dassert(RUNE_EMPTY(&sg->sg_DeclList)); /* XXX zfree? */ } #if 0 /* * Allocate a declaration block from a compound expression */ SemGroup * AllocSemGroupFromCompoundExp(SemGroup *par, Exp *exp) { SemGroup *sg; sg = AllocSemGroup(SG_COMPOUND, par->sg_Parse, par, NULL); while (exp) { Declaration *d; Scope tscope = INIT_SCOPE(SCOPE_PUBLIC); d = AllocDeclaration(sg, DOP_GROUP_STORAGE, &tscope); LexDupRef(&exp->ex_LexRef, &d->d_LexRef); d->d_StorDecl.ed_Type = exp->ex_Type; } return (sg); } #endif /* * DupSemGroup() - duplicate a semantic group * * Duplicate ssg. sg is the new parent. * * This routine guarentees that the d_Index's and ordering will match up * (because it forces it). */ SemGroup * DupSemGroup(SemGroup *sg, Stmt *st, SemGroup *ssg, int dupDecls) { Declaration *d; SemGroup *nsg = AllocSemGroup(ssg->sg_Op, ssg->sg_Parse, sg, st); dassert(RUNE_EMPTY(&nsg->sg_DeclList)); nsg->sg_Flags = ssg->sg_Flags & SGF_INHERIT_BASE; if (dupDecls) { int hiindex = -1; RUNE_FOREACH(d, &ssg->sg_DeclList, d_Node) { Declaration *nd; nd = DupDeclaration(nsg, d); nd->d_Index = d->d_Index; if (hiindex < d->d_Index) /* sanity */ hiindex = d->d_Index; /* sanity */ } dassert(hiindex == nsg->sg_DeclCount - 1); nsg->sg_VarCount = ssg->sg_VarCount; } nsg->sg_NestLevel = ssg->sg_NestLevel; nsg->sg_NestSize = ssg->sg_NestSize; nsg->sg_AltContext = ssg->sg_AltContext; return (nsg); } static int hashid(SemGroup *sg, runeid_t id) { id += (uintptr_t)sg; id ^= id >> 32; id ^= id >> 16; return ((int)(id & DHMASK)); } /* * findDecl() - find declaration relative to class level N * * FindDecl() will find the highest identifier less then or equal to the * specified level. When multple identifiers are found at the same * (highest) level, FindDecl() will return the last, which is first entered. * This allows us to bring certain types of declarations, such as superclass * methods, into a subclass and only access them under special circumstances. * See ResolveClasses(). */ static Declaration * findDecl(SemGroup *sg, runeid_t id, int visibility, int level, int *eno) { Declaration *d; /* * private visibility is usually lost first, then library, then public. * But some searches want to initially restrict to within the current * library or private space in which case we must still match against * public declarations, etc. This typically happens at parse time. */ if (visibility & SCOPE_LIBRARY) visibility |= SCOPE_PUBLIC; if (visibility & SCOPE_PRIVATE) visibility |= SCOPE_LIBRARY | SCOPE_PUBLIC; for (d = DeclHashAry[hashid(sg, id)]; d; d = d->d_HNext) { Declaration *bd; int scopeFlags; if (d->d_MyGroup != sg || d->d_Id != id) continue; if (level >= 0 && d->d_Level->sg_Level > level) continue; /* * Find the closest matching level. */ if (level >= 0) { bd = d; for (d = d->d_HNext; d; d = d->d_HNext) { if (d->d_MyGroup != sg || d->d_Id != id) continue; if (d->d_Level->sg_Level <= level && d->d_Level->sg_Level >= bd->d_Level->sg_Level) { bd = d; /* first, highest id < level */ } } d = bd; } /* * Check visibility. We may have to recurse through d_Super to be * able to check visibility from the point of view of the superclass. */ if (level >= 0) { bd = d; while (bd->d_MyGroup->sg_Level != level) { dassert(bd->d_Super != NULL); bd = bd->d_Super; } scopeFlags = bd->d_ScopeFlags; } else { bd = d; scopeFlags = d->d_ScopeFlags; } #if 0 /* * XXX not tied in yet? so not relevant? * * If scope not yet resolved and not specified in the subclass, * refinements inherit the scope from the superclass declaration. */ while ((scopeFlags & SCOPE_ALL_VISIBLE) == 0) { bd = bd->d_Super; if (bd == NULL) break; scopeFlags = bd->d_ScopeFlags; } #endif if ((scopeFlags & visibility) == 0) { *eno = TOK_ERR_SCOPE_NOT_VISIBLE; break; } /* * Yahh */ *eno = 0; return (d); } *eno = TOK_ERR_ID_NOT_FOUND; return (NULL); } Declaration * FindOperId(SemGroup *sg, runeid_t id, int args) { Declaration *d; for (d = OperHashAry[hashid(sg, id)]; d; d = d->d_ONext) { if (d->d_MyGroup == sg && d->d_Op == DOP_PROC && d->d_ProcDecl.ed_OperId == id) { if (d->d_ProcDecl.ed_Type->ty_ProcType.et_ArgCount == (urunesize_t)args) { break; } } } return (d); } /* * Associate an identifier with the declaration. We eat the caller's * reference to the identifier. * * Return 1 if a duplicate declaration has been detected. Such detection is * only useful during the parsing phase since we overload identifiers in the * resolution phase. * * NOTE! we depend on inserting at the base of the hash chain. See * findDecl() for more information. */ int HashDecl(Declaration *d, runeid_t id) { Declaration **pd; SemGroup *sg = d->d_MyGroup; dassert(d != NULL); dassert_decl(d, id != 0 && d->d_Id == 0 && d->d_MyGroup != NULL); d->d_Id = id; pd = &DeclHashAry[hashid(d->d_MyGroup, id)]; d->d_HNext = *pd; *pd = d; if (id != RUNEID_SELF) { while ((d = d->d_HNext) != NULL) { if (d->d_Id == id && d->d_MyGroup == sg) return (1); } } return (0); } /* * Remove the identifier associated with the declaration. */ runeid_t UnHashDecl(Declaration *d) { runeid_t id; if ((id = d->d_Id) != 0) { Declaration **pd; pd = &DeclHashAry[hashid(d->d_MyGroup, id)]; while (*pd != d) { dassert(*pd != NULL); pd = &(*pd)->d_HNext; } *pd = d->d_HNext; d->d_HNext = NULL; d->d_Id = 0; } return (id); } void HashOper(Declaration *d, runeid_t id) { Declaration **pd; dassert(d != NULL); dassert_decl(d, id && d->d_MyGroup != NULL && d->d_Op == DOP_PROC && d->d_ProcDecl.ed_OperId == 0); d->d_ProcDecl.ed_OperId = id; pd = &OperHashAry[hashid(d->d_MyGroup, id)]; d->d_ONext = *pd; *pd = d; } runeid_t UnHashOper(Declaration *d) { runeid_t id; dassert_decl(d, d->d_Op == DOP_PROC); if ((id = d->d_ProcDecl.ed_OperId) != 0) { Declaration **pd; pd = &OperHashAry[hashid(d->d_MyGroup, id)]; while (*pd != d) { dassert(*pd != NULL); pd = &(*pd)->d_ONext; } *pd = d->d_ONext; d->d_ONext = NULL; d->d_ProcDecl.ed_OperId = 0; } return (id); } /* * Rename a declaration, fixup the hash tables appropriately. Used when * making contextual declarations such 'Fd.setfd ...' outside the Fd class. * * NOTE: The original SemGroup will have holes in its index now. */ void RenameDecl(Declaration *d, SemGroup *nsg) { runeid_t id; runeid_t operId; id = UnHashDecl(d); if (d->d_Op == DOP_PROC) operId = UnHashOper(d); else operId = 0; RUNE_REMOVE(&d->d_MyGroup->sg_DeclList, d, d_Node); RUNE_INSERT_TAIL(&nsg->sg_DeclList, d, d_Node); d->d_Index = nsg->sg_DeclCount++; d->d_MyGroup = nsg; if (id) HashDecl(d, id); if (operId) HashOper(d, operId); } /* * MatchOperatorTypes() - Match left and right hand types to the operator * procedure. * * The op decl is a procedure definition. Access the procedure arguments and * match them against the Lhs and Rhs types of the exp, returning 0 on * failure, 1 on match. * * If the op decl is an internal operator, match pointers to void against any * pointer. */ int MatchOperatorTypes(Declaration *d, Type *ltype, Type *rtype) { Type *type; runeid_t id; Type *dltype; Type *drtype; SemGroup *sg; int scope; int internal1; int internal2; /* * Pull the procedure argument type, which must be a compound type. */ dassert_decl(d, d->d_Op == DOP_PROC && d->d_ProcDecl.ed_Type->ty_Op == TY_PROC); type = d->d_ProcDecl.ed_Type->ty_ProcType.et_ArgsType; id = d->d_ProcDecl.ed_OperId; id = id; dassert_decl(d, type->ty_Op == TY_ARGS); sg = type->ty_ArgsType.et_SemGroup; scope = d->d_ScopeFlags; //char buf[RUNE_IDTOSTR_LEN]; //printf("START %s\n", runeid_text(id, buf)); /* * Obtain first argument */ RUNE_FOREACH(d, &sg->sg_DeclList, d_Node) { if (d->d_Op == DOP_ARGS_STORAGE) break; } /* * Operators need at least one argument */ if (d == NULL) return (0); dltype = d->d_StorDecl.ed_Type; /* * Match left argument (ltype) to first operator argument (decl) * * For an internal operator, void * in the operator declaration * matches any pointer and void @ in the operator declaration matches * any reference. * * Set the internal1 variable for either internal or internal2 scope * when the first argument of the operator declaration is a void * or * void @. */ if ((scope & (SCOPE_INTERNAL | SCOPE_INTERNAL2)) && ltype->ty_Op == TY_PTRTO && SimilarType(&VoidPtrType, dltype)) { /* ltype pointer matches against void * operator declaration */ internal1 = 1; } else if ((scope & (SCOPE_INTERNAL | SCOPE_INTERNAL2)) && ltype->ty_Op == TY_REFTO && SimilarType(&VoidRefType, dltype)) { /* ltype pointer matches against void @ operator declaration */ internal1 = 1; } else if (MatchType(dltype, ltype) >= SG_COMPAT_FAIL) { /* normal match failed, return failure */ return (0); } else { /* * Normal match succeeded and was not an internal-scoped pointer * or reference. */ internal1 = 0; } /* * The left argument matched the declaration, now for the right hand * side. * * Obtain the second argument, terminate unary operator, and fail if * we have a unary operator against a binary expression or a binary * operator against a unary expression. */ while ((d = RUNE_NEXT(d, d_Node)) && d->d_Op != DOP_ARGS_STORAGE) ; if (rtype == NULL) return d ? 0 : 1; if (d == NULL) return 0; drtype = d->d_StorDecl.ed_Type; /* * Binary operator. * * If internal2 and the declaration's arg type is a pointer, rtype * can be any pointer and does not have to match the ltype. * * else if internal1 is active (meaning the left side was a pointer * under internal1 guidelines), then the right and left sides must * be the same type of pointer. */ internal2 = (scope & SCOPE_INTERNAL2); //printf("CHECK\n"); if (SimilarType(&VoidPtrType, drtype)) { //printf("Q1\n"); if (internal2 && SimilarType(&VoidPtrType, rtype)) return 1; //printf("Q2\n"); if (internal1 && MatchType(ltype, rtype) < SG_COMPAT_FAIL) return 1; //printf("Q3\n"); } if (SimilarType(&VoidRefType, drtype)) { //printf("Q4\n"); if (internal2 && SimilarType(&VoidRefType, rtype)) return 1; //printf("Q5\n"); if (internal1 && MatchType(ltype, rtype) < SG_COMPAT_FAIL) return 1; //printf("Q6\n"); } /* * Otherwise no special deals are active, try to match the * types directly. */ if (MatchType(drtype, rtype) < SG_COMPAT_FAIL) return 1; #if 0 printf("END OPERATOR %s internal1=%d internal2=%d\n", runeid_text(id, buf), internal1, internal2); printf("Expression types %s\t", TypeToStr(ltype, NULL)); printf("%s\n", TypeToStr(rtype, NULL)); printf("Declaration types %s\t", TypeToStr(dltype, NULL)); printf("%s\n", TypeToStr(drtype, NULL)); #endif return 0; } /* * MatchCastTypes() - Match the return type and argument for a cast * * The cast decl is a procedure definition. Access the procedure arguments * and match them against the Lhs and Rhs types of the exp, returning 0 on * failure, 1 on match. * * XXX allow internal bindings of subclasses to super classes. */ int MatchCastTypes(Declaration *d, Type *ltype, Type *rtype) { Declaration *d2; Type *type; SemGroup *sg; int r; dassert_decl(d, d->d_Op == DOP_PROC && d->d_ProcDecl.ed_Type->ty_Op == TY_PROC); /* * Pull the procedure return type, which must match ltype */ type = d->d_ProcDecl.ed_Type->ty_ProcType.et_RetType; dassert(type->ty_Op != TY_UNRESOLVED); if (MatchType(type, ltype) >= SG_COMPAT_FAIL) return (0); /* * Pull the procedure argument type, which must be a compound type. */ type = d->d_ProcDecl.ed_Type->ty_ProcType.et_ArgsType; dassert_decl(d, type->ty_Op == TY_ARGS); sg = type->ty_ArgsType.et_SemGroup; /* * Obtain first argument and match against the cast argument (rtype). */ RUNE_FOREACH(d2, &sg->sg_DeclList, d_Node) { if (d2->d_Op == DOP_ARGS_STORAGE) break; } /* * Match types. Internal operators on pointers automatically * match against void pointers, and internal operators on references * automatically match against references. */ if (d2 == NULL) return (0); if ((d->d_ScopeFlags & SCOPE_INTERNAL) && rtype->ty_Op == TY_PTRTO && SimilarType(&VoidPtrType, d2->d_StorDecl.ed_Type)) { r = 0; } else if ((d->d_ScopeFlags & SCOPE_INTERNAL) && rtype->ty_Op == TY_REFTO && SimilarType(&VoidRefType, d2->d_StorDecl.ed_Type)) { r = 0; } else { r = MatchType(d2->d_StorDecl.ed_Type, rtype); } if (r >= SG_COMPAT_FAIL) return (0); /* * Obtain the second argument .. that is, make sure there isn't a second * argument. */ while ((d2 = RUNE_NEXT(d2, d_Node)) && d->d_Op != DOP_ARGS_STORAGE) ; if (d2) return (0); return (1); } /* * FindDLLSymbol() * * Find the requested DLL symbol by recursing through our SemGroup's looking * for DLL imports. If found, return its value. */ void * FindDLLSymbol(SemGroup *osg, SemGroup *sg, string_t str) { while (sg) { /* * Look for the declaration in this SemGroup */ Declaration *d; RUNE_FOREACH(d, &sg->sg_DeclList, d_Node) { if (d->d_Op == DOP_IMPORT) { Stmt *st; void (*func) (void); st = d->d_Stmt; dassert(st && st->st_Op == ST_Import); if (st->st_ImportStmt.es_DLL) { func = dlsym(st->st_ImportStmt.es_DLL, str); if (func) return (func); } } } if (osg && sg->sg_Stmt && sg->sg_Stmt->st_Op == ST_Class) { sg = osg; osg = NULL; } else { if (sg->sg_Flags & SGF_SELFCONTAINED) break; sg = sg->sg_Parent; } } return (NULL); } /* * FindDeclPath() * * Find the declaration associated with the array of identifiers representing * id.id.id... * * We are allowed to recurse backwards in order to locate the first id in the * array, and to recurse downwards through 'self' or explicitly named mounts. * Once the first identifier matches, however, we can only recurse downwards. * We do not try to overload our searches. * * When recursing backwards it is not possible to access non-global storage * elements of a class, because there is no context in which to get at those * variables (you have to go through "." or "->" via 'this' or something like * that). But it is ok to access globals and types. * * Note that typedef's do not match their own identifier, so something like * 'typedef int int' works to create a qualified type that hides the * original. XXX remove * * 'level' is used when resolving identifiers in a subclass that were pulled * in from the superclass. Also, if we have to go backwards (the identifier * can't be found in the subclass), we have to switch to the original * superclass's semgroup. LEVEL IS ONLY USED WHEN RESOLVING IDENTIFIERS * RELATIVE TO A "." OR "->" EXPRESSION, OR WHEN THE 'CURRENT' SEMGROUP IS * THE CLASS ITSELF (I.E. DEFAULT ASSIGNMENTS AND THE DECLARATION ITSELF, BUT * NOT A PROCEDURE BODY). * * On success *visibility will be set to the base visibility required if any * further searches are made relative to the returned declaration. * * On failure *visibility is garbage. */ Declaration * FindDeclPath(LexRef *lexRef, SemGroup *osg, SemGroup *sg, Type *nomatch, runeid_t *ary, int mask, int *visibility, int level, int *eno) { int i; int noLocalStorage = 0; Declaration *d = NULL; i = 0; while (ary[i] && sg) { /* * Look for the declaration in this SemGroup */ d = findDecl(sg, ary[i], *visibility, level, eno); if (d != NULL) { if (nomatch && d->d_Op == DOP_TYPEDEF && nomatch == d->d_TypedefDecl.ed_Type) { d = NULL; } } /* * Look for the declaration in the procedure arguments if this is the * top level of a procedure. */ if (d == NULL && sg->sg_Stmt && sg->sg_Stmt->st_Op == ST_Proc) { Type *argsType; SemGroup *sg2; d = sg->sg_Stmt->st_ProcStmt.es_Decl; dassert_decl(d, d->d_Op == DOP_PROC); argsType = d->d_ProcDecl.ed_Type->ty_ProcType.et_ArgsType; dassert_decl(d, argsType->ty_Op == TY_ARGS); sg2 = argsType->ty_ArgsType.et_SemGroup; d = findDecl(sg2, ary[i], *visibility, level, eno); } /* * Look for the declaration by recursing down into "self" imports. We * do not overload on partial matches.. a partial match is considered * to be a failure. * * If crossing a self-contained boundary only public scope can be * searched. Otherwise both public and library scope (but not * private scope) can be searched. */ if (d == NULL) { d = findDecl(sg, RUNEID_SELF, *visibility, sg->sg_Level, eno); while (d) { Declaration *d2; int visibility2; if (d->d_MyGroup != sg || d->d_Id != RUNEID_SELF || d->d_Op != DOP_IMPORT) { d = d->d_HNext; continue; } dassert_decl(d, d->d_ImportDecl.ed_SemGroup != NULL); if (d->d_ImportDecl.ed_SemGroup->sg_Flags & SGF_SELFCONTAINED) { visibility2 = *visibility & ~(SCOPE_LIBRARY | SCOPE_PRIVATE); } else { visibility2 = *visibility & ~SCOPE_PRIVATE; } d2 = FindDeclPath(lexRef, NULL, d->d_ImportDecl.ed_SemGroup, nomatch, ary + i, (mask & ~FDC_NULL) | FDC_NOBACK, &visibility2, -1, eno); if (d2 == (void *)-1) { if (mask & FDC_NULL) d2 = NULL; return (d2); } /* * This was a complete search of all remaining array * elements, so return on success. */ if (d2) { *visibility = visibility2; return (d2); } d = d->d_HNext; } } /* * If still not found then go back a level. If we cross an import * boundary we lose private-scope (and do not regain it even if we * cross back into the same library). If we cross a class boundary * we may have to locate the superclass's SG. */ if (d == NULL) { /* * If we are leaving a procedure context */ if (sg && sg->sg_Stmt && sg->sg_Stmt->st_Op == ST_Proc) { if ((sg->sg_Stmt->st_Flags & STF_NESTED) == 0) noLocalStorage = 1; } /* * If we are crossing a class statement boundary we have to jump * to the original import semantic context. For example, when a * procedure is pulled in from a superclass we want to jump to * its original semantic context, not the context our subclass is * defined in. * * XXX I really wanted the 'original semantic context of the * statement' but it's a mess. This works just as well. */ if (osg && sg->sg_Stmt && sg->sg_Stmt->st_Op == ST_Class) { sg = osg; osg = NULL; *visibility = SCOPE_ALL_VISIBLE; } else { if ((mask & FDC_NOBACK) || (sg->sg_Flags & SGF_SELFCONTAINED)) { return (NULL); } if (sg->sg_Flags & SGF_SEMTOP) *visibility &= ~SCOPE_PRIVATE; /* * If SGF_ALTPRIORITY is set this was an inlined procedure * and the sg_Parent link is not the proper semantic path (it * isn't visible namespace-wise). In this situation the * inliner in resolve.c has set sg_Altcontext to the proper * group for searching. */ if (sg->sg_Flags & SGF_ALTPRIORITY) sg = sg->sg_AltContext; else sg = sg->sg_Parent; } /* * You can't back into a class's local storage (from a process). */ if (sg && sg->sg_Stmt && sg->sg_Stmt->st_Op == ST_Class) noLocalStorage = 1; continue; } /* * FOUND! No going backwards now, only recurse downwards through * imports and classes from this point on. * * We lose private scope when we go through an import, and we loose * library scope when we cross a selfcontained (library) boundary. * Also, when pushing into a class semantically there is no context * in which to access local storage. */ mask |= FDC_NOBACK; switch (d->d_Op) { case DOP_IMPORT: sg = d->d_ImportDecl.ed_SemGroup; dassert_decl(d, sg != NULL); if (sg->sg_Flags & SGF_SELFCONTAINED) *visibility &= ~(SCOPE_LIBRARY | SCOPE_PRIVATE); else *visibility &= ~SCOPE_PRIVATE; break; case DOP_CLASS: /* * pushdown into a class. Visibility remains the same. */ sg = d->d_ClassDecl.ed_SemGroup; noLocalStorage = 1; break; case DOP_GROUP_STORAGE: case DOP_ARGS_STORAGE: case DOP_STACK_STORAGE: if (noLocalStorage) { fprintf(stderr, "You cannot access instantiated " "storage this way, there is " "no context.\n" "Perhaps you meant to " "use 'this.'.\n"); LexPrintRef(lexRef, 0); dpanic(""); } /* fall through */ default: dassert_decl(d, ary[i + 1] == 0); sg = NULL; break; } ++i; } return (d); } /* * Look for the declaration via an alternative context. sg_AltContext is * temporarily set while resolving the expression tree, primarily to get more * direct access to type globals when making method calls (otherwise the code * has to prefix each one with the type or object which can be messy). */ Declaration * FindDeclPathAltContext(LexRef *lexRef, SemGroup *osg, SemGroup *sg, Type *nomatch, runeid_t *ary, int mask, int *visibility, int level, int *eno) { Declaration *d; while (sg) { if (sg->sg_AltContext) { d = FindDeclPath(lexRef, osg, sg->sg_AltContext, nomatch, ary, mask, visibility, level, eno); if (d && (d->d_ScopeFlags & SCOPE_GLOBAL)) return d; } if ((sg->sg_Flags & (SGF_SEMTOP | SGF_NESTED)) == SGF_SEMTOP) break; sg = sg->sg_Parent; } return NULL; } Declaration * FindDeclId(SemGroup *sg, runeid_t id, int *eno) { Declaration *d; d = findDecl(sg, id, SCOPE_ALL_VISIBLE, sg->sg_Level, eno); return (d); } /* * This version is used by the resolver to allow matches against refining * declarations to ignore scope. Scope might not be assigned to such * declarations yet and will inherit the scope of the declaration in the * superclass that is being refined. */ Declaration * FindDeclRefineId(SemGroup *sg, runeid_t id, int *eno) { Declaration *d; d = findDecl(sg, id, SCOPE_REFINE | SCOPE_ALL_VISIBLE, sg->sg_Level, eno); return (d); } Declaration * FindDeclIdLevel(SemGroup *sg, runeid_t id, int level, int *eno) { Declaration *d; d = findDecl(sg, id, SCOPE_ALL_VISIBLE, level, eno); return (d); } /* * RefineDeclaration() - rd is a refinement of sd. Fixup rd. * * sg is the semgroup representing our subclass (in the middle of being * built). Refinements can be: * * - Completely compatible, such as a simple change in defaults * * - Partially compatible, such as compatible method overrides (means can * still be directly accessed via a superclass @ref). * * - Incompatible, when refinements change the underlying type, arguments, or * return values in incompatible ways. * * XXX YYY * * NOTE: May be called multiple times with the same argument. */ void RefineDeclaration(SemGroup *sg, Declaration *sd, Declaration *rd) { int compat = MatchDeclTypes(sd, rd); if (compat < SG_COMPAT_PART && sd->d_Op == DOP_PROC) compat = SG_COMPAT_PART; if (sg->sg_Compat < compat) sg->sg_Compat = compat; if (compat == SG_COMPAT_FAIL) { fprintf(stderr, "Refinement is illegal\n"); dassert_decl(rd, 0); } } /* * DupDeclaration() - duplicate a declaration (called during resolve) * * Note: When you dup a resolved declaration, it becomes unresolved. The * semGroup the decl's is being dup'd into must be resolved afterwords. Note * that the d_Index's will not necessarily match up. * * Note: d_Level and d_Search are inherited from sd. d_Level should be * non-NULL if sd is a declaration within a class, and NULL otherwise. * d_Search will be non-NULL when we integrate a declaration into a subclass * recursively. That is, if you have class A and subclass B of A, and * subclass C of B, then when C pulls in a declaration from B that was * originally from A, it will see a non-NULL d_Search in b's version of the * declaration. */ Declaration * DupDeclaration(SemGroup *sg, Declaration *sd) { Declaration *d = AllocDeclaration(sg, sd->d_Op, &sd->d_Scope); SemGroup *xsg; LexDupRef(&sd->d_LexRef, &d->d_LexRef); if (sd->d_Id) HashDecl(d, sd->d_Id); d->d_Flags = sd->d_Flags & ~(DF_RESOLVED | DF_RESOLVING | DF_LAYOUT | DF_DIDPULLDOWN | DF_ONCLIST | DF_ONDLIST | DF_ONGLIST | DF_ONSRLIST | DF_ADDROF | DF_ADDRUSED); d->d_RState = 0; d->d_Level = sd->d_Level; d->d_Search = sd->d_Search; d->d_ImportSemGroup = sd->d_ImportSemGroup; d->d_Super = sd; d->d_Level = sd->d_Level; d->d_Stmt = sd->d_Stmt; /* do not dup d_DynIndex */ /* * We do not need to re-evaluate types for the new SemGroup unless the * change of venue would cause them to change (such as a refined * typedef). */ switch (sg->sg_Op) { case SG_CLASS: case SG_COMPOUND: case SG_PROCARGS: xsg = sg; break; default: xsg = NULL; break; } /* * XXX decl may be resolved if dup'ing a semgroup for a varargs call. See * libd/resolve.d 'Too many arg' */ #if 0 dassert_decl(d, (d->d_Flags & (DF_RESOLVED | DF_RESOLVING)) == 0); #endif /* * Just reuse OrigAssExp. The resolver will dup the epxression to * ed_AssExp later on. */ switch (d->d_Op) { case DOP_CLASS: case DOP_IMPORT: /* * Only occurs inside import, so dup() should never be called on it. */ dassert_decl(d, 0); break; case DOP_ARGS_STORAGE: case DOP_STACK_STORAGE: case DOP_GLOBAL_STORAGE: case DOP_GROUP_STORAGE: d->d_StorDecl.ed_Type = DupType(xsg, sd->d_StorDecl.ed_Type); d->d_StorDecl.ed_OrigAssExp = sd->d_StorDecl.ed_OrigAssExp; //d->d_StorDecl.ed_AssExp = NULL; break; case DOP_TYPEDEF: d->d_TypedefDecl.ed_Type = DupType(xsg, sd->d_TypedefDecl.ed_Type); d->d_TypedefDecl.ed_OrigAssExp = sd->d_TypedefDecl.ed_OrigAssExp; //d->d_TypedefDecl.ed_AssExp = NULL; break; case DOP_ALIAS: d->d_AliasDecl.ed_Type = DupType(xsg, sd->d_AliasDecl.ed_Type); d->d_AliasDecl.ed_OrigAssExp = sd->d_AliasDecl.ed_OrigAssExp; //d->d_AliasDecl.ed_AssExp = NULL; break; case DOP_PROC: /* * We do not duplicate the procedure body here. If we were to it * would result in X*Y copies of the procedure whether they were used * or not. Instead this is done at resolve time in resolveDecl(). */ d->d_ProcDecl.ed_Type = DupType(xsg, sd->d_ProcDecl.ed_Type); d->d_ProcDecl.ed_OrigBody = sd->d_ProcDecl.ed_OrigBody; if (sd->d_ProcDecl.ed_OperId) HashOper(d, sd->d_ProcDecl.ed_OperId); break; default: dassert_decl(d, 0); } return (d); } void AdjustProcArgsNestingLevel(Declaration *d, SemGroup *fromSg) { Type *type; SemGroup *sg; dassert_decl(d, d->d_Op == DOP_PROC && d->d_ProcDecl.ed_Type->ty_Op == TY_PROC); type = d->d_ProcDecl.ed_Type->ty_ProcType.et_ArgsType; dassert_decl(d, type->ty_Op == TY_ARGS); sg = type->ty_ArgsType.et_SemGroup; sg->sg_NestLevel = fromSg->sg_NestLevel; sg->sg_NestSize = fromSg->sg_NestSize; } void AdjustNestSize(SemGroup *sg, int size) { for (;;) { sg->sg_NestSize = size; if ((sg->sg_Flags & (SGF_SEMTOP | SGF_NESTED)) == SGF_SEMTOP) break; sg = sg->sg_Parent; } } /* * Extra cacheability under certain circumstances. Is not all-encompassing. * Used to determine if an inline procedure argument can be cached. */ int DeclarationCacheable(Declaration *d) { Type *type; dassert_decl(d, d->d_Op & DOPF_STORAGE); type = d->d_StorDecl.ed_Type; if (d->d_Flags & DF_ADDRUSED) return 0; if (d->d_Scope.s_Flags & SCOPE_LVALUE) return 0; if (type->ty_Flags & (TF_ISINTEGER | TF_ISFLOATING | TF_ISBOOL)) return 1; return 0; }