From 73770ce573422c95347abc774928cf8e9b7c546a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 20 May 2022 17:03:27 +0200 Subject: [PATCH] winedbg: Do type / id discrimination when parsing. - Get rid of struct type_expr_t - Enable back typecasts (and rewrite the typecast to fit the type parsing scheme) Signed-off-by: Eric Pouech --- programs/winedbg/dbg.y | 14 ++-- programs/winedbg/debug.l | 9 ++- programs/winedbg/debugger.h | 34 +++------- programs/winedbg/expr.c | 131 +++++++++++++++--------------------- programs/winedbg/stack.c | 8 +-- programs/winedbg/types.c | 85 +++++++++++++---------- 6 files changed, 133 insertions(+), 148 deletions(-) diff --git a/programs/winedbg/dbg.y b/programs/winedbg/dbg.y index 748d16e96c5..6faa196e052 100644 --- a/programs/winedbg/dbg.y +++ b/programs/winedbg/dbg.y @@ -44,7 +44,7 @@ static void parser(const char*); dbg_lgint_t integer; IMAGEHLP_LINE64 listing; struct expr* expression; - struct type_expr_t type; + struct dbg_type type; struct list_string* strings; } @@ -57,6 +57,7 @@ static void parser(const char*); %token tSTEPI tNEXTI tFINISH tSHOW tDIR tWHATIS tSOURCE %token tPATH tIDENTIFIER tSTRING tINTVAR %token tNUM tFORMAT +%token tTYPEDEF %token tSYMBOLFILE tRUN tATTACH tDETACH tKILL tMAINTENANCE tTYPE tMINIDUMP %token tNOPROCESS @@ -346,11 +347,12 @@ type_expr: | tFLOAT { if (!types_find_basic(L"float", $1, &$$)) YYERROR; } | tDOUBLE { if (!types_find_basic(L"double", $1, &$$)) YYERROR; } | tLONG tDOUBLE { if (!types_find_basic(L"long double", $1, &$$)) YYERROR; } - | type_expr '*' { $$ = $1; $$.deref_count++; } - | tCLASS identifier { $$.type = type_expr_udt_class; $$.deref_count = 0; $$.u.name = $2; } - | tSTRUCT identifier { $$.type = type_expr_udt_struct; $$.deref_count = 0; $$.u.name = $2; } - | tUNION identifier { $$.type = type_expr_udt_union; $$.deref_count = 0; $$.u.name = $2; } - | tENUM identifier { $$.type = type_expr_enumeration; $$.deref_count = 0; $$.u.name = $2; } + | tTYPEDEF { $$ = $1; } + | type_expr '*' { if (!types_find_pointer(&$1, &$$)) {yyerror("Cannot find pointer type\n"); YYERROR; } } + | tCLASS identifier { if (!types_find_type($2, SymTagUDT, &$$)) {yyerror("Unknown type\n"); YYERROR; } } + | tSTRUCT identifier { if (!types_find_type($2, SymTagUDT, &$$)) {yyerror("Unknown type\n"); YYERROR; } } + | tUNION identifier { if (!types_find_type($2, SymTagUDT, &$$)) {yyerror("Unknown type\n"); YYERROR; } } + | tENUM identifier { if (!types_find_type($2, SymTagEnum, &$$)) {yyerror("Unknown type\n"); YYERROR; } } ; expr_lvalue: diff --git a/programs/winedbg/debug.l b/programs/winedbg/debug.l index 90dd4dfac11..1f8040ff976 100644 --- a/programs/winedbg/debug.l +++ b/programs/winedbg/debug.l @@ -100,6 +100,13 @@ static char* unescape_string(const char* str) return ret; } +static int resolve_identifier(const char* id, DBG_STYPE* lval) +{ + if (types_find_type(id, SymTagTypedef, &lval->type)) return tTYPEDEF; + lval->string = lexeme_alloc(id); + return tIDENTIFIER; +} + #define YY_INPUT(buf,result,max_size) \ (result = input_lex_read_buffer(buf, max_size)) @@ -258,7 +265,7 @@ union { return tUNION; } enum { return tENUM; } all { return tALL; } -{MODULE_IDENTIFIER}?{SCOPED_IDENTIFIER}*{IDENTIFIER} { dbg_lval.string = lexeme_alloc(yytext); return tIDENTIFIER; } +{MODULE_IDENTIFIER}?{SCOPED_IDENTIFIER}*{IDENTIFIER} { return resolve_identifier(yytext, &dbg_lval); } "$"{IDENTIFIER} { dbg_lval.string = lexeme_alloc(yytext+1); return tINTVAR; } {PATHNAME} { dbg_lval.string = lexeme_alloc(yytext); return tPATH; } diff --git a/programs/winedbg/debugger.h b/programs/winedbg/debugger.h index bde912eaee5..d5c5934cceb 100644 --- a/programs/winedbg/debugger.h +++ b/programs/winedbg/debugger.h @@ -150,14 +150,15 @@ static inline void init_lvalue(struct dbg_lvalue* lv, BOOL in_debuggee, void* ad lv->type.id = dbg_itype_none; } -static inline void init_lvalue_in_debugger(struct dbg_lvalue* lv, enum dbg_internal_types it, void* addr) +static inline void init_lvalue_in_debugger(struct dbg_lvalue* lv, DWORD_PTR module, + enum dbg_internal_types it, void* addr) { lv->in_debuggee = 0; lv->bitstart = 0; lv->bitlen = 0; lv->addr.Mode = AddrModeFlat; lv->addr.Offset = (DWORD_PTR)addr; - lv->type.module = 0; + lv->type.module = module; lv->type.id = it; } @@ -310,26 +311,6 @@ struct dbg_internal_var enum sym_get_lval {sglv_found, sglv_unknown, sglv_aborted}; -enum type_expr_e -{ - type_expr_type_id, - type_expr_udt_class, - type_expr_udt_struct, - type_expr_udt_union, - type_expr_enumeration -}; - -struct type_expr_t -{ - enum type_expr_e type; - unsigned deref_count; - union - { - struct dbg_type type; - const char* name; - } u; -}; - enum dbg_start {start_ok, start_error_parse, start_error_init}; /* break.c */ @@ -385,7 +366,7 @@ extern struct expr* expr_alloc_unary_op(int oper, struct expr*); extern struct expr* expr_alloc_pstruct(struct expr*, const char* element); extern struct expr* expr_alloc_struct(struct expr*, const char* element); extern struct expr* WINAPIV expr_alloc_func_call(const char*, int nargs, ...); -extern struct expr* expr_alloc_typecast(struct type_expr_t*, struct expr*); +extern struct expr* expr_alloc_typecast(struct dbg_type*, struct expr*); extern struct dbg_lvalue expr_eval(struct expr*); extern struct expr* expr_clone(const struct expr* exp, BOOL *local_binding); extern BOOL expr_free(struct expr* exp); @@ -510,12 +491,13 @@ extern BOOL types_udt_find_element(struct dbg_lvalue* value, const c extern BOOL types_array_index(const struct dbg_lvalue* value, int index, struct dbg_lvalue* result); extern BOOL types_get_info(const struct dbg_type*, IMAGEHLP_SYMBOL_TYPE_INFO, void*); extern BOOL types_get_real_type(struct dbg_type* type, DWORD* tag); -extern struct dbg_type types_find_pointer(const struct dbg_type* type); -extern struct dbg_type types_find_type(DWORD64 linear, const char* name, enum SymTagEnum tag); +extern BOOL types_find_pointer(const struct dbg_type* type, struct dbg_type* outtype); +extern BOOL types_find_type(const char* name, enum SymTagEnum tag, struct dbg_type* outtype); extern BOOL types_compare(const struct dbg_type, const struct dbg_type, BOOL* equal); extern BOOL types_is_integral_type(const struct dbg_lvalue*); extern BOOL types_is_float_type(const struct dbg_lvalue*); -extern BOOL types_find_basic(const WCHAR*, const char*, struct type_expr_t* type); +extern BOOL types_is_pointer_type(const struct dbg_lvalue*); +extern BOOL types_find_basic(const WCHAR*, const char*, struct dbg_type* type); /* winedbg.c */ #ifdef __GNUC__ diff --git a/programs/winedbg/expr.c b/programs/winedbg/expr.c index 5d9ef30ad13..9a927338ba8 100644 --- a/programs/winedbg/expr.c +++ b/programs/winedbg/expr.c @@ -75,8 +75,9 @@ struct expr struct { - struct type_expr_t cast_to; + struct dbg_type cast_to; struct expr* expr; + dbg_lgint_t result; } cast; struct @@ -128,14 +129,14 @@ void expr_free_all(void) next_expr_free = 0; } -struct expr* expr_alloc_typecast(struct type_expr_t* tet, struct expr* exp) +struct expr* expr_alloc_typecast(struct dbg_type* type, struct expr* exp) { struct expr* ex; ex = expr_alloc(); ex->type = EXPR_TYPE_CAST; - ex->un.cast.cast_to = *tet; + ex->un.cast.cast_to = *type; ex->un.cast.expr = exp; return ex; } @@ -272,75 +273,74 @@ struct expr* WINAPIV expr_alloc_func_call(const char* funcname, int nargs, ...) struct dbg_lvalue expr_eval(struct expr* exp) { struct dbg_lvalue rtn; - int i; struct dbg_lvalue exp1; struct dbg_lvalue exp2; DWORD64 scale1, scale2, scale3; struct dbg_type type1, type2; DWORD tag; const struct dbg_internal_var* div; + BOOL ret; - init_lvalue_in_debugger(&rtn, dbg_itype_none, NULL); + init_lvalue_in_debugger(&rtn, 0, dbg_itype_none, NULL); switch (exp->type) { case EXPR_TYPE_CAST: - /* this is really brute force, we simply change the type... without - * checking if this is right or not - */ - rtn = expr_eval(exp->un.cast.expr); - switch (exp->un.cast.cast_to.type) - { - case type_expr_type_id: - if (exp->un.cast.cast_to.u.type.id == dbg_itype_none) - { - dbg_printf("Can't cast to unknown type\n"); - RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL); - } - rtn.type = exp->un.cast.cast_to.u.type; - break; - case type_expr_udt_class: - case type_expr_udt_struct: - case type_expr_udt_union: - rtn.type = types_find_type(rtn.type.module, exp->un.cast.cast_to.u.name, - SymTagUDT); - if (rtn.type.id == dbg_itype_none) - { - dbg_printf("Can't cast to UDT %s\n", exp->un.cast.cast_to.u.name); - RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL); - } - break; - case type_expr_enumeration: - rtn.type = types_find_type(rtn.type.module, exp->un.cast.cast_to.u.name, - SymTagEnum); - if (rtn.type.id == dbg_itype_none) - { - dbg_printf("Can't cast to enumeration %s\n", exp->un.cast.cast_to.u.name); - RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL); - } - break; - default: - dbg_printf("Unsupported cast type %u\n", exp->un.cast.cast_to.type); + exp1 = expr_eval(exp->un.cast.expr); + if (exp1.type.id == dbg_itype_none) RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL); - } - for (i = 0; i < exp->un.cast.cast_to.deref_count; i++) + rtn = exp1; + rtn.type = exp->un.cast.cast_to; + init_lvalue_in_debugger(&rtn, exp->un.cast.cast_to.module, exp->un.cast.cast_to.id, &exp->un.cast.result); + if (types_is_float_type(&exp1)) { - rtn.type = types_find_pointer(&rtn.type); - if (rtn.type.id == dbg_itype_none) + double dbl; + ret = memory_fetch_float(&exp1, &dbl); + if (ret) { - dbg_printf("Cannot find pointer type\n"); - RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL); + if (types_is_float_type(&rtn)) + ret = memory_store_float(&rtn, &dbl); + else if (types_is_integral_type(&rtn)) + ret = memory_store_integer(&rtn, (dbg_lgint_t)dbl); + else + ret = FALSE; } } + else if (types_is_integral_type(&exp1)) + { + dbg_lgint_t val = types_extract_as_integer(&exp1); + if (types_is_float_type(&rtn)) + { + double dbl = val; + ret = memory_store_float(&rtn, &dbl); + } + else if (types_is_integral_type(&rtn) || types_is_pointer_type(&rtn)) + ret = memory_store_integer(&rtn, val); + else + ret = FALSE; + } + else if (types_is_pointer_type(&exp1)) + { + dbg_lgint_t val = types_extract_as_integer(&exp1); + + if (types_is_integral_type(&rtn) || types_is_pointer_type(&rtn)) + ret = memory_store_integer(&rtn, val); + else + ret = FALSE; + } + else + ret = FALSE; + if (!ret) + RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL); break; case EXPR_TYPE_STRING: - init_lvalue_in_debugger(&rtn, dbg_itype_astring, &exp->un.string.str); + init_lvalue_in_debugger(&rtn, 0, dbg_itype_astring, &exp->un.string.str); break; case EXPR_TYPE_U_CONST: - init_lvalue_in_debugger(&rtn, dbg_itype_lguint, &exp->un.u_const.value); + init_lvalue_in_debugger(&rtn, 0, dbg_itype_lguint, &exp->un.u_const.value); break; case EXPR_TYPE_S_CONST: - init_lvalue_in_debugger(&rtn, dbg_itype_lgint, &exp->un.s_const.value); + init_lvalue_in_debugger(&rtn, 0, dbg_itype_lgint, &exp->un.s_const.value); break; case EXPR_TYPE_SYMBOL: switch (symbol_get_lvalue(exp->un.symbol.name, -1, &rtn, FALSE)) @@ -440,7 +440,7 @@ struct dbg_lvalue expr_eval(struct expr* exp) */ exp->un.call.result = 0; #endif - init_lvalue_in_debugger(&rtn, dbg_itype_none, &exp->un.call.result); + init_lvalue_in_debugger(&rtn, 0, dbg_itype_none, &exp->un.call.result); /* get return type from function signature type */ /* FIXME rtn.type.module should be set to function's module... */ types_get_info(&rtn.type, TI_GET_TYPE, &rtn.type.id); @@ -448,14 +448,14 @@ struct dbg_lvalue expr_eval(struct expr* exp) case EXPR_TYPE_INTVAR: if (!(div = dbg_get_internal_var(exp->un.intvar.name))) RaiseException(DEBUG_STATUS_NO_SYMBOL, 0, 0, NULL); - init_lvalue_in_debugger(&rtn, div->typeid, div->pval); + init_lvalue_in_debugger(&rtn, 0, div->typeid, div->pval); break; case EXPR_TYPE_BINOP: exp1 = expr_eval(exp->un.binop.exp1); exp2 = expr_eval(exp->un.binop.exp2); if (exp1.type.id == dbg_itype_none || exp2.type.id == dbg_itype_none) RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL); - init_lvalue_in_debugger(&rtn, dbg_itype_lgint, &exp->un.binop.result); + init_lvalue_in_debugger(&rtn, 0, dbg_itype_lgint, &exp->un.binop.result); type1 = exp1.type; type2 = exp2.type; switch (exp->un.binop.binop_type) @@ -584,7 +584,7 @@ struct dbg_lvalue expr_eval(struct expr* exp) case EXPR_TYPE_UNOP: exp1 = expr_eval(exp->un.unop.exp1); if (exp1.type.id == dbg_itype_none) RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL); - init_lvalue_in_debugger(&rtn, dbg_itype_lgint, &exp->un.unop.result); + init_lvalue_in_debugger(&rtn, 0, dbg_itype_lgint, &exp->un.unop.result); switch (exp->un.unop.unop_type) { case EXP_OP_NEG: @@ -610,8 +610,7 @@ struct dbg_lvalue expr_eval(struct expr* exp) if (exp1.addr.Mode != AddrModeFlat) RaiseException(DEBUG_STATUS_CANT_DEREF, 0, 0, NULL); exp->un.unop.result = (ULONG_PTR)memory_to_linear_addr(&exp1.addr); - rtn.type = types_find_pointer(&exp1.type); - if (rtn.type.id == dbg_itype_none) + if (!types_find_pointer(&exp1.type, &rtn.type)) RaiseException(DEBUG_STATUS_CANT_DEREF, 0, 0, NULL); break; default: RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); @@ -629,30 +628,12 @@ struct dbg_lvalue expr_eval(struct expr* exp) BOOL expr_print(const struct expr* exp) { int i; - struct dbg_type type; switch (exp->type) { case EXPR_TYPE_CAST: - WINE_FIXME("No longer supported (missing module base)\n"); dbg_printf("(("); - switch (exp->un.cast.cast_to.type) - { - case type_expr_type_id: - type.module = 0; - type.id = exp->un.cast.cast_to.type; - types_print_type(&type, FALSE); break; - case type_expr_udt_class: - dbg_printf("class %s", exp->un.cast.cast_to.u.name); break; - case type_expr_udt_struct: - dbg_printf("struct %s", exp->un.cast.cast_to.u.name); break; - case type_expr_udt_union: - dbg_printf("union %s", exp->un.cast.cast_to.u.name); break; - case type_expr_enumeration: - dbg_printf("enum %s", exp->un.cast.cast_to.u.name); break; - } - for (i = 0; i < exp->un.cast.cast_to.deref_count; i++) - dbg_printf("*"); + types_print_type(&exp->un.cast.cast_to, FALSE); dbg_printf(")"); expr_print(exp->un.cast.expr); dbg_printf(")"); diff --git a/programs/winedbg/stack.c b/programs/winedbg/stack.c index 50932e14ddd..01a86108d6e 100644 --- a/programs/winedbg/stack.c +++ b/programs/winedbg/stack.c @@ -103,7 +103,7 @@ BOOL stack_get_register_frame(const struct dbg_internal_var* div, struct dbg_lva struct dbg_frame* currfrm = stack_get_curr_frame(); if (currfrm == NULL) return FALSE; if (currfrm->is_ctx_valid) - init_lvalue_in_debugger(lvalue, div->typeid, + init_lvalue_in_debugger(lvalue, 0, div->typeid, (char*)&currfrm->context + (DWORD_PTR)div->pval); else { @@ -116,13 +116,13 @@ BOOL stack_get_register_frame(const struct dbg_internal_var* div, struct dbg_lva switch (kind) { case be_cpu_addr_pc: - init_lvalue_in_debugger(lvalue, itype, &currfrm->linear_pc); + init_lvalue_in_debugger(lvalue, 0, itype, &currfrm->linear_pc); break; case be_cpu_addr_stack: - init_lvalue_in_debugger(lvalue, itype, &currfrm->linear_stack); + init_lvalue_in_debugger(lvalue, 0, itype, &currfrm->linear_stack); break; case be_cpu_addr_frame: - init_lvalue_in_debugger(lvalue, itype, &currfrm->linear_frame); + init_lvalue_in_debugger(lvalue, 0, itype, &currfrm->linear_frame); break; } } diff --git a/programs/winedbg/types.c b/programs/winedbg/types.c index f9860bd12c1..b8f29058162 100644 --- a/programs/winedbg/types.c +++ b/programs/winedbg/types.c @@ -300,7 +300,8 @@ BOOL types_array_index(const struct dbg_lvalue* lvalue, int index, struct dbg_lv } break; default: - assert(FALSE); + FIXME("unexpected tag %lx\n", tag); + return FALSE; } /* * Get the base type, so we know how much to index by. @@ -332,13 +333,9 @@ BOOL types_array_index(const struct dbg_lvalue* lvalue, int index, struct dbg_lv struct type_find_t { - ULONG result; /* out: the found type */ enum SymTagEnum tag; /* in: the tag to look for */ - union - { - ULONG typeid; /* when tag is SymTagUDT */ - const char* name; /* when tag is SymTagPointerType */ - } u; + struct dbg_type type; /* out: the type found */ + ULONG ptr_typeid; /* in: when tag is SymTagPointerType */ }; static BOOL CALLBACK types_cb(PSYMBOL_INFO sym, ULONG size, void* _user) @@ -353,18 +350,18 @@ static BOOL CALLBACK types_cb(PSYMBOL_INFO sym, ULONG size, void* _user) switch (user->tag) { case SymTagUDT: - if (!strcmp(user->u.name, sym->Name)) - { - user->result = sym->TypeIndex; - ret = FALSE; - } + case SymTagEnum: + case SymTagTypedef: + user->type.module = sym->ModBase; + user->type.id = sym->TypeIndex; + ret = FALSE; break; case SymTagPointerType: type.module = sym->ModBase; type.id = sym->TypeIndex; - if (types_get_info(&type, TI_GET_TYPE, &type_id) && type_id == user->u.typeid) + if (types_get_info(&type, TI_GET_TYPE, &type_id) && type_id == user->ptr_typeid) { - user->result = sym->TypeIndex; + user->type = type; ret = FALSE; } break; @@ -380,18 +377,17 @@ static BOOL CALLBACK types_cb(PSYMBOL_INFO sym, ULONG size, void* _user) * Should look up in module based at linear whether (typeid*) exists * Otherwise, we could create it locally */ -struct dbg_type types_find_pointer(const struct dbg_type* type) +BOOL types_find_pointer(const struct dbg_type* type, struct dbg_type* outtype) { struct type_find_t f; - struct dbg_type ret; - f.result = dbg_itype_none; + f.type.id = dbg_itype_none; f.tag = SymTagPointerType; - f.u.typeid = type->id; - SymEnumTypes(dbg_curr_process->handle, type->module, types_cb, &f); - ret.module = type->module; - ret.id = f.result; - return ret; + f.ptr_typeid = type->id; + if (!SymEnumTypes(dbg_curr_process->handle, type->module, types_cb, &f) || f.type.id == dbg_itype_none) + return FALSE; + *outtype = f.type; + return TRUE; } /****************************************************************** @@ -400,19 +396,29 @@ struct dbg_type types_find_pointer(const struct dbg_type* type) * Should look up in the module based at linear address whether a type * named 'name' and with the correct tag exists */ -struct dbg_type types_find_type(DWORD64 linear, const char* name, enum SymTagEnum tag) - +BOOL types_find_type(const char* name, enum SymTagEnum tag, struct dbg_type* outtype) { struct type_find_t f; - struct dbg_type ret; + char* str = NULL; + BOOL ret; - f.result = dbg_itype_none; + if (!strchr(name, '!')) /* no module, lookup across all modules */ + { + str = malloc(strlen(name) + 3); + if (!str) return FALSE; + str[0] = '*'; + str[1] = '!'; + strcpy(str + 2, name); + name = str; + } + f.type.id = dbg_itype_none; f.tag = tag; - f.u.name = name; - SymEnumTypes(dbg_curr_process->handle, linear, types_cb, &f); - ret.module = linear; - ret.id = f.result; - return ret; + ret = SymEnumTypesByName(dbg_curr_process->handle, 0, name, types_cb, &f); + free(str); + if (!ret || f.type.id == dbg_itype_none) + return FALSE; + *outtype = f.type; + return TRUE; } /*********************************************************************** @@ -916,7 +922,7 @@ static BOOL CALLBACK enum_mod_cb(const char* module, DWORD64 base, void* user) return TRUE; } -BOOL types_find_basic(const WCHAR* name, const char* mod, struct type_expr_t* type) +BOOL types_find_basic(const WCHAR* name, const char* mod, struct dbg_type* type) { const struct data_model* model; struct mod_by_name mbn = {mod, 0}; @@ -928,15 +934,13 @@ BOOL types_find_basic(const WCHAR* name, const char* mod, struct typ SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES, opt); if (!ret || mbn.base == 0) return FALSE; - type->type = type_expr_type_id; - type->deref_count = 0; model = get_data_model(mbn.base); for (; model->name; model++) { if (!wcscmp(name, model->name)) { - type->u.type.module = 0; - type->u.type.id = model->itype; + type->module = 0; + type->id = model->itype; return TRUE; } } @@ -1238,3 +1242,12 @@ BOOL types_is_float_type(const struct dbg_lvalue* lv) !types_get_info(&type, TI_GET_BASETYPE, &bt)) return FALSE; return bt == btFloat; } + +BOOL types_is_pointer_type(const struct dbg_lvalue* lv) +{ + struct dbg_type type = lv->type; + DWORD tag; + if (lv->bitlen) return FALSE; + return types_get_real_type(&type, &tag) && + (tag == SymTagPointerType || tag == SymTagArrayType || tag == SymTagFunctionType); +}