// -*- c++ -*- // cc_tcheck.ast see license.txt for copyright and terms of use // extension module for cc.ast that defines the entry points // for the type checker, and the annotations it produces // the implementations for the functions declared here are in // cc_tcheck.cc and cc_ast_aux.cc // extension modules' first "verbatim" section goes after // the initial verbatims from the base, but before any // of the classes from the base verbatim { #include "variable.h" // Variable #include "cc_type.h" // Type, FunctonType, CompoundType #include "template.h" // STemplateArgument #include "const_eval.h" // ConstEval #include "macros.h" // Restorer #include "stdconv.h" // StandardConversion #include "llvm/ADT/APFloat.h" // APFloat class Env; // cc_env.h class ArgumentInfo; // overload.h class DataBlock; // smbase/datablok.h #if RICH inline void fromXml(DeclaratorContext &out, sm::string str) { out = static_cast(atoi(str)); } // defined in cc_ast_aux.cc sm::string toXml(DataBlock *&block); #endif } impl_verbatim { // NOTE: do not include xml_type_id.h in the header, because it needs to // include cc.gen.ast.h. // RICH: #include "xml_type_id.h" // type system serialization identity } // this code goes into the xml serialization class xml_verbatim { /* this is elsa/cc_tcheck.ast */ virtual bool shouldSerialize(Variable const *) { return true; } } custom ASTVisitor { public: // custom data extensions // this is true when we are inside the body of a template function // or class (uninstantiated) bool inTemplate; } custom ASTVisitor_ctor { inTemplate=false; } class TranslationUnit { public(xml_TY) Scope *globalScope = NULL; // This is the type checker entry point at the top level. It // writes error messages (if any) into 'env' and annotations // directly into the AST fields declared below. // // Most of the AST classes have their own 'tcheck' or similar // function, though the exact calling convention (params, etc.) // varies. // // Some classes have an 'itcheck' (internal type check) for the // subclasses, meaning the superclass 'tcheck' does some common // processing and then delegates to the per-subclass 'itcheck. public void tcheck(Env &env); } class TopForm { public /*no_ignore*/ TopForm *tcheck(Env &env); pure_virtual void itcheck(Env &env); } class Function { // since 'nameAndParams->var' might refer to a previous declaration, // and therefore might have differently-named parameters, I'll add // another field specifically for a version of the FunctionType // which has the parameters for *this* definition public(xml_TY) FunctionType *funcType = NULL; // since the body of the function (if a class member) may refer // to the receiver object, I need to write it down so client // analyses can find it // // 7/29/04: combined this with the old 'ctorReceiver' public(xml_TY,xmlShouldSerialize) Variable *receiver = NULL; public void printExtras(std::ostream &os, int indent) const; custom debugPrint { printExtras(os, indent); } // if this Function is intended to be a clone of a template body, // but we have not actually cloned the init/body/handlers, then // this is a pointer to the Function to eventually clone, acting // as a thunk; otherwise it is NULL // // 2005-08-07: Don't traverse this! It would make the resulting // structure not a tree, and isn't for external consumption anyway. // ("const" implies "serf" implies non-tree) public Function const *cloneThunkSource = NULL; // return a copy of this Function, but with just a clone thunk public Function *shallowClone() const; custom substituteClone { return shallowClone(); } // if 'cloneThunkSource' is not NULL, clone its body elements // to fill out 'this' object; otherwise, do nothing public void finishClone(); // if 'instV' is not NULL, then force the declarator 'nameAndParams' // to refer to it instead of using the usual lookup/creation // mechanism public void tcheck(Env &env, Variable *instV = NULL); // this is where we end up if 'env.checkFunctionBodies' is true public void tcheckBody(Env &env); private CompoundType *verifyIsCtor(Env &env, char const *context); private void tcheck_memberInits(Env &env); private void tcheck_handlers(Env &env); // this returns true if this is an instantiation of a template // function that has not yet been tcheck'd public bool instButNotTchecked() const; // true if this is a function template (uninstantiated) public bool isTemplate() const; } class MemberInit { // if this is initializing a data member, tcheck will set this public(xml_TY,xmlShouldSerialize) Variable *member = NULL; // if it's a base class ctor call, tcheck will set this instead public(xml_TY) CompoundType *base = NULL; // the constructor used to initialize the member or subobject // (can't call it "ctor".. that's an astgen keyword.. doh), // or NULL for non-CompoundTypes public(xml_TY,xmlShouldSerialize) Variable *ctorVar = NULL; public void printExtras(std::ostream &os, int indent) const; custom debugPrint { printExtras(os, indent); } custom clone { ret->member = member; ret->base = base; ret->ctorVar = ctorVar; } public void tcheck(Env &env, CompoundType *enclosing); } class Declaration { public void tcheck(Env &env, DeclaratorContext context); } class ASTTypeId { // tcheck params for ASTTypeId public struct Tcheck { // when non-NULL, we're in an E_new, and this points to an // Expression* which should be set to the expression which denotes // the number of elements for new[] to allocate Expression **newSizeExpr; // additional declflags to attach to the resulting Variable; // when this includes DF_PARAMETER, it's a function parameter, // which knowledge can be applied towards disambiguation DeclFlags dflags; // syntactic context DeclaratorContext context; public: Tcheck(DeclFlags df, DeclaratorContext dc) : newSizeExpr(NULL), dflags(df), context(dc) {} }; // the return value is the ASTTypeId* to store in place of the // one used to invoke the function, to resolve ambiguities public ASTTypeId *tcheck(Env &env, Tcheck &tc); public void mid_tcheck(Env &env, Tcheck &tc); public Type *getType() const; // can use after calling 'tcheck' } class PQName { // typecheck the template arguments buried in this PQName // // 8/01/04: Go all the way to looking up the scopes denoted by the // qualifiers. 'scope' is the scope denoted by the qualifiers seen // so far, or NULL to mean "current scope", and 'lflags' might be // LF_DECLARATOR if this PQName is the name of a declarator. // // 2005-03-14: Do not call this directly if the name might be // ambiguous; use 'tcheckPQName' (in cc_tcheck.cc) instead. pure_virtual void tcheck_pq(Env &env, Scope *scope = NULL, LookupFlags lflags = LF_NONE); -> PQ_qualifier { // record the result of looking up just the 'qualifier' portion // (without considering template arguments); this is only set // for the *first* PQ_qualifier in a PQName; it is needed for // applyArgumentMap, since it should not be doing lookup public(xml_TY,xmlShouldSerialize) Variable *qualifierVar = NULL; custom debugPrint { ind(os, indent) << "qualifierVar: " << refersTo(qualifierVar) << "\n"; } // digested template arguments public(xml_OL,xmlEmbed_List) ObjList sargs; } -> PQ_template { public(xml_OL,xmlEmbed_List) ObjList sargs; } } class TypeSpecifier { // yield the type named by the specifier; this type may of course // get refined when the declarator is considered public Type *tcheck(Env &env, DeclFlags dflags, LookupFlags lflags); pure_virtual Type *itcheck(Env &env, DeclFlags dflags, LookupFlags lflags); // since several of the type specifiers use names, and names are // scoped, but we don't want later analyses to have to know about // scopes, for those specifiers that use names cc_tcheck writes down // which type it refers to -> TS_name { // typedef Variable this name refers to public(xml_TY,xmlShouldSerialize) Variable *var = NULL; // if the lookup is *not* dependent (and some other conditions // hold...), this will point to 'var' and be preserved when // the AST is cloned public(xml_TY,xmlShouldSerialize) Variable *nondependentVar = NULL; custom clone { ret->nondependentVar = nondependentVar; } } -> TS_elaborated { public(xml_TY) NamedAtomicType *atype = NULL; } -> TS_classSpec { public(xml_TY) CompoundType *ctype = NULL; public void tcheckIntoCompound( Env &env, DeclFlags dflags, CompoundType *ct, bool suppressErrors = false); private void tcheckFunctionBodies(Env &env); public void printExtras(std::ostream &os, int indent) const; custom debugPrint { printExtras(os, indent); } } -> TS_enumSpec { public(xml_TY) EnumType *etype = NULL; } } class BaseClassSpec { public void printExtras(std::ostream &os, int indent) const; custom debugPrint { printExtras(os, indent); } // the type named by the 'name' public(xml_TY) CompoundType *type = NULL; } class Member { pure_virtual void tcheck(Env &env); } class Enumerator { public(xml_TY,xmlShouldSerialize) Variable *var; // (serf) introduction record ctor var=NULL; // when the enumerator values are computed, I store them here // so I can see them in the AST printout; I only print this // value if 'var' is non-NULL (i.e. the enumerator has been // tcheck'd so the value has been determined) public(xml) int enumValue; public void printExtras(std::ostream &os, int indent) const; custom debugPrint { printExtras(os, indent); } // we pass both the base 'enum' and the Type wrapped around it, // since both are needed and it's slightly expensive to compute // one from the other for each enumerator public void tcheck(Env &env, EnumType *parentEnum, Type *parentType); } // The "Declarator" AST node appears in many different contexts in the // AST, and visitor-based analyses often need to do quite different // things depending on which context a declarator appears in. So, // this enumeration lists all the possible contexts. enum DeclaratorContext { DC_UNKNOWN, // dummy value; nothing should have this after tcheck DC_FUNCTION, // Function::nameAndParams // inside Declaration DC_TF_DECL, // TF_decl::decl DC_TF_EXPLICITINST, // TF_explicitInst::d DC_MR_DECL, // MR_decl::d DC_S_DECL, // S_decl::decl DC_TD_DECL, // TD_decl::d DC_FEA, // FullExpressionAnnot::declarations // inside ASTTypeId DC_D_FUNC, // D_func::params DC_EXCEPTIONSPEC, // ExceptionSpec::types DC_ON_CONVERSION, // ON_conversion::type DC_CN_DECL, // CN_decl::typeId DC_HANDLER, // Handler::typeId DC_E_CAST, // E_cast::ctype DC_E_SIZEOFTYPE, // E_sizeofType::atype DC_E_NEW, // E_new::atype (new) DC_E_KEYWORDCAST, // E_keywordCast::ctype DC_E_TYPEIDTYPE, // E_typeidType::ttype DC_TP_TYPE, // TP_type::defaultType DC_TP_NONTYPE, // TP_nontype::param DC_TA_TYPE, // TA_type::type }; class Declarator { // entity declared by this declarator public(xml_TY,xmlShouldSerialize) Variable *var = NULL;// (serf) computed information: name, type, etc. // SGM 2007-09-14: Why do we do this? It's very dangerous; template // instantiation depends on cloning, and cloning the results of // tchecking mean that the tcheck of the uninst template can // contaminate the results of tchecking the instantiation. (They // deliberately communicate to implement two-phase lookup, but that // is not done through 'var'.) As far as I can tell, the tchecker // correctly recomputes 'var' w/o looking at the prior info, but // this is a bug waiting to bite. This was added by dsw in revision // 1203, at 2004-10-28 16:09:55 -0700. custom clone {ret->var = var;} // This field need not be identical to 'var->type'; the 'type' here // comes directly from the declartor syntax, but 'var' might refer // to a previous declaration (e.g. two protoypes of the same // function), and hence 'var->type' might differ in details like // parameter names. Otherwise, nominally, 'var->type' and 'type' // denote equivalent types. public(xml_TY) Type *type = NULL; // syntactic context; sometimes useful to visitors public(xml) DeclaratorContext context = DC_UNKNOWN; custom clone {ret->context = context;} public void printExtras(std::ostream &os, int indent) const; custom debugPrint { printExtras(os, indent); } // this class contains the data passed into and out of the // declarator checking functions public struct Tcheck { // Normally, a declarator creates a Variable and inserts it // into the environment. If this field is non-NULL, then the // declarator simply uses the supplied Variable instead. // Either way, the Variable* is then stored in Declarator::var. Variable *existingVar; // on the way in, this is the type computed so far; initially // it's just the type specifier but additional declarators will // layer additional type constructors on top of it and replace // the pointer here with a pointer to the constructed type; at // the end it is the fully-constructed type Type *type; // these are the declflags attached to the outer declaration DeclFlags dflags; // in a new[] declarator, when we hit the final [size], stash // the size's AST node pointer here; then E_new can collect it Expression *size_E_new; // if this is non-NULL, then it points at the D_func responsible // for creating 'type', a FunctionType; this is used to determine // the cv flags of the implicit 'this' parameter of member functions D_func *funcSyntax; // syntactic context DeclaratorContext context; // Assembler label. StringRef asmname; public: Tcheck(Type *t, DeclFlags d, DeclaratorContext dc) : existingVar (NULL) , type (t) , dflags (d) , size_E_new (NULL) , funcSyntax (NULL) , context (dc) , asmname (NULL) {} Tcheck(Tcheck const &obj) : existingVar (obj.existingVar) , type (obj.type) , dflags (obj.dflags) , size_E_new (obj.size_E_new) , funcSyntax (obj.funcSyntax) , context (obj.context) , asmname (obj.asmname) {} Tcheck& operator= (Tcheck const &obj) { existingVar = obj.existingVar; type = obj.type; dflags = obj.dflags; size_E_new = obj.size_E_new; funcSyntax = obj.funcSyntax; context = obj.context; asmname = obj.asmname; return *this; } bool hasFlag(DeclFlags f) const { return !!(dflags &f); } }; // determine the type denoted by the combination of 'dt.type' and // the type constructors in this declarator, then make a Variable // that has that type; if this declarator refers to something that // is *already* declared (like a function with a prior prototype), // the 'var' field will be shared among the various declarations; if // not, put it into the environment // // the return value specifies which of possibly ambiguous // alternatives was selected public /*no_ignore*/ Declarator *tcheck(Env &env, Tcheck &dt); public void mid_tcheck(Env &env, Tcheck &dt); public void tcheck_init(Env &env); } class IDeclarator { // 8/11/04: There used to be a 'type' field here, but I decided that // analyses probably have no need to know about the types denoted by // IDeclarators, only that denoted by the full Declarator, which is // stored in Declarator::type. // external interface; add the type constructor represented by this // IDeclarator to 'dt.type' pure_virtual void tcheck(Env &env, Declarator::Tcheck &dt); // returns true if there is a D_grouping that does not have // any D_pointer, D_reference or D_ptrToMember inside it public bool hasInnerGrouping() const; -> D_func { // I want to know which D_funcs are members of classes (whether // static or not), but it's not sufficient to just check my // environment context since I get confused by pointers to // functions.. so I set this to true in MR_decl::tcheck and // MR_func::tcheck public(xml) bool isMember = false; } -> D_array { // client analyses find it difficult to know whether this // D_array means "array" or "new[] size", so I'm going to // write down in cc_tcheck which one this is public(xml) bool isNewSize = false; } -> D_bitfield { // type checker stashes the bitfield size in bits here public(xml) int numBits = 0; } } class ExceptionSpec { public FunctionType::ExnSpec *tcheck(Env &env); } class OperatorName { public void tcheck(Env &env); } class Statement { // typecheck, and return which Statement is selected from among // syntactically ambiguous choices public /*no_ignore*/ Statement *tcheck(Env &env); public void mid_tcheck(Env &env, int &); pure_virtual void itcheck(Env &env); -> S_case { // the case label must be an integer; this its value public(xml) int labelVal = 0; } } class Condition { // typecheck, and return which Condition is selected from among // syntactically ambiguous choices public /*no_ignore*/ Condition *tcheck(Env &env); public void mid_tcheck(Env &env, int &) { itcheck(env); }; pure_virtual void itcheck(Env &env); // can use after calling 'tcheck' pure_virtual Type *getType() const; } class Handler { public void tcheck(Env &env); } class Expression { // type check and yield the type of the expression; this type // gets automatically stored in the 'type' field; the return // value specifies which of possibly ambiguous alternatives // was selected for retention public void tcheck(Env &env, Expression *&ptr); public void mid_tcheck(Env &env, Expression *&replacement); // per-type checker; return type this expression eval's to; if // this Expression would like to rewrite itself, then it can put // the new Expression into 'replacement' and the parent AST node // will be updated accordingly; the caller should always put the // receiver object pointer into 'replacement' initially // // the "_x" is for grepping value; think of it as standing for 'eXpression' pure_virtual Type *itcheck_x(Env &env, Expression *&replacement); // type computed for this expression; might be a SimpleType for // ST_ERROR, in which case a subsequent attempt to typecheck the // same expression should stop and look for an ambiguous alt. public(xml_TY) Type *type = NULL; // this is a violation of the cloning invariant, but it gets fixed // with CloneExprTypesVisitor after the AST has been fully cloned custom clone { ret->type = type; } // make the API for getting the type identical to that of FullExpression public Type *getType() { return type; }; // const-eval; will add an error message to the environment if this // expression is not a constant (and also return false); can only // call this after tchecking; result must be an integer public bool constEval(Env &env, int &result) const; // if the function returns true and 'dependent' returns as true, the // expression *is* constant but the precise value is dependent on // template parameters public bool constEval(Env &env, int &result, bool &dependent) const; // more control: all info returned in the return value public CValue constEval(ConstEval &env) const; public CValue iconstEval(ConstEval &env) const; public virtual CValue extConstEval(ConstEval &env) const; // this attempts to evaluate the expression's address public CValue constEvalAddr(ConstEval &env) const; // shared by E_cast and E_keywordCast public CValue constEvalCast(ConstEval &env, ASTTypeId const *ctype, Expression const *expr) const; // return true if this expression has any greater-than operators // that are not buried under an E_grouping; this may modify 'expr' // if it filters out certain ambiguous alternatives public static bool hasUnparenthesizedGT(Expression *&expr); public bool ihasUnparenthesizedGT(); public virtual bool extHasUnparenthesizedGT(); // return what kind of special expression this is, if any public SpecialExpr getSpecial(const ellcc::LangOptions& LO) const; public void printExtras(std::ostream &os, int indent) const; custom debugPrint { printExtras(os, indent); } // interpretations of literals -> E_intLit { public unsigned long long i = 0; } -> E_floatLit { public llvm::APFloat v = 0.0; } -> E_stringLit { // The decoded string contents, including the contents from // continuations, and a final NUL character. If this is a wide // string literal, then the data block contains wchar_ts. The // exact number of characters, including the NUL, and the // character type (and hence width), are available from the // ArrayType attached to this Expression. // // This is NULL only until it has been type checked. public DataBlock *data = NULL; } -> E_charLit { public unsigned int c = 0; } -> E_this { // the receiver parameter to which this 'this' refers; NOTE // that 'receiver' is a *reference* whereas E_this is a // *pointer* (it's like taking the address of 'receiver') public(xml_TY,xmlShouldSerialize) Variable *receiver = NULL; custom clone { ret->receiver = receiver; } } -> E_variable { public(xml_TY,xmlShouldSerialize) Variable *var = NULL; // (serf) binding introduction of this name public Type *itcheck_var(Env &env, Expression *&replacement, LookupFlags flags); custom clone { ret->var = var; } // extended tcheck interface for lookups that yield sets public Type *itcheck_var_set(Env &env, Expression *&replacement, LookupFlags flags, LookupSet &set); // similar to TS_name::nondependentVar public(xml_TY,xmlShouldSerialize) Variable *nondependentVar = NULL; custom clone { ret->nondependentVar = nondependentVar; } } // these two kinds have their tcheck further split into two stages // so I can use a more specialized disambiguation procedure; the // first stage is sufficient to disambiguate between the two -> E_funCall { public void inner1_itcheck(Env &env, LookupSet &candidates); public Type *inner2_itcheck(Env &env, LookupSet &candidates); } -> E_constructor { public void inner1_itcheck(Env &env); public Type *inner2_itcheck(Env &env, Expression *&replacement); // The constructor function being called. If this is NULL, it // means the constructor is "trivial"; for a no-arg ctor this // means a no-op, and for a copy ctor it means member-wise // assignment implementable with a memcpy. (This is a // generalization of the Standard's notion of "trivial"; see // cppstd 12.1p5,6.) public(xml_TY,xmlShouldSerialize) Variable *ctorVar = NULL; custom clone { ret->ctorVar = ctorVar; } } -> E_fieldAcc { public(xml_TY,xmlShouldSerialize) Variable *field = NULL; public Type *itcheck_fieldAcc(Env &env, LookupFlags flags); public Type *itcheck_fieldAcc_set(Env &env, LookupFlags flags, LookupSet &candidates); custom clone { ret->field = field; } } -> E_sizeof { public(xml) int size = -1; // size of the type of expr custom clone { ret->size = size; } } -> E_cast { // idempotency for type definitions in casts public(xml) bool tcheckedType = false; } -> E_sizeofType { public(xml) int size = -1; // size of the type custom clone { ret->size = size; } // This flag is used in some circumstances in C mode to ensure // a given type expression is only tchecked one time, despite // possible ambiguities. (See the tcheck method.) public(xml) bool tchecked = false; } // Q: should I manually clone all the annotation fields? Do I // need them all? Should I make a new mechanism in astgen to do // this automatically? -> E_new { // if this is non-NULL, it's the number of elements to allocate via new[] // // 2005-08-07: Don't traverse this! It is a non-tree pointer. // That is why it says "serf". public(xml_AST) Expression /*nullable serf*/ *arraySize = NULL; // constructor being called to initialize the storage public(xml_TY,xmlShouldSerialize) Variable *ctorVar = NULL; custom debugPrint { ind(os,indent) << "ctorVar: " << refersTo(ctorVar) << "\n"; } custom clone { ret->ctorVar = ctorVar; } } // extended tcheck interface for lookups that yield sets; see // tcheckExpression_set() in cc_tcheck.cc -> E_addrOf { public Type *itcheck_addrOf_set(Env &env, Expression *&replacement, LookupFlags flags, LookupSet &set); } -> E_grouping { public Type *itcheck_grouping_set(Env &env, Expression *&replacement, LookupFlags flags, LookupSet &set); } -> E_arrow { public Type *itcheck_arrow_set(Env &env, Expression *&replacement, LookupFlags flags, LookupSet &set); } // Carry an implicit standard conversion. The reason I do not // re-use E_cast is that E_cast wants to carry a syntactic name for // the destination type, but I don't want to waste space by // constructing it (nor do I want to make E_cast::type nullable). // // 'stdConv' is the overall description of the conversion; it is // never SC_ERROR nor SC_IDENTITY. The exact conversion is // 'expr->type' being converted to 'this->type'. -> E_stdConv(Expression expr, StandardConversion stdConv) { ctor { xassert(stdConv != SC_ERROR && stdConv != SC_IDENTITY); }; } } class FullExpression { public void tcheck(Env &env); // duplicate the API of Expression and delegate all the calls down // to the Expression *expr member public Type *getType() const { return expr->type; }; public bool constEval(Env &env, int &result) const { return expr->constEval(env, result); }; public CValue constEval(ConstEval &env) const { return expr->constEval(env); }; public CValue iconstEval(ConstEval &env) const { return expr->iconstEval(env); }; } class ArgExpression { // repeat some of Expression's interface so I can avoid adding // lots of annoying "expr->expr" clutter public Type *getType() const { return expr->type; }; public bool constEval(Env &env, int &result) const { return expr->constEval(env, result); }; public CValue constEval(ConstEval &env) const { return expr->constEval(env); }; public CValue iconstEval(ConstEval &env) const { return expr->iconstEval(env); }; public bool hasUnparenthesizedGT() { return Expression::hasUnparenthesizedGT(expr); }; public SpecialExpr getSpecial(const ellcc::LangOptions& LO) const { return expr->getSpecial(LO); }; public ArgExpression *tcheck(Env &env, ArgumentInfo &); public void mid_tcheck(Env &env, ArgumentInfo &); } class Initializer { // check that the initializer is well-typed, given the type of // the thing it initializes pure_virtual void tcheck(Env &env, Type *type); -> IN_ctor() { // constructor function being called public(xml_TY,xmlShouldSerialize) Variable *ctorVar = NULL; custom debugPrint { ind(os,indent) << "ctorVar: " << refersTo(ctorVar) << "\n"; } // true if this was originally an IN_expr that the type // checker rewrote as IN_ctor; needed to implement 8.5p14 public(xml,field) bool was_IN_expr = false; custom clone { ret->ctorVar = ctorVar; ret->was_IN_expr = was_IN_expr; } } } class TemplateDeclaration { public void tcheck(Env &env); pure_virtual void itcheck(Env &env); // oy, I wanted 'preemptTraverse' to affect all TemplateDeclarations // at once, but that's a little difficult, so I have to copy it.. -> TD_func { custom preemptTraverse { // we are now in a template if we already were, or if this // declaration is not a complete specialization Restorer r(vis.inTemplate, vis.inTemplate || params!=NULL); } } -> TD_decl { custom preemptTraverse { Restorer r(vis.inTemplate, vis.inTemplate || params!=NULL); } } -> TD_tmember { custom preemptTraverse { Restorer r(vis.inTemplate, vis.inTemplate || params!=NULL); } } } class TemplateParameter { public /*no_ignore*/ TemplateParameter *tcheck(Env &env); public void mid_tcheck(Env &env, int &dummy); pure_virtual void itcheck(Env &env, int &dummy); // parameter Variable public(xml_TY,xmlShouldSerialize) Variable *var = NULL; } class TemplateArgument { public /*no_ignore*/ TemplateArgument *tcheck(Env &env, STemplateArgument &sarg); public void mid_tcheck(Env &env, STemplateArgument &sarg); pure_virtual void itcheck(Env &env, STemplateArgument &sarg); } class NamespaceDecl { pure_virtual void tcheck(Env &env); } // EOF