// cc_type.h see license.txt for copyright and terms of use // compile-type representation of C++ types // see doc/cc_type.html // and Diagram 1 of doc/cpp_er.html // The original design intent was that type representation would be // completely independent of the Expression language, or anything // else from cc.ast. That is, types should have meaning independent // of the language of values, or of the syntax used to describe them // in source code. // // However, the practicalities of parsing C++ have forced my hand in a // couple of places. Those places are marked with the annotation // "(AST pointer)". In all cases so far, I can forsee an intermediate // transformation which would remove (e.g. nullify) all the AST // pointers in the process of creating a "minimized" C++ subset. In // particular, it should be possible to translate away everything // related to templates. // // *Please* do not add any additional AST pointers unless it is // really necessary. Also please make sure all the types can // intelligbly print themselves *without* inspecting the AST pointers // (by storing a textual representation if necessary). #ifndef CC_TYPE_H #define CC_TYPE_H #include "str.h" // string #include "objlist.h" // ObjList #include "sobjlist.h" // SObjList #include "astlist.h" // ASTList #include "cc_flags.h" // CVFlags, DeclFlags, SimpleTypeId #include "strtable.h" // StringRef #include "strobjdict.h" // StringObjDict #include "cc_scope.h" // Scope #include "srcloc.h" // SourceLoc #include "exc.h" // xBase #include "serialno.h" // INHERIT_SERIAL_BASE #include "mflags.h" // MatchFlags class Variable; // variable.h class Env; // cc_env.h class TS_classSpec; // cc.ast class Expression; // cc.ast class TemplateArgument; // cc.ast class D_pointer; // cc.ast class D_reference; // cc.ast class D_func; // cc.ast class D_ptrToMember; // cc.ast class TypeSpecifier; // cc.ast class Declaration; // cc.ast class TypeVariable; // template.h class PseudoInstantiation;// template.h class DependentQType; // template.h class ReadXML; // xml.h class MType; // mtype.h // fwd in this file class AtomicType; class SimpleType; class NamedAtomicType; class CompoundType; class BaseClass; class EnumType; class CVAtomicType; class PointerType; class ReferenceType; class FunctionType; class ArrayType; class PointerToMemberType; class Type; class TemplateInfo; class STemplateArgument; class TypeFactory; class BasicTypeFactory; class TypePred; // FIX: is a debugging aid; remove extern bool global_mayUseTypeAndVarToCString; // --------------------- type visitor ----------------------- // Visitor that digs down into template arguments, among other things. // Like the AST visitors, the 'visit' functions return true to // continue digging into subtrees, or false to prune the search. class TypeVisitor { public: virtual ~TypeVisitor() {} // silence warning virtual bool visitType(Type *obj); virtual void postvisitType(Type *obj); virtual bool visitFunctionType_params(SObjList ¶ms); virtual void postvisitFunctionType_params(SObjList ¶ms); virtual bool visitFunctionType_params_item(Variable *param); virtual void postvisitFunctionType_params_item(Variable *param); virtual bool visitVariable(Variable *var); virtual void postvisitVariable(Variable *var); virtual bool visitAtomicType(AtomicType *obj); virtual void postvisitAtomicType(AtomicType *obj); // I don't know where to say this, so I'll say it here: there is no // call in EnumType::traverse() that will take you here; visitors // call it manually. However, the existance of these methods allows // you to organize your code in the same way as you would for the // other classes. // // Also, please note that I cannot forward declare EnumType::Value, // so unless I move this class until after class Enum, I have to // make the argument type void! virtual bool visitEnumType_Value(void /*EnumType::Value*/ *obj); virtual void postvisitEnumType_Value(void /*EnumType::Value*/ *obj); virtual bool visitScope(Scope *obj); virtual void postvisitScope(Scope *obj); virtual bool visitScope_variables(StringRefMap &variables); virtual void postvisitScope_variables(StringRefMap &variables); virtual bool visitScope_variables_entry(StringRef name, Variable *var); virtual void postvisitScope_variables_entry(StringRef name, Variable *var); virtual bool visitScope_typeTags(StringRefMap &typeTags); virtual void postvisitScope_typeTags(StringRefMap &typeTags); virtual bool visitScope_typeTags_entry(StringRef name, Variable *var); virtual void postvisitScope_typeTags_entry(StringRef name, Variable *var); virtual bool visitScope_templateParams(SObjList &templateParams); virtual void postvisitScope_templateParams(SObjList &templateParams); virtual bool visitScope_templateParams_item(Variable *var); virtual void postvisitScope_templateParams_item(Variable *var); virtual bool visitBaseClass(BaseClass *bc); virtual void postvisitBaseClass(BaseClass *bc); virtual bool visitBaseClassSubobj(BaseClassSubobj *bc); virtual void postvisitBaseClassSubobj(BaseClassSubobj *bc); virtual bool visitBaseClassSubobj_parents(SObjList &parents); virtual void postvisitBaseClassSubobj_parents(SObjList &parents); virtual bool visitBaseClassSubobj_parents_item(BaseClassSubobj *parent); virtual void postvisitBaseClassSubobj_parents_item(BaseClassSubobj *parent); virtual bool visitSTemplateArgument(STemplateArgument *obj); virtual void postvisitSTemplateArgument(STemplateArgument *obj); virtual bool visitPseudoInstantiation_args(ObjList &args); virtual void postvisitPseudoInstantiation_args(ObjList &args); virtual bool visitPseudoInstantiation_args_item(STemplateArgument *arg); virtual void postvisitPseudoInstantiation_args_item(STemplateArgument *arg); virtual bool visitDependentQTypePQTArgsList(ObjList &list); virtual void postvisitDependentQTypePQTArgsList(ObjList &list); virtual bool visitDependentQTypePQTArgsList_item(STemplateArgument *sta); virtual void postvisitDependentQTypePQTArgsList_item(STemplateArgument *sta); // note that this call is always a leaf in the traversal; the type // visitor does *not* dig into Expressions (though of course you can // write a 'visitExpression' method that does) virtual bool visitExpression(Expression *obj); virtual void postvisitExpression(Expression *obj); }; // --------------------- atomic types -------------------------- // interface to types that are atomic in the sense that no // modifiers can be stripped away; see cc_type.html class AtomicType { public: // types enum Tag { // these are defined in cc_type.h T_SIMPLE, // int, char, float, etc. T_COMPOUND, // struct/class/union T_ENUM, // enum // these are defined in template.h T_TYPEVAR, // T T_PSEUDOINSTANTIATION, // C T_DEPENDENTQTYPE, // C::some_type NUM_TAGS }; public: // funcs AtomicType(); virtual ~AtomicType(); virtual Tag getTag() const = 0; bool isSimpleType() const { return getTag() == T_SIMPLE; } bool isCompoundType() const { return getTag() == T_COMPOUND; } bool isEnumType() const { return getTag() == T_ENUM; } bool isTypeVariable() const { return getTag() == T_TYPEVAR; } bool isPseudoInstantiation() const { return getTag() == T_PSEUDOINSTANTIATION; } bool isDependentQType() const { return getTag() == T_DEPENDENTQTYPE; } virtual bool isNamedAtomicType() const; // default impl returns false // see smbase/macros.h DOWNCAST_FN(SimpleType) DOWNCAST_FN(NamedAtomicType) DOWNCAST_FN(CompoundType) DOWNCAST_FN(EnumType) DOWNCAST_FN(TypeVariable) DOWNCAST_FN(PseudoInstantiation) DOWNCAST_FN(DependentQType) // concrete types do not have holes bool isConcrete() const { return !isTypeVariable() && !isPseudoInstantiation(); } // this is type equality, *not* coercibility -- e.g. if // we say "extern type1 x" and then "extern type2 x" we // will allow it only if type1==type2 bool equals(AtomicType const *obj) const; // print the string according to 'Type::printAsML' string toString() const; // print in C notation virtual string toCString() const = 0; // print in "ML" notation (really just more verbose) virtual string toMLString() const = 0; // size this type's representation occupies in memory; this // might throw XReprSize, see below virtual int reprSize() const = 0; // invoke 'vis.visitAtomicType(this)', and then traverse subtrees virtual void traverse(TypeVisitor &vis) = 0; // toString()+newline to cout void gdb() const; ALLOC_STATS_DECLARE }; // represents one of C's built-in types; // there are exactly as many of these objects as there are built-in types class SimpleType : public AtomicType { public: // data SimpleTypeId const type; // global read-only array for each built-in type // (read-only is enforced by making all of SimpleType's members // const, rather then the objects themselves, because doing the // latter would clash AtomicType pointers not being const below) static SimpleType fixed[NUM_SIMPLE_TYPES]; public: // funcs SimpleType(SimpleTypeId t) : type(t) {} // AtomicType interface virtual Tag getTag() const { return T_SIMPLE; } virtual string toCString() const; virtual string toMLString() const; virtual int reprSize() const; virtual void traverse(TypeVisitor &vis); }; // elements common to structs and enums class NamedAtomicType : public AtomicType { public: // data StringRef name; // (nullable) user-assigned name of this struct or enum Variable *typedefVar; // (owner) implicit typedef variable AccessKeyword access; // accessibility of this type in its declaration context public: NamedAtomicType(StringRef name); ~NamedAtomicType(); virtual bool isNamedAtomicType() const; // returns true }; // represent a base class class BaseClass { public: CompoundType *ct; // (serf) the base class itself AccessKeyword access; // access mode of the inheritance bool isVirtual; // true for virtual inheritance public: BaseClass(BaseClass const &obj) : DMEMB(ct), DMEMB(access), DMEMB(isVirtual) {} BaseClass(CompoundType *c, AccessKeyword a, bool v) : ct(c), access(a), isVirtual(v) {} // this isn't virtual because the caller always knows the // dynamic type void traverse(TypeVisitor &vis); }; // represent an instance of a base class in a particular class' // inheritance hierarchy; it is these things that represent things // like diamond inheritance patterns; the terminology comes from // [cppstd 10.1 para 4] class BaseClassSubobj : public BaseClass { public: // classes that this one inherits from; non-owning since the // 'parents' links form a DAG, not a tree; in fact I regard // this list as an owner of exactly those sub-objects whose // inheritance is *not* virtual SObjList parents; // for visit-once-only traversals mutable bool visited; public: BaseClassSubobj(CompoundType *c, AccessKeyword a, bool v) : BaseClass(c, a, v) {} BaseClassSubobj(BaseClass const &base); ~BaseClassSubobj(); // dsw: note: we use the implicit operator=() on this object // name and virtual address to uniquely identify this object string canonName() const; void traverse(TypeVisitor &vis); }; // represent a user-defined compound type; the members of the // compound are whatever has been entered in the Scope class CompoundType : public NamedAtomicType, public Scope { public: // types // NOTE: keep these consistent with TypeIntr (in file cc_flags.h) enum Keyword { K_STRUCT, K_CLASS, K_UNION, NUM_KEYWORDS }; public: // data bool forward; // true when it's only fwd-declared Keyword keyword; // keyword used to introduce the type // nonstatic data members, in the order they appear within the body // of the class definition; note that this is partially redundant // with the Scope's 'variables' map, and hence should not be changed // without also updating that map SObjList dataMembers; // classes from which this one inherits; 'const' so you have to // use 'addBaseClass', but public to make traversal easy const ObjList bases; // collected virtual base class subobjects ObjList virtualBases; // this is the root of the subobject hierarchy diagram // invariant: subobj.ct == this BaseClassSubobj subobj; // list of all conversion operators this class has, including // those that have been inherited but not then hidden SObjList conversionOperators; // list of friends; the friends are members of other scopes, but // they can access protected/private members of this class, and // argument-dependent lookups in this class can find them // // this is almost certainly *not* the best way to record this // information if access control were to be implemented, but it // does the job for the moment SObjList friends; // if this class is a template instantiation, 'instName' is // the class name plus a rendering of the arguments; otherwise // it's the same as 'name'; this is for debugging StringRef instName; // AST node that describes this class; used for implementing // templates (AST pointer) // dsw: used for other purposes also TS_classSpec *syntax; // (nullable serf) // template parameter scope that is consulted for lookups after // this scope and its base classes; this changes over time as // the class is added to and removed from the scope stack; it // is NULL whenever it is not on the scope stack Scope *parameterizingScope; // (nullable serf) // this is the type of the compound as bound to the // injected-class-name; it is different than typedefVar->type // for template classes, as the former is the template class // itself while the latter is a PseudoInstantiation thereof Type *selfType; // (nullable serf) private: // funcs void makeSubobjHierarchy(BaseClassSubobj *subobj, BaseClass const *newBase); BaseClassSubobj const *findVirtualSubobjectC(CompoundType const *ct) const; BaseClassSubobj *findVirtualSubobject(CompoundType *ct) { return const_cast(findVirtualSubobjectC(ct)); } static void clearVisited_helper(BaseClassSubobj const *subobj); static void getSubobjects_helper (SObjList &dest, BaseClassSubobj const *subobj); void addLocalConversionOp(Variable *op); protected: // funcs // create an incomplete (forward-declared) compound // experiment: force creation of these to go through the factory too CompoundType(Keyword keyword, StringRef name); friend class TypeFactory; friend class TypeXmlReader; // override no-op implementation in Scope virtual void afterAddVariable(Variable *v); public: // funcs virtual ~CompoundType(); bool isComplete() const { return !forward; } // true if this is a class that is incomplete because it requires // template arguments to be supplied (i.e. not true for classes // that come from templates, but all arguments have been supplied) bool isTemplate(bool considerInherited = false) const; // true if it is an instance (fully concrete type arguments) of some // template bool isInstantiation() const; // manipulate 'templateInfo', which is now stored in the 'typedefVar' TemplateInfo *templateInfo() const; void setTemplateInfo(TemplateInfo *templInfo0); // same as 'typedefVar' but with a bunch of assertions... Variable *getTypedefVar() const; // true if the class has RTTI/vtable bool hasVirtualFns() const; static char const *keywordName(Keyword k); // AtomicType interface virtual Tag getTag() const { return T_COMPOUND; } virtual string toCString() const; virtual string toMLString() const; virtual int reprSize() const; virtual void traverse(TypeVisitor &vis); string toStringWithFields() const; string keywordAndName() const { return toCString(); } int numFields() const; // returns NULL if doesn't exist Variable *getNamedField(StringRef name, Env &env, LookupFlags f=LF_NONE) { return lookupVariable(name, env, f); } // alias for Scope::addVariable (should probably be deleted) void addField(Variable *v); // sm: for compatibility with existing code SObjList &getDataVariablesInOrder() { return dataMembers; } // return the ordinal position of the named nonstatic data field // in this class, starting with 0; or, return -1 if the field does // not exist (this is a query on 'dataMembers') int getDataMemberPosition(StringRef name) const; // add to 'bases'; incrementally maintains 'virtualBases' virtual void addBaseClass(BaseClass * /*owner*/ newBase); // true if this class inherits from 'ct', either directly or // indirectly, and the inheritance is virtual bool hasVirtualBase(CompoundType const *ct) const { return !!findVirtualSubobjectC(ct); } // set all the 'visited' fields to false in the subobject hierarchy void clearSubobjVisited() const; // collect all of the subobjects into a list; each subobject // appears exactly once; NOTE: you may want to call // Env::ensureClassBodyInstantiated before calling this, since // an uninstantiated class won't have any subobjects yet void getSubobjects(SObjList &dest) const; // render the subobject hierarchy to a 'dot' graph string renderSubobjHierarchy() const; // how many times does 'ct' appear as a subobject? // returns 1 if ct==this int countBaseClassSubobjects(CompoundType const *ct) const; bool hasUnambiguousBaseClass(CompoundType const *ct) const { return countBaseClassSubobjects(ct)==1; } bool hasBaseClass(CompoundType const *ct) const { return countBaseClassSubobjects(ct)>=1; } bool hasStrictBaseClass(CompoundType const *ct) const { return this != ct && hasBaseClass(ct); } // compute the least upper bound of two compounds in the inheritance // network; 'wasAmbig==true' means that the intersection of the // superclasses was not empty, but that set had no least element; // return NULL if no LUB ("least" means most-derived) static CompoundType *lub(CompoundType *t1, CompoundType *t2, bool &wasAmbig); // call this when we're finished adding base classes and member // fields; it builds 'conversionOperators'; 'specialName' is the // name under which the conversion operators have been filed in // the class scope virtual void finishedClassDefinition(StringRef specialName); // true if this is an "aggregate" (8.5.1p1) bool isAggregate() const; }; string toString(CompoundType::Keyword k); // represent an enumerated type class EnumType : public NamedAtomicType { public: // types // represent a single value in an enum class Value { public: StringRef name; // the thing whose name is being defined EnumType *type; // enum in which it was declared int value; // value it's assigned to // similar to fields, I keep a record of where this came from Variable *decl; // (nullable serf) public: Value(StringRef n, EnumType *t, int v, Variable *d); ~Value(); }; public: // data StringObjDict valueIndex; // values in this enumeration int nextValue; // next value to assign to elements automatically public: // funcs EnumType(StringRef n) : NamedAtomicType(n), nextValue(0) {} ~EnumType(); // AtomicType interface virtual Tag getTag() const { return T_ENUM; } virtual string toCString() const; virtual string toMLString() const; virtual int reprSize() const; virtual void traverse(TypeVisitor &vis); Value *addValue(StringRef name, int value, /*nullable*/ Variable *d); Value const *getValue(StringRef name) const; }; // moved PseudoInstantiation into template.h // ---------------------- TypePred ---------------------------- // This visitor predates TypeVisitor, and is a little different // because it does not dig into atomic types or template args. // It would be good to merge them at some point. // abstract superclass class TypePred { public: virtual bool operator() (Type const *t) = 0; virtual ~TypePred() {} // gcc sux }; // when you just want a stateless predicate typedef bool (*TypePredFunc)(Type const *t); class StatelessTypePred : public TypePred { TypePredFunc const f; public: StatelessTypePred(TypePredFunc f0) : f(f0) {} virtual bool operator() (Type const *t); }; // ------------------- constructed types ------------------------- // generic constructed type; to allow client analyses to annotate the // description of types, this class is inherited by "Type", the class // that all of the rest of the parser regards as being a "type" // // note: clients should refer to Type, not BaseType class BaseType INHERIT_SERIAL_BASE { public: // types enum Tag { // dsw: I want these to not be picked arbitrarily by the compiler, // so I start it at something specific; I like 0 to be meaningless // so that nothing meaningfull happens by default, a la pointers. T_ATOMIC = 1, // int const, class Foo, etc. T_POINTER, // int * T_REFERENCE, // int & T_FUNCTION, // int ()(int, float) T_ARRAY, // int [3] T_POINTERTOMEMBER, // int C::* T_LAST_TYPE_TAG, // IMPORTANT: it is important that T_LAST_TYPE_TAG be the last one // so I can make a disjoint set in Oink by starting my enum at // T_LAST_TYPE_TAG+1. Therefore if you add anything, add it // before T_LAST_TYPE_TAG. }; public: // data // when true (the default is false), types are printed in ML-like // notation instead of C notation by AtomicType::toString and // Type::toString static bool printAsML; private: // disallowed BaseType(BaseType&); private: // funcs // the constructor of BaseType is private so the only subclass // is Type, an assumption relied upon in BaseType::equals BaseType(); friend class Type; public: // funcs virtual ~BaseType(); virtual Tag getTag() const = 0; bool isCVAtomicType() const { return getTag() == T_ATOMIC; } bool isPointerType() const { return getTag() == T_POINTER; } bool isReferenceType() const { return getTag() == T_REFERENCE; } bool isFunctionType() const { return getTag() == T_FUNCTION; } bool isArrayType() const { return getTag() == T_ARRAY; } bool isPointerToMemberType() const { return getTag() == T_POINTERTOMEMBER; } DOWNCAST_FN(CVAtomicType) DOWNCAST_FN(PointerType) DOWNCAST_FN(ReferenceType) DOWNCAST_FN(FunctionType) DOWNCAST_FN(ArrayType) DOWNCAST_FN(PointerToMemberType) // like above, this is (structural) equality, not coercibility; // internally, this calls the innerEquals() method on the two // objects, once their tags have been established to be equal bool equals(BaseType const *obj, MatchFlags flags = MF_NONE) const; // compute a hash value: equal types (EF_EXACT) have the same hash // value, and unequal types are likely to have different values unsigned hashValue() const; virtual unsigned innerHashValue() const = 0; // print the string according to 'printAsML' string toString() const; // print the type, with an optional name like it was a declaration // for a variable of that type string toCString() const; string toCString(char const * /*nullable*/ name) const; string toCString(rostring name) const { return toCString(name.c_str()); } // NOTE: yes, toMLString() is virtual, whereas toCString() is not virtual string toMLString() const = 0; void putSerialNo(stringBuilder &sb) const; // toString+newline to cout void gdb() const; // the left/right business is to allow us to print function // and array types in C's syntax; if 'innerParen' is true then // the topmost type constructor should print the inner set of // paretheses virtual string leftString(bool innerParen=true) const = 0; virtual string rightString(bool innerParen=true) const; // default: returns "" // size of representation at run-time; for now uses nominal 32-bit // values virtual int reprSize() const = 0; // filter on all constructed types that appear in the type, // *including* parameter types; return true if any constructor // satisfies 'pred' (note that recursive types always go through // CompoundType, and this does not dig into the fields of // CompoundTypes) virtual bool anyCtorSatisfies(TypePred &pred) const=0; // automatically wrap 'pred' in a StatelessTypePred; // trailing 'F' in name means "function", so as to avoid // overloading and overriding on the same name bool anyCtorSatisfiesF(TypePredFunc f) const; // return the cv flags that apply to this type, if any; // default returns CV_NONE virtual CVFlags getCVFlags() const; bool isConst() const { return !!(getCVFlags() & CV_CONST); } // invoke 'vis.visitType(this)', and then traverse subtrees virtual void traverse(TypeVisitor &vis) = 0; // some common queries bool isSimpleType() const; SimpleType const *asSimpleTypeC() const; bool isSimple(SimpleTypeId id) const; bool isSimpleCharType() const { return isSimple(ST_CHAR); } bool isSimpleWChar_tType() const { return isSimple(ST_WCHAR_T); } bool isStringType() const; bool isIntegerType() const; // any of the simple integer types bool isEnumType() const; bool isUnionType() const { return isCompoundTypeOf(CompoundType::K_UNION); } bool isStructType() const { return isCompoundTypeOf(CompoundType::K_STRUCT); } bool isCompoundTypeOf(CompoundType::Keyword keyword) const; bool isVoid() const { return isSimple(ST_VOID); } bool isBool() const { return isSimple(ST_BOOL); } bool isEllipsis() const { return isSimple(ST_ELLIPSIS); } bool isError() const { return isSimple(ST_ERROR); } bool isDependent() const; // TODO: this should be ST_DEPENDENT only bool isOwnerPtr() const; bool isMethod() const; // function and method // ST_DEPENDENT or TypeVariable or PseudoInstantiation or DependentQType bool isGeneralizedDependent() const; bool containsGeneralizedDependent() const; // anywhere in Type tree // (some of the following are redundant but I want them anyway, to // continue with the pattern that isXXX is for language concepts and // isXXXType is for implementation concepts) // pointer/reference stuff bool isPointer() const { return isPointerType(); } bool isReference() const { return isReferenceType(); } bool isReferenceToConst() const; bool isLval() const { return isReference(); }// C terminology // allow some degree of unified handling of PointerType and ReferenceType bool isPtrOrRef() const { return isPointer() || isReference(); } bool isPointerOrArrayRValueType() const; // dsw: this is virtual because in Oink an int can act as a pointer // so I need a way to do that virtual Type *getAtType() const; // note that Type overrides these to return Type instead of BaseType BaseType const *asRvalC() const; // if I am a reference, return referrent type BaseType *asRval() { return const_cast(asRvalC()); } bool isCVAtomicType(AtomicType::Tag tag) const; bool isTypeVariable() const { return isCVAtomicType(AtomicType::T_TYPEVAR); } TypeVariable const *asTypeVariableC() const; TypeVariable *asTypeVariable() { return const_cast(asTypeVariableC()); } // downcast etc. to NamedAtomicType bool isNamedAtomicType() const; NamedAtomicType *ifNamedAtomicType(); // NULL or corresp. NamedAtomicType NamedAtomicType const *asNamedAtomicTypeC() const; NamedAtomicType *asNamedAtomicType() { return const_cast(asNamedAtomicTypeC()); } // similar for CompoundType bool isCompoundType() const { return isCVAtomicType(AtomicType::T_COMPOUND); } CompoundType *ifCompoundType(); // NULL or corresp. compound CompoundType const *asCompoundTypeC() const; // fail assertion if not CompoundType *asCompoundType() { return const_cast(asCompoundTypeC()); } // etc. ... bool isPseudoInstantiation() const { return isCVAtomicType(AtomicType::T_PSEUDOINSTANTIATION); } PseudoInstantiation const *asPseudoInstantiationC() const; PseudoInstantiation *asPseudoInstantiation() { return const_cast(asPseudoInstantiationC()); } bool isDependentQType() const { return isCVAtomicType(AtomicType::T_DEPENDENTQTYPE); } // something that behaves like a CompoundType in most respects bool isLikeCompoundType() const { return isCompoundType() || isPseudoInstantiation(); } // this is true if any of the type *constructors* on this type // refer to ST_ERROR; we don't dig down inside e.g. members of // referred-to classes bool containsErrors() const; // similar for TypeVariable bool containsTypeVariables() const; // returns true if contains type or object variables (template // parameters); recurses down into TemplateArguments // // if 'map' is not NULL, then we only count variables that // are *not* bound in 'map' bool containsVariables(MType *map = NULL) const; ALLOC_STATS_DECLARE }; string cvToString(CVFlags cv); #ifdef TYPE_CLASS_FILE // pull in the definition of Type, which may have additional // fields (etc.) added for the client analysis #include TYPE_CLASS_FILE #else // please see cc_type.html, section 6, "BaseType and Type", for more // information about this class class Type : public BaseType { protected: // funcs Type() {} public: // funcs // do not leak the name "BaseType" Type const *asRvalC() const { return static_cast(BaseType::asRvalC()); } Type *asRval() { return static_cast(BaseType::asRval()); } }; #endif // TYPE_CLASS_FILE // supports the use of 'Type*' in AST constructor argument lists string toString(Type *t); // essentially just a wrapper around an atomic type, but // also with optional const/volatile flags class CVAtomicType : public Type { public: // data AtomicType *atomic; // (serf) underlying type CVFlags cv; // const/volatile protected: friend class BasicTypeFactory; friend class TypeXmlReader; CVAtomicType(AtomicType *a, CVFlags c) : atomic(a), cv(c) {} // need this to make a static array of them CVAtomicType(CVAtomicType const &obj) : atomic(obj.atomic), cv(obj.cv) {} public: bool isConst() const { return !!(cv & CV_CONST); } bool isVolatile() const { return !!(cv & CV_VOLATILE); } // Type interface virtual Tag getTag() const { return T_ATOMIC; } unsigned innerHashValue() const; virtual string toMLString() const; virtual string leftString(bool innerParen=true) const; virtual int reprSize() const; virtual bool anyCtorSatisfies(TypePred &pred) const; virtual CVFlags getCVFlags() const; virtual void traverse(TypeVisitor &vis); }; // type of a pointer class PointerType : public Type { public: // data CVFlags cv; // const/volatile; refers to pointer *itself* Type *atType; // (serf) type of thing pointed-at protected: // funcs friend class BasicTypeFactory; friend class TypeXmlReader; PointerType(CVFlags c, Type *a); public: bool isConst() const { return !!(cv & CV_CONST); } bool isVolatile() const { return !!(cv & CV_VOLATILE); } // Type interface virtual Tag getTag() const { return T_POINTER; } unsigned innerHashValue() const; virtual string toMLString() const; virtual string leftString(bool innerParen=true) const; virtual string rightString(bool innerParen=true) const; virtual int reprSize() const; virtual bool anyCtorSatisfies(TypePred &pred) const; virtual CVFlags getCVFlags() const; virtual void traverse(TypeVisitor &vis); }; // type of a reference class ReferenceType : public Type { public: // data Type *atType; // (serf) type of thing pointed-at protected: // funcs friend class BasicTypeFactory; friend class TypeXmlReader; ReferenceType(Type *a); public: bool isConst() const { return false; } bool isVolatile() const { return false; } // Type interface virtual Tag getTag() const { return T_REFERENCE; } unsigned innerHashValue() const; virtual string toMLString() const; virtual string leftString(bool innerParen=true) const; virtual string rightString(bool innerParen=true) const; virtual int reprSize() const; virtual bool anyCtorSatisfies(TypePred &pred) const; virtual CVFlags getCVFlags() const; virtual void traverse(TypeVisitor &vis); }; // some flags that can be associated with function types enum FunctionFlags { FF_NONE = 0x0000, // nothing special FF_METHOD = 0x0001, // function is a nonstatic method FF_VARARGS = 0x0002, // accepts variable # of arguments FF_CONVERSION = 0x0004, // conversion operator function FF_CTOR = 0x0008, // constructor FF_DTOR = 0x0010, // destructor FF_BUILTINOP = 0x0020, // built-in operator function (cppstd 13.6) FF_NO_PARAM_INFO = 0x0040, // C parameter list "()" (C99 6.7.5.3 para 14) FF_DEFAULT_ALLOC = 0x0080, // is a default [de]alloc function from 3.7.3p2 FF_KANDR_DEFN = 0x0100, // derived from a K&R-style function definition FF_ALL = 0x01FF, // all flags set to 1 }; ENUM_BITWISE_OPS(FunctionFlags, FF_ALL); // type of a function class FunctionType : public Type { public: // types // list of exception types that can be thrown class ExnSpec { public: SObjList types; public: ExnSpec() {} ExnSpec(ExnSpec const &obj); ~ExnSpec(); bool anyCtorSatisfies(TypePred &pred) const; }; public: // data // various boolean properties FunctionFlags flags; // type of return value Type *retType; // (serf) // list of function parameters; if (flags & FF_METHOD) then the // first parameter is '__receiver' SObjList params; // allowable exceptions, if not NULL ExnSpec *exnSpec; // (nullable owner) protected: // funcs friend class BasicTypeFactory; friend class TypeXmlReader; FunctionType(Type *retType); public: virtual ~FunctionType(); // interpretations of flags bool hasFlag(FunctionFlags f) const { return !!(flags & f); } bool isMethod() const { return hasFlag(FF_METHOD); } bool acceptsVarargs() const { return hasFlag(FF_VARARGS); } bool isConversionOperator() const { return hasFlag(FF_CONVERSION); } bool isConstructor() const { return hasFlag(FF_CTOR); } bool isDestructor() const { return hasFlag(FF_DTOR); } void setFlag(FunctionFlags f) { flags |= f; } void clearFlag(FunctionFlags f) { flags &= ~f; } // if the '__receiver' parameter (if any) is ignored in both // function types, am I equal to 'obj'? bool equalOmittingReceiver(FunctionType const *obj) const { return equals(obj, MF_STAT_EQ_NONSTAT | MF_IGNORE_IMPLICIT); } // true if all parameters after 'startParam' (0 is first) have // default values bool paramsHaveDefaultsPast(int startParam) const; // append a parameter to the (ordinary) parameters list void addParam(Variable *param); // add the implicit '__receiver' param; sets 'isMember()' to true void addReceiver(Variable *param); // Note: After calling 'addParam', etc., the client must call // TypeFactory::doneParams so that the factory has a chance to // do any final adjustments. Variable const *getReceiverC() const; // 'isMember' must be true Variable *getReceiver() { return const_cast(getReceiverC()); } CVFlags getReceiverCV() const; // dig down; or CV_NONE if !isMember CompoundType *getClassOfMember(); // 'isMember' must be true // the above only works if the function is a member of a concrete // class; if it's a member of a template class, this must be used NamedAtomicType *getNATOfMember(); // more specialized printing, for Cqual++ syntax static string rightStringQualifiers(CVFlags cv); virtual string rightStringUpToQualifiers(bool innerParen) const; virtual string rightStringAfterQualifiers() const; // print the function type, but use these cv-flags as the // receiver param cv-flags string toString_withCV(CVFlags cv) const; // a hook for the verifier's printer virtual void extraRightmostSyntax(stringBuilder &sb) const; // Type interface virtual Tag getTag() const { return T_FUNCTION; } unsigned innerHashValue() const; virtual string toMLString() const; virtual string leftString(bool innerParen=true) const; virtual string rightString(bool innerParen=true) const; virtual int reprSize() const; virtual bool anyCtorSatisfies(TypePred &pred) const; virtual void traverse(TypeVisitor &vis); }; // type of an array class ArrayType : public Type { public: // types enum { NO_SIZE = -1, // no size specified DYN_SIZE = -2 // GNU extension: size is not a constant }; public: // data Type *eltType; // (serf) type of the elements int size; // specified size (>=0), or NO_SIZE or DYN_SIZE // Note that whether a size of 0 is legal depends on the current // language settings (cc_lang.h), so most code should adapt itself // to that possibility. private: // funcs void checkWellFormedness() const; protected: friend class BasicTypeFactory; friend class TypeXmlReader; ArrayType(Type *e, int s = NO_SIZE) : eltType(e), size(s) { checkWellFormedness(); } ArrayType(ReadXML&) // a ctor for de-serialization : eltType(NULL), size(NO_SIZE) {} public: int getSize() const { return size; } bool hasSize() const { return size >= 0; } // Type interface virtual Tag getTag() const { return T_ARRAY; } unsigned innerHashValue() const; virtual string toMLString() const; virtual string leftString(bool innerParen=true) const; virtual string rightString(bool innerParen=true) const; virtual int reprSize() const; virtual bool anyCtorSatisfies(TypePred &pred) const; virtual void traverse(TypeVisitor &vis); }; // pointer to member class PointerToMemberType : public Type { public: // Usually, this is a compound type, as ptr-to-members are // w.r.t. some compound. However, to support the 'compound' // being a template parameter, we generalize slightly so that // a TypeVariable can be the 'inClass'. NamedAtomicType *inClassNAT; CVFlags cv; // whether this pointer is const Type *atType; // type of the member protected: friend class BasicTypeFactory; friend class TypeXmlReader; PointerToMemberType(NamedAtomicType *inClassNAT0, CVFlags c, Type *a); PointerToMemberType(ReadXML&) // a ctor for de-serialization : inClassNAT(NULL), cv(CV_NONE), atType(NULL) {} public: bool isConst() const { return !!(cv & CV_CONST); } // use this in contexts where the 'inClass' is known to be // a real compound (which is most contexts); this fails an // assertion if it turns out to be a TypeVariable CompoundType *inClass() const; // Type interface virtual Tag getTag() const { return T_POINTERTOMEMBER; } unsigned innerHashValue() const; virtual string toMLString() const; virtual string leftString(bool innerParen=true) const; virtual string rightString(bool innerParen=true) const; virtual int reprSize() const; virtual bool anyCtorSatisfies(TypePred &pred) const; virtual CVFlags getCVFlags() const; virtual void traverse(TypeVisitor &vis); }; // moved into template.h: // class TypeVariable // class TemplateParams // class InheritedTemplateParams // class TemplateInfo // class STemplateArgument // ------------------- type factory ------------------- // The type factory is used for constructing objects that represent // C++ types. The reasons for using a factory instead of direct // construction include: // // - Types have a complicated and unpredictable sharing structure, // which makes recursive deallocation impossible. The factory // is thus given responsibility for deallocation of all objects // created by that factory. (Hmm.. currently there's no interface // for relinquishing a reference back to the factory... doh.) // // - Types are intended to be immutable, and thus referentially // transparent. This enables the optimization of "hash consing" // where multiple requests for the same equivalent object yield // the exact same object. The factory is responsible for // maintaining the data structures necessary for this, and for // choosing whether to do it at all. // // - It is often desirable to annotate Types, but the base Type // hierarchy should be free from any particular annotations. // The factory allows one to derive subclasses of Type to add // such annotations, without modifying creation sites (since // they use the factory). Of course, an alternative is to use // a hash table on the side, but that's sometimes inconvenient. // first, we have the abstract interface of a TypeFactory class TypeFactory { public: virtual ~TypeFactory() {} // silence stupid compiler warnings // ---- constructors for the atomic types ---- // for now, only CompoundType is built this way, and I'm going to // provide a default implementation in TypeFactory to avoid having // to change the interface w.r.t. cc_qual virtual CompoundType *makeCompoundType (CompoundType::Keyword keyword, StringRef name); // ---- constructors for the constructed types ---- virtual CVAtomicType *makeCVAtomicType(AtomicType *atomic, CVFlags cv)=0; virtual PointerType *makePointerType(CVFlags cv, Type *atType)=0; // this returns a Type* instead of a ReferenceType because I need to // be able to return an error type virtual Type *makeReferenceType(Type *atType)=0; virtual FunctionType *makeFunctionType(Type *retType)=0; // this must be called after 'makeFunctionType', once all of the // parameters have been added virtual void doneParams(FunctionType *ft)=0; virtual ArrayType *makeArrayType(Type *eltType, int size)=0; virtual PointerToMemberType *makePointerToMemberType (NamedAtomicType *inClassNAT, CVFlags cv, Type *atType)=0; // ---- create a type based on another one ---- // NOTE: all 'syntax' pointers are nullable, since there are contexts // where I don't have an AST node to pass // NOTE: The functions in this section do *not* modify their argument // Types, rather they return a new object if the desired Type is different // from the one passed-in. (That is, they behave functionally.) // do a shallow clone virtual Type *shallowCloneType(Type *baseType); // given a type, set its cv-qualifiers to 'cv'; return NULL if the // base type cannot be so qualified; I pass the syntax from which // the 'cv' flags were derived, when I have it, for the benefit of // extension analyses // // NOTE: 'baseType' is *not* modified; a copy is returned if necessary virtual Type *setQualifiers(SourceLoc loc, CVFlags cv, Type *baseType, TypeSpecifier * /*nullable*/ syntax); // add 'cv' to existing qualifiers; default implementation just // calls setQualifiers virtual Type *applyCVToType(SourceLoc loc, CVFlags cv, Type *baseType, TypeSpecifier * /*nullable*/ syntax); // build a pointer type from a syntactic description; here I allow // the factory to know the name of an AST node, but the default // implementation will not use it, so it need not be linked in for // this to make sense virtual Type *syntaxPointerType(SourceLoc loc, CVFlags cv, Type *underlying, D_pointer * /*nullable*/ syntax); virtual Type *syntaxReferenceType(SourceLoc loc, Type *underlying, D_reference * /*nullable*/ syntax); // similar for a function type; the parameters will be added by // the caller after this function returns virtual FunctionType *syntaxFunctionType(SourceLoc loc, Type *retType, D_func * /*nullable*/ syntax, TranslationUnit *tunit); // and another for pointer-to-member virtual PointerToMemberType *syntaxPointerToMemberType(SourceLoc loc, NamedAtomicType *inClassNAT, CVFlags cv, Type *atType, D_ptrToMember * /*nullable*/ syntax); // given a class, build the type of the receiver object parameter // 1/19/03: it should be a *reference* type // 1/30/04: fixing name: it's the receiver, not 'this' // 4/18/04: the 'classType' has been generalized because we // represent ptr-to-member-func using a FunctionType // with receiver parameter of type 'classType' virtual Type *makeTypeOf_receiver(SourceLoc loc, NamedAtomicType *classType, CVFlags cv, D_func * /*nullable*/ syntax); // given a function type and a return type, make a new function type // which is like it but has no parameters; i.e., copy all fields // except 'params'; this does *not* call doneParams virtual FunctionType *makeSimilarFunctionType(SourceLoc loc, Type *retType, FunctionType *similar); // ---- similar functions for Variable ---- // Why not make a separate factory? // - It's inconvenient to have two. // - Every application I can think of will want to define both // or neither. // - Variable is used by Type and vice-versa.. they could have // both been defined in cc_type.h virtual Variable *makeVariable(SourceLoc L, StringRef n, Type *t, DeclFlags f, TranslationUnit *tunit)=0; // ---- convenience functions ---- // these functions are implemented in TypeFactory directly, in // terms of the interface above, so they're not virtual // given an AtomicType, wrap it in a CVAtomicType // with no const or volatile qualifiers CVAtomicType *makeType(AtomicType *atomic) { return makeCVAtomicType(atomic, CV_NONE); } // make a ptr-to-'type' type; returns generic Type instead of // PointerType because sometimes I return ST_ERROR inline Type *makePtrType(Type *type) { return type->isError()? type : makePointerType(CV_NONE, type); } // map a simple type into its CVAtomicType representative CVAtomicType *getSimpleType(SimpleTypeId st, CVFlags cv = CV_NONE); // given an array type with no size, return one that is // the same except its size is as specified ArrayType *setArraySize(SourceLoc loc, ArrayType *type, int size); }; // This is an implementation of the above interface which returns the // actual Type-derived objects defined, as opposed to objects of // further-derived classes. On the extension topics mentioned above: // - It does not deallocate Types at all (oh well...). // - It does not (yet) implement hash-consing, except for CVAtomics // of SimpleTypes. // - No annotations are added; the objects returned have exactly // the types declared below. class BasicTypeFactory : public TypeFactory { private: // data // global array of non-const, non-volatile built-ins; it's expected // to be treated as read-only static CVAtomicType unqualifiedSimple[NUM_SIMPLE_TYPES]; public: // funcs // TypeFactory funcs virtual CVAtomicType *makeCVAtomicType(AtomicType *atomic, CVFlags cv); virtual PointerType *makePointerType(CVFlags cv, Type *atType); virtual Type *makeReferenceType(Type *atType); virtual FunctionType *makeFunctionType(Type *retType); virtual void doneParams(FunctionType *ft); virtual ArrayType *makeArrayType(Type *eltType, int size); virtual PointerToMemberType *makePointerToMemberType (NamedAtomicType *inClassNAT, CVFlags cv, Type *atType); virtual Variable *makeVariable(SourceLoc L, StringRef n, Type *t, DeclFlags f, TranslationUnit *tunit); }; // this one should be sound; gradually making it more available template inline SObjList const & objToSObjListC(ObjList const &list) { return reinterpret_cast const &>(list); } // -------------------- XReprSize --------------------- // thrown when the reprSize() function cannot determine an // array size class XReprSize : public xBase { public: // This is set to true when the reason for failing to determine a // size is that a dynamically-sized array was involved (this is a // gnu extension). // // TODO: The better solution is to store the size expression with // the array, so I can compute a symbolic expression that evaluates // to the array's size. But that involves changing a lot of stuff. bool isDynamic; public: XReprSize(bool isDynamic = false); XReprSize(XReprSize const &obj); ~XReprSize(); }; void throw_XReprSize(bool isDynamic = false) NORETURN; // ------ for debugging ------ // The idea here is you say "print type_toString(x)" in gdb, where 'x' // is a pointer to some type. The pointer that is returned will be // printed as a string by gdb. // // update: it turns out the latest gdbs are capable of using the // toString method directly! but I'll leave this here anyway. char *type_toString(Type const *t); // I should explain my intended relationship between references and // lvalues. The C++ standard uses the term 'reference' to refer to // types (a compile-time concept), and 'lvalue' to refer to // expressions and values (a run-time concept). Of course, a // reference type gives rise to an lvalue, and a (non-const) reference // must be bound to an lvalue, so there's a close relationship between // the two. In constrast, the same document uses the term 'pointer' // when referring to both types and values. // // In the interest of reducing the number of distinct concepts, I use // "reference types" and "lvalues" interchangeably; non-references // refer to rvalues. Generally, this works well. However, one must // keep in mind that there is consequently a slight terminological // difference between my stuff and the C++ standard. For example, in // the code // // int a; // f(a); // // the argument 'a' has type 'int' and is an lvalue according to the // standard, whereas cc_tcheck.cc just says it's an 'int &'. #endif // CC_TYPE_H