From: Douglas Gregor Date: Fri, 5 Dec 2008 23:32:09 +0000 (+0000) Subject: Introduce basic support for dependent types, type-dependent X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=898574e7496ba8fd76290079d3a9d06954992734;p=clang Introduce basic support for dependent types, type-dependent expressions, and value-dependent expressions. This permits us to parse some template definitions. This is not a complete solution; we're missing type- and value-dependent computations for most of the expression types, and we're missing checks for dependent types and type-dependent expressions throughout Sema. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@60615 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 0d3568b127..2a8b1c8a5b 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -60,6 +60,7 @@ class ASTContext { llvm::FoldingSet ConstantArrayTypes; llvm::FoldingSet IncompleteArrayTypes; std::vector VariableArrayTypes; + std::vector DependentSizedArrayTypes; llvm::FoldingSet VectorTypes; llvm::FoldingSet FunctionTypeNoProtos; llvm::FoldingSet FunctionTypeProtos; @@ -142,6 +143,7 @@ public: QualType FloatComplexTy, DoubleComplexTy, LongDoubleComplexTy; QualType VoidPtrTy; QualType OverloadTy; + QualType DependentTy; ASTContext(const LangOptions& LOpts, SourceManager &SM, TargetInfo &t, IdentifierTable &idents, SelectorTable &sels, @@ -183,6 +185,14 @@ public: QualType getVariableArrayType(QualType EltTy, Expr *NumElts, ArrayType::ArraySizeModifier ASM, unsigned EltTypeQuals); + + /// getDependentSizedArrayType - Returns a non-unique reference to + /// the type for a dependently-sized array of the specified element + /// type. FIXME: We will need these to be uniqued, or at least + /// comparable, at some point. + QualType getDependentSizedArrayType(QualType EltTy, Expr *NumElts, + ArrayType::ArraySizeModifier ASM, + unsigned EltTypeQuals); /// getIncompleteArrayType - Returns a unique reference to the type for a /// incomplete array of the specified element type. diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 02e581dce4..57c9763f7d 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -38,8 +38,27 @@ namespace clang { /// class Expr : public Stmt { QualType TR; + + /// TypeDependent - Whether this expression is type-dependent + /// (C++ [temp.dep.expr]). + bool TypeDependent : 1; + + /// ValueDependent - Whether this expression is value-dependent + /// (C++ [temp.dep.constexpr]). + bool ValueDependent : 1; + protected: - Expr(StmtClass SC, QualType T) : Stmt(SC) { setType(T); } + // FIXME: Eventually, this constructor should go away and we should + // require every subclass to provide type/value-dependence + // information. + Expr(StmtClass SC, QualType T) + : Stmt(SC), TypeDependent(false), ValueDependent(false) { setType(T); } + + Expr(StmtClass SC, QualType T, bool TD, bool VD) + : Stmt(SC), TypeDependent(TD), ValueDependent(VD) { + setType(T); + } + public: QualType getType() const { return TR; } void setType(QualType t) { @@ -56,6 +75,28 @@ public: TR = t; } + /// isValueDependent - Determines whether this expression is + /// value-dependent (C++ [temp.dep.constexpr]). For example, the + /// array bound of "Chars" in the following example is + /// value-dependent. + /// @code + /// template struct meta_string; + /// @endcode + bool isValueDependent() const { return ValueDependent; } + + /// isTypeDependent - Determines whether this expression is + /// type-dependent (C++ [temp.dep.expr]), which means that its type + /// could change from one template instantiation to the next. For + /// example, the expressions "x" and "x + y" are type-dependent in + /// the following code, but "y" is not type-dependent: + /// @code + /// template + /// void add(T x, int y) { + /// x + y; + /// } + /// @endcode + bool isTypeDependent() const { return TypeDependent; } + /// SourceLocation tokens are not useful in isolation - they are low level /// value objects created/interpreted by SourceManager. We assume AST /// clients will have a pointer to the respective SourceManager. @@ -204,7 +245,10 @@ public: const Expr *IgnoreParenCasts() const { return const_cast(this)->IgnoreParenCasts(); } - + + static bool hasAnyTypeDependentArguments(Expr** Exprs, unsigned NumExprs); + static bool hasAnyValueDependentArguments(Expr** Exprs, unsigned NumExprs); + static bool classof(const Stmt *T) { return T->getStmtClass() >= firstExprConstant && T->getStmtClass() <= lastExprConstant; @@ -232,8 +276,13 @@ protected: Expr(SC, t), D(d), Loc(l) {} public: + // FIXME: Eventually, this constructor will go away and all clients + // will have to provide the type- and value-dependent flags. DeclRefExpr(NamedDecl *d, QualType t, SourceLocation l) : Expr(DeclRefExprClass, t), D(d), Loc(l) {} + + DeclRefExpr(NamedDecl *d, QualType t, SourceLocation l, bool TD, bool VD) : + Expr(DeclRefExprClass, t, TD, VD), D(d), Loc(l) {} NamedDecl *getDecl() { return D; } const NamedDecl *getDecl() const { return D; } @@ -450,7 +499,9 @@ class ParenExpr : public Expr { Stmt *Val; public: ParenExpr(SourceLocation l, SourceLocation r, Expr *val) - : Expr(ParenExprClass, val->getType()), L(l), R(r), Val(val) {} + : Expr(ParenExprClass, val->getType(), + val->isTypeDependent(), val->isValueDependent()), + L(l), R(r), Val(val) {} const Expr *getSubExpr() const { return cast(Val); } Expr *getSubExpr() { return cast(Val); } @@ -863,7 +914,14 @@ class CastExpr : public Expr { Stmt *Op; protected: CastExpr(StmtClass SC, QualType ty, Expr *op) : - Expr(SC, ty), Op(op) {} + Expr(SC, ty, + // Cast expressions are type-dependent if the type is + // dependent (C++ [temp.dep.expr]p3). + ty->isDependentType(), + // Cast expressions are value-dependent if the type is + // dependent or if the subexpression is value-dependent. + ty->isDependentType() || (op && op->isValueDependent())), + Op(op) {} public: Expr *getSubExpr() { return cast(Op); } @@ -1030,7 +1088,10 @@ public: BinaryOperator(Expr *lhs, Expr *rhs, Opcode opc, QualType ResTy, SourceLocation opLoc) - : Expr(BinaryOperatorClass, ResTy), Opc(opc), OpLoc(opLoc) { + : Expr(BinaryOperatorClass, ResTy, + lhs->isTypeDependent() || rhs->isTypeDependent(), + lhs->isValueDependent() || rhs->isValueDependent()), + Opc(opc), OpLoc(opLoc) { SubExprs[LHS] = lhs; SubExprs[RHS] = rhs; assert(!isCompoundAssignmentOp() && @@ -1128,7 +1189,14 @@ class ConditionalOperator : public Expr { Stmt* SubExprs[END_EXPR]; // Left/Middle/Right hand sides. public: ConditionalOperator(Expr *cond, Expr *lhs, Expr *rhs, QualType t) - : Expr(ConditionalOperatorClass, t) { + : Expr(ConditionalOperatorClass, t, + // FIXME: the type of the conditional operator doesn't + // depend on the type of the conditional, but the standard + // seems to imply that it could. File a bug! + ((lhs && lhs->isTypeDependent()) || (rhs && rhs->isTypeDependent())), + (cond->isValueDependent() || + (lhs && lhs->isValueDependent()) || + (rhs && rhs->isValueDependent()))) { SubExprs[COND] = cond; SubExprs[LHS] = lhs; SubExprs[RHS] = rhs; diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 411d35a5b1..ffcc9f6457 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -51,6 +51,7 @@ namespace clang { class ConstantArrayType; class VariableArrayType; class IncompleteArrayType; + class DependentSizedArrayType; class RecordType; class EnumType; class ComplexType; @@ -236,7 +237,7 @@ class Type { public: enum TypeClass { Builtin, Complex, Pointer, Reference, - ConstantArray, VariableArray, IncompleteArray, + ConstantArray, VariableArray, IncompleteArray, DependentSizedArray, Vector, ExtVector, FunctionNoProto, FunctionProto, TypeName, Tagged, ASQual, @@ -249,17 +250,21 @@ public: private: QualType CanonicalType; + /// Dependent - Whether this type is a dependent type (C++ [temp.dep.type]). + bool Dependent : 1; + /// TypeClass bitfield - Enum that specifies what subclass this belongs to. /// Note that this should stay at the end of the ivars for Type so that /// subclasses can pack their bitfields into the same word. unsigned TC : 5; + protected: // silence VC++ warning C4355: 'this' : used in base member initializer list Type *this_() { return this; } - Type(TypeClass tc, QualType Canonical) + Type(TypeClass tc, QualType Canonical, bool dependent) : CanonicalType(Canonical.isNull() ? QualType(this_(), 0) : Canonical), - TC(tc) {} - virtual ~Type() {}; + Dependent(dependent), TC(tc) {} + virtual ~Type() {} virtual void Destroy(ASTContext& C); friend class ASTContext; @@ -332,6 +337,7 @@ public: bool isConstantArrayType() const; bool isIncompleteArrayType() const; bool isVariableArrayType() const; + bool isDependentSizedArrayType() const; bool isRecordType() const; bool isClassType() const; bool isStructureType() const; @@ -343,6 +349,11 @@ public: bool isObjCQualifiedInterfaceType() const; // NSString bool isObjCQualifiedIdType() const; // id bool isTemplateTypeParmType() const; // C++ template type parameter + + /// isDependentType - Whether this type is a dependent type, meaning + /// that its definition somehow depends on a template parameter + /// (C++ [temp.dep.type]). + bool isDependentType() const { return Dependent; } bool isOverloadType() const; // C++ overloaded function // Type Checking Functions: Check to see if this type is structurally the @@ -357,7 +368,7 @@ public: const ReferenceType *getAsReferenceType() const; const RecordType *getAsRecordType() const; const RecordType *getAsStructureType() const; - /// NOTE: getAsArrayType* are methods on ASTContext. + /// NOTE: getAs*ArrayType are methods on ASTContext. const TypedefType *getAsTypedefType() const; const RecordType *getAsUnionType() const; const EnumType *getAsEnumType() const; @@ -440,7 +451,8 @@ class ASQualType : public Type, public llvm::FoldingSetNode { /// Address Space ID - The address space ID this type is qualified with. unsigned AddressSpace; ASQualType(Type *Base, QualType CanonicalPtr, unsigned AddrSpace) : - Type(ASQual, CanonicalPtr), BaseType(Base), AddressSpace(AddrSpace) { + Type(ASQual, CanonicalPtr, Base->isDependentType()), BaseType(Base), + AddressSpace(AddrSpace) { } friend class ASTContext; // ASTContext creates these. public: @@ -493,12 +505,15 @@ public: Float, Double, LongDouble, - Overload // This represents the type of an overloaded function declaration. + Overload, // This represents the type of an overloaded function declaration. + Dependent // This represents the type of a type-dependent expression. }; private: Kind TypeKind; public: - BuiltinType(Kind K) : Type(Builtin, QualType()), TypeKind(K) {} + BuiltinType(Kind K) + : Type(Builtin, QualType(), /*Dependent=*/(K == Dependent)), + TypeKind(K) {} Kind getKind() const { return TypeKind; } const char *getName() const; @@ -515,7 +530,8 @@ public: class ComplexType : public Type, public llvm::FoldingSetNode { QualType ElementType; ComplexType(QualType Element, QualType CanonicalPtr) : - Type(Complex, CanonicalPtr), ElementType(Element) { + Type(Complex, CanonicalPtr, Element->isDependentType()), + ElementType(Element) { } friend class ASTContext; // ASTContext creates these. public: @@ -548,7 +564,7 @@ class PointerLikeType : public Type { QualType PointeeType; protected: PointerLikeType(TypeClass K, QualType Pointee, QualType CanonicalPtr) : - Type(K, CanonicalPtr), PointeeType(Pointee) { + Type(K, CanonicalPtr, Pointee->isDependentType()), PointeeType(Pointee) { } public: @@ -597,7 +613,8 @@ protected: class BlockPointerType : public Type, public llvm::FoldingSetNode { QualType PointeeType; // Block is some kind of pointer type BlockPointerType(QualType Pointee, QualType CanonicalCls) : - Type(BlockPointer, CanonicalCls), PointeeType(Pointee) { + Type(BlockPointer, CanonicalCls, Pointee->isDependentType()), + PointeeType(Pointee) { } friend class ASTContext; // ASTContext creates these. public: @@ -651,8 +668,9 @@ public: class ArrayType : public Type, public llvm::FoldingSetNode { public: /// ArraySizeModifier - Capture whether this is a normal array (e.g. int X[4]) - /// an array with a static size (e.g. int X[static 4]), or with a star size - /// (e.g. int X[*]). 'static' is only allowed on function parameters. + /// an array with a static size (e.g. int X[static 4]), or an array + /// with a star size (e.g. int X[*]). + /// 'static' is only allowed on function parameters. enum ArraySizeModifier { Normal, Static, Star }; @@ -669,9 +687,16 @@ private: unsigned IndexTypeQuals : 3; protected: + // C++ [temp.dep.type]p1: + // A type is dependent if it is... + // - an array type constructed from any dependent type or whose + // size is specified by a constant expression that is + // value-dependent, ArrayType(TypeClass tc, QualType et, QualType can, ArraySizeModifier sm, unsigned tq) - : Type(tc, can), ElementType(et), SizeModifier(sm), IndexTypeQuals(tq) {} + : Type(tc, can, et->isDependentType() || tc == DependentSizedArray), + ElementType(et), SizeModifier(sm), IndexTypeQuals(tq) {} + friend class ASTContext; // ASTContext creates these. public: QualType getElementType() const { return ElementType; } @@ -683,7 +708,8 @@ public: static bool classof(const Type *T) { return T->getTypeClass() == ConstantArray || T->getTypeClass() == VariableArray || - T->getTypeClass() == IncompleteArray; + T->getTypeClass() == IncompleteArray || + T->getTypeClass() == DependentSizedArray; } static bool classof(const ArrayType *) { return true; } }; @@ -806,6 +832,54 @@ protected: friend class Type; }; +/// DependentSizedArrayType - This type represents an array type in +/// C++ whose size is a value-dependent expression. For example: +/// @code +/// template +/// class array { +/// T data[Size]; +/// }; +/// @endcode +/// For these types, we won't actually know what the array bound is +/// until template instantiation occurs, at which point this will +/// become either a ConstantArrayType or a VariableArrayType. +class DependentSizedArrayType : public ArrayType { + /// SizeExpr - An assignment expression that will instantiate to the + /// size of the array. + Stmt *SizeExpr; + + DependentSizedArrayType(QualType et, QualType can, Expr *e, + ArraySizeModifier sm, unsigned tq) + : ArrayType(DependentSizedArray, et, can, sm, tq), SizeExpr((Stmt*) e) {} + friend class ASTContext; // ASTContext creates these. + virtual void Destroy(ASTContext& C); + +public: + Expr *getSizeExpr() const { + // We use C-style casts instead of cast<> here because we do not wish + // to have a dependency of Type.h on Stmt.h/Expr.h. + return (Expr*) SizeExpr; + } + + virtual void getAsStringInternal(std::string &InnerString) const; + + static bool classof(const Type *T) { + return T->getTypeClass() == DependentSizedArray; + } + static bool classof(const DependentSizedArrayType *) { return true; } + + friend class StmtIteratorBase; + + void Profile(llvm::FoldingSetNodeID &ID) { + assert (0 && "Cannnot unique DependentSizedArrayTypes."); + } + +protected: + virtual void EmitImpl(llvm::Serializer& S) const; + static Type* CreateImpl(ASTContext& Context,llvm::Deserializer& D); + friend class Type; +}; + /// VectorType - GCC generic vector type. This type is created using /// __attribute__((vector_size(n)), where "n" specifies the vector size in /// bytes. Since the constructor takes the number of vector elements, the @@ -819,10 +893,12 @@ protected: unsigned NumElements; VectorType(QualType vecType, unsigned nElements, QualType canonType) : - Type(Vector, canonType), ElementType(vecType), NumElements(nElements) {} + Type(Vector, canonType, vecType->isDependentType()), + ElementType(vecType), NumElements(nElements) {} VectorType(TypeClass tc, QualType vecType, unsigned nElements, - QualType canonType) : Type(tc, canonType), ElementType(vecType), - NumElements(nElements) {} + QualType canonType) + : Type(tc, canonType, vecType->isDependentType()), ElementType(vecType), + NumElements(nElements) {} friend class ASTContext; // ASTContext creates these. public: @@ -924,8 +1000,8 @@ class FunctionType : public Type { QualType ResultType; protected: FunctionType(TypeClass tc, QualType res, bool SubclassInfo, - unsigned typeQuals, QualType Canonical) - : Type(tc, Canonical), + unsigned typeQuals, QualType Canonical, bool Dependent) + : Type(tc, Canonical, Dependent), SubClassData(SubclassInfo), TypeQuals(typeQuals), ResultType(res) {} bool getSubClassData() const { return SubClassData; } unsigned getTypeQuals() const { return TypeQuals; } @@ -945,7 +1021,8 @@ public: /// no information available about its arguments. class FunctionTypeNoProto : public FunctionType, public llvm::FoldingSetNode { FunctionTypeNoProto(QualType Result, QualType Canonical) - : FunctionType(FunctionNoProto, Result, false, 0, Canonical) {} + : FunctionType(FunctionNoProto, Result, false, 0, Canonical, + /*Dependent=*/false) {} friend class ASTContext; // ASTContext creates these. public: // No additional state past what FunctionType provides. @@ -974,9 +1051,21 @@ protected: /// 'int foo(int)' or 'int foo(void)'. 'void' is represented as having no /// arguments, not as having a single void argument. class FunctionTypeProto : public FunctionType, public llvm::FoldingSetNode { + /// hasAnyDependentType - Determine whether there are any dependent + /// types within the arguments passed in. + static bool hasAnyDependentType(const QualType *ArgArray, unsigned numArgs) { + for (unsigned Idx = 0; Idx < numArgs; ++Idx) + if (ArgArray[Idx]->isDependentType()) + return true; + + return false; + } + FunctionTypeProto(QualType Result, const QualType *ArgArray, unsigned numArgs, bool isVariadic, unsigned typeQuals, QualType Canonical) - : FunctionType(FunctionProto, Result, isVariadic, typeQuals, Canonical), + : FunctionType(FunctionProto, Result, isVariadic, typeQuals, Canonical, + (Result->isDependentType() || + hasAnyDependentType(ArgArray, numArgs))), NumArgs(numArgs) { // Fill in the trailing argument array. QualType *ArgInfo = reinterpret_cast(this+1);; @@ -1032,7 +1121,7 @@ class TypedefType : public Type { TypedefDecl *Decl; protected: TypedefType(TypeClass tc, TypedefDecl *D, QualType can) - : Type(tc, can), Decl(D) { + : Type(tc, can, can->isDependentType()), Decl(D) { assert(!isa(can) && "Invalid canonical type"); } friend class ASTContext; // ASTContext creates these. @@ -1062,9 +1151,7 @@ protected: /// TypeOfExpr (GCC extension). class TypeOfExpr : public Type { Expr *TOExpr; - TypeOfExpr(Expr *E, QualType can) : Type(TypeOfExp, can), TOExpr(E) { - assert(!isa(can) && "Invalid canonical type"); - } + TypeOfExpr(Expr *E, QualType can); friend class ASTContext; // ASTContext creates these. public: Expr *getUnderlyingExpr() const { return TOExpr; } @@ -1078,7 +1165,8 @@ public: /// TypeOfType (GCC extension). class TypeOfType : public Type { QualType TOType; - TypeOfType(QualType T, QualType can) : Type(TypeOfTyp, can), TOType(T) { + TypeOfType(QualType T, QualType can) + : Type(TypeOfTyp, can, T->isDependentType()), TOType(T) { assert(!isa(can) && "Invalid canonical type"); } friend class ASTContext; // ASTContext creates these. @@ -1096,7 +1184,11 @@ class TagType : public Type { friend class ASTContext; protected: - TagType(TagDecl *D, QualType can) : Type(Tagged, can), decl(D) {} + // FIXME: We'll need the user to pass in information about whether + // this type is dependent or not, because we don't have enough + // information to compute it here. + TagType(TagDecl *D, QualType can) + : Type(Tagged, can, /*Dependent=*/false), decl(D) {} public: TagDecl *getDecl() const { return decl; } @@ -1184,7 +1276,7 @@ class TemplateTypeParmType : public Type { protected: TemplateTypeParmType(TemplateTypeParmDecl *D) - : Type(TemplateTypeParm, QualType(this, 0)), Decl(D) { } + : Type(TemplateTypeParm, QualType(this, 0), /*Dependent=*/true), Decl(D) { } friend class ASTContext; // ASTContext creates these @@ -1214,7 +1306,7 @@ class ObjCInterfaceType : public Type { ObjCInterfaceDecl *Decl; protected: ObjCInterfaceType(TypeClass tc, ObjCInterfaceDecl *D) : - Type(tc, QualType()), Decl(D) { } + Type(tc, QualType(), /*Dependent=*/false), Decl(D) { } friend class ASTContext; // ASTContext creates these. public: @@ -1327,7 +1419,8 @@ class ObjCQualifiedIdType : public Type, llvm::SmallVector Protocols; ObjCQualifiedIdType(ObjCProtocolDecl **Protos, unsigned NumP) - : Type(ObjCQualifiedId, QualType()/*these are always canonical*/), + : Type(ObjCQualifiedId, QualType()/*these are always canonical*/, + /*Dependent=*/false), Protocols(Protos, Protos+NumP) { } friend class ASTContext; // ASTContext creates these. public: @@ -1469,6 +1562,9 @@ inline bool Type::isIncompleteArrayType() const { inline bool Type::isVariableArrayType() const { return isa(CanonicalType.getUnqualifiedType()); } +inline bool Type::isDependentSizedArrayType() const { + return isa(CanonicalType.getUnqualifiedType()); +} inline bool Type::isRecordType() const { return isa(CanonicalType.getUnqualifiedType()); } diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 7c8b4b1b41..35647cf02a 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -183,7 +183,14 @@ void ASTContext::InitBuiltinTypes() { InitBuiltinType(WCharTy, BuiltinType::WChar); // Placeholder type for functions. - InitBuiltinType(OverloadTy, BuiltinType::Overload); + InitBuiltinType(OverloadTy, BuiltinType::Overload); + + // Placeholder type for type-dependent expressions whose type is + // completely unknown. No code should ever check a type against + // DependentTy and users should never see it; however, it is here to + // help diagnose failures to properly check for type-dependent + // expressions. + InitBuiltinType(DependentTy, BuiltinType::Dependent); // C99 6.2.5p11. FloatComplexTy = getComplexType(FloatTy); @@ -235,6 +242,8 @@ ASTContext::getTypeInfo(const Type *T) { assert(0 && "Incomplete types have no size!"); case Type::VariableArray: assert(0 && "VLAs not implemented yet!"); + case Type::DependentSizedArray: + assert(0 && "Dependently-sized arrays don't have a known size"); case Type::ConstantArray: { const ConstantArrayType *CAT = cast(T); @@ -759,6 +768,28 @@ QualType ASTContext::getVariableArrayType(QualType EltTy, Expr *NumElts, return QualType(New, 0); } +/// getDependentSizedArrayType - Returns a non-unique reference to +/// the type for a dependently-sized array of the specified element +/// type. FIXME: We will need these to be uniqued, or at least +/// comparable, at some point. +QualType ASTContext::getDependentSizedArrayType(QualType EltTy, Expr *NumElts, + ArrayType::ArraySizeModifier ASM, + unsigned EltTypeQuals) { + assert((NumElts->isTypeDependent() || NumElts->isValueDependent()) && + "Size must be type- or value-dependent!"); + + // Since we don't unique expressions, it isn't possible to unique + // dependently-sized array types. + + DependentSizedArrayType *New + = new DependentSizedArrayType(EltTy, QualType(), NumElts, + ASM, EltTypeQuals); + + DependentSizedArrayTypes.push_back(New); + Types.push_back(New); + return QualType(New, 0); +} + QualType ASTContext::getIncompleteArrayType(QualType EltTy, ArrayType::ArraySizeModifier ASM, unsigned EltTypeQuals) { @@ -1174,6 +1205,11 @@ QualType ASTContext::getCanonicalType(QualType T) { return getIncompleteArrayType(NewEltTy, IAT->getSizeModifier(), IAT->getIndexTypeQualifier()); + if (DependentSizedArrayType *DSAT = dyn_cast(AT)) + return getDependentSizedArrayType(NewEltTy, DSAT->getSizeExpr(), + DSAT->getSizeModifier(), + DSAT->getIndexTypeQualifier()); + // FIXME: What is the ownership of size expressions in VLAs? VariableArrayType *VAT = cast(AT); return getVariableArrayType(NewEltTy, VAT->getSizeExpr(), @@ -1246,6 +1282,16 @@ const ArrayType *ASTContext::getAsArrayType(QualType T) { return cast(getIncompleteArrayType(NewEltTy, IAT->getSizeModifier(), IAT->getIndexTypeQualifier())); + + // FIXME: What is the ownership of size expressions in + // dependent-sized array types? + if (const DependentSizedArrayType *DSAT + = dyn_cast(ATy)) + return cast( + getDependentSizedArrayType(NewEltTy, + DSAT->getSizeExpr(), + DSAT->getSizeModifier(), + DSAT->getIndexTypeQualifier())); // FIXME: What is the ownership of size expressions in VLAs? const VariableArrayType *VAT = cast(ATy); diff --git a/lib/AST/CFG.cpp b/lib/AST/CFG.cpp index 82336a44e0..69e82f2b83 100644 --- a/lib/AST/CFG.cpp +++ b/lib/AST/CFG.cpp @@ -154,6 +154,8 @@ private: bool badCFG; }; +// FIXME: Add support for dependent-sized array types in C++? +// Does it even make sense to build a CFG for an uninstantiated template? static VariableArrayType* FindVA(Type* t) { while (ArrayType* vt = dyn_cast(t)) { if (VariableArrayType* vat = dyn_cast(vt)) diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index a3264b010b..14db18c134 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -103,7 +103,10 @@ const char *UnaryOperator::getOpcodeStr(Opcode Op) { CallExpr::CallExpr(StmtClass SC, Expr *fn, Expr **args, unsigned numargs, QualType t, SourceLocation rparenloc) - : Expr(SC, t), NumArgs(numargs) { + : Expr(SC, t, + fn->isTypeDependent() || hasAnyTypeDependentArguments(args, numargs), + fn->isValueDependent() || hasAnyValueDependentArguments(args, numargs)), + NumArgs(numargs) { SubExprs = new Stmt*[numargs+1]; SubExprs[FN] = fn; for (unsigned i = 0; i != numargs; ++i) @@ -113,7 +116,10 @@ CallExpr::CallExpr(StmtClass SC, Expr *fn, Expr **args, unsigned numargs, CallExpr::CallExpr(Expr *fn, Expr **args, unsigned numargs, QualType t, SourceLocation rparenloc) - : Expr(CallExprClass, t), NumArgs(numargs) { + : Expr(CallExprClass, t, + fn->isTypeDependent() || hasAnyTypeDependentArguments(args, numargs), + fn->isValueDependent() || hasAnyValueDependentArguments(args, numargs)), + NumArgs(numargs) { SubExprs = new Stmt*[numargs+1]; SubExprs[FN] = fn; for (unsigned i = 0; i != numargs; ++i) @@ -631,6 +637,26 @@ Expr *Expr::IgnoreParenCasts() { } } +/// hasAnyTypeDependentArguments - Determines if any of the expressions +/// in Exprs is type-dependent. +bool Expr::hasAnyTypeDependentArguments(Expr** Exprs, unsigned NumExprs) { + for (unsigned I = 0; I < NumExprs; ++I) + if (Exprs[I]->isTypeDependent()) + return true; + + return false; +} + +/// hasAnyValueDependentArguments - Determines if any of the expressions +/// in Exprs is value-dependent. +bool Expr::hasAnyValueDependentArguments(Expr** Exprs, unsigned NumExprs) { + for (unsigned I = 0; I < NumExprs; ++I) + if (Exprs[I]->isValueDependent()) + return true; + + return false; +} + bool Expr::isConstantExpr(ASTContext &Ctx, SourceLocation *Loc) const { switch (getStmtClass()) { default: diff --git a/lib/AST/StmtIterator.cpp b/lib/AST/StmtIterator.cpp index 46882422e1..4800345019 100644 --- a/lib/AST/StmtIterator.cpp +++ b/lib/AST/StmtIterator.cpp @@ -16,6 +16,8 @@ using namespace clang; +// FIXME: Add support for dependent-sized array types in C++? +// Does it even make sense to build a CFG for an uninstantiated template? static inline VariableArrayType* FindVA(Type* t) { while (ArrayType* vt = dyn_cast(t)) { if (VariableArrayType* vat = dyn_cast(vt)) diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 303fc7e7ca..5909c976aa 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -43,6 +43,10 @@ void VariableArrayType::Destroy(ASTContext& C) { delete this; } +void DependentSizedArrayType::Destroy(ASTContext& C) { + SizeExpr->Destroy(C); + delete this; +} /// getArrayElementTypeNoTypeQual - If this is an array type, return the /// element type of the array, potentially with type qualifiers missing. @@ -634,11 +638,12 @@ bool Type::isAggregateType() const { /// isConstantSizeType - Return true if this is not a variable sized type, /// according to the rules of C99 6.7.5p3. It is not legal to call this on -/// incomplete types. +/// incomplete types or dependent types. bool Type::isConstantSizeType() const { if (const ASQualType *ASQT = dyn_cast(CanonicalType)) return ASQT->getBaseType()->isConstantSizeType(); assert(!isIncompleteType() && "This doesn't make sense for incomplete types"); + assert(!isDependentType() && "This doesn't make sense for dependent types"); // The VAT must have a size, as it is known to be complete. return !isa(CanonicalType); } @@ -706,6 +711,7 @@ const char *BuiltinType::getName() const { case LongDouble: return "long double"; case WChar: return "wchar_t"; case Overload: return ""; + case Dependent: return ""; } } @@ -780,6 +786,11 @@ QualType TypedefType::LookThroughTypedefs() const { } } +TypeOfExpr::TypeOfExpr(Expr *E, QualType can) + : Type(TypeOfExp, can, E->isTypeDependent()), TOExpr(E) { + assert(!isa(can) && "Invalid canonical type"); +} + bool RecordType::classof(const TagType *TT) { return isa(TT->getDecl()); } @@ -932,6 +943,30 @@ void VariableArrayType::getAsStringInternal(std::string &S) const { getElementType().getAsStringInternal(S); } +void DependentSizedArrayType::getAsStringInternal(std::string &S) const { + S += '['; + + if (getIndexTypeQualifier()) { + AppendTypeQualList(S, getIndexTypeQualifier()); + S += ' '; + } + + if (getSizeModifier() == Static) + S += "static"; + else if (getSizeModifier() == Star) + S += '*'; + + if (getSizeExpr()) { + std::string SStr; + llvm::raw_string_ostream s(SStr); + getSizeExpr()->printPretty(s); + S += s.str(); + } + S += ']'; + + getElementType().getAsStringInternal(S); +} + void VectorType::getAsStringInternal(std::string &S) const { // FIXME: We prefer to print the size directly here, but have no way // to get the size of the type. diff --git a/lib/AST/TypeSerialization.cpp b/lib/AST/TypeSerialization.cpp index 3bdc946de7..8e35f187a3 100644 --- a/lib/AST/TypeSerialization.cpp +++ b/lib/AST/TypeSerialization.cpp @@ -314,6 +314,26 @@ Type* VariableArrayType::CreateImpl(ASTContext& Context, Deserializer& D) { return Context.getVariableArrayType(ElTy,SizeExpr,am,ITQ).getTypePtr(); } +//===----------------------------------------------------------------------===// +// DependentSizedArrayType +//===----------------------------------------------------------------------===// + +void DependentSizedArrayType::EmitImpl(Serializer& S) const { + S.Emit(getElementType()); + S.EmitInt(getSizeModifier()); + S.EmitInt(getIndexTypeQualifier()); + S.EmitOwnedPtr(SizeExpr); +} + +Type* DependentSizedArrayType::CreateImpl(ASTContext& Context, Deserializer& D) { + QualType ElTy = QualType::ReadVal(D); + ArraySizeModifier am = static_cast(D.ReadInt()); + unsigned ITQ = D.ReadInt(); + Expr* SizeExpr = D.ReadOwnedPtr(Context); + + return Context.getDependentSizedArrayType(ElTy,SizeExpr,am,ITQ).getTypePtr(); +} + //===----------------------------------------------------------------------===// // IncompleteArrayType //===----------------------------------------------------------------------===// diff --git a/lib/CodeGen/CodeGenTypes.cpp b/lib/CodeGen/CodeGenTypes.cpp index fbf1120407..85b10399a4 100644 --- a/lib/CodeGen/CodeGenTypes.cpp +++ b/lib/CodeGen/CodeGenTypes.cpp @@ -191,6 +191,7 @@ const llvm::Type *CodeGenTypes::ConvertNewType(QualType T) { switch (Ty.getTypeClass()) { case Type::TypeName: // typedef isn't canonical. case Type::TemplateTypeParm:// template type parameters never generated + case Type::DependentSizedArray: // dependent types are never generated case Type::TypeOfExp: // typeof isn't canonical. case Type::TypeOfTyp: // typeof isn't canonical. assert(0 && "Non-canonical type, shouldn't happen"); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c893d27597..e581b100dc 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -870,9 +870,8 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { if (PrevDecl && isTemplateParameterDecl(PrevDecl)) { // Maybe we will complain about the shadowed template parameter. - InvalidDecl - = InvalidDecl || DiagnoseTemplateParameterShadow(D.getIdentifierLoc(), - PrevDecl); + InvalidDecl = InvalidDecl + || DiagnoseTemplateParameterShadow(D.getIdentifierLoc(), PrevDecl); // Just pretend that we didn't see the previous declaration. PrevDecl = 0; } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index ec86b9f2a6..e4beb74fc5 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -490,7 +490,52 @@ Sema::ExprResult Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc, } // If this reference is not in a block or if the referenced variable is // within the block, create a normal DeclRefExpr. - return new DeclRefExpr(VD, VD->getType().getNonReferenceType(), Loc); + + // C++ [temp.dep.expr]p3: + // An id-expression is type-dependent if it contains: + bool TypeDependent = false; + + // - an identifier that was declared with a dependent type, + if (VD->getType()->isDependentType()) + TypeDependent = true; + // - FIXME: a template-id that is dependent, + // - a conversion-function-id that specifies a dependent type, + else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName && + Name.getCXXNameType()->isDependentType()) + TypeDependent = true; + // - a nested-name-specifier that contains a class-name that + // names a dependent type. + else if (SS && !SS->isEmpty()) { + for (DeclContext *DC = static_cast(SS->getScopeRep()); + DC; DC = DC->getParent()) { + // FIXME: could stop early at namespace scope. + if (DC->isCXXRecord()) { + CXXRecordDecl *Record = cast(DC); + if (Context.getTypeDeclType(Record)->isDependentType()) { + TypeDependent = true; + break; + } + } + } + } + + // C++ [temp.dep.constexpr]p2: + // + // An identifier is value-dependent if it is: + bool ValueDependent = false; + + // - a name declared with a dependent type, + if (TypeDependent) + ValueDependent = true; + // - the name of a non-type template parameter, + else if (isa(VD)) + ValueDependent = true; + // - a constant with integral or enumeration type and is + // initialized with an expression that is value-dependent + // (FIXME!). + + return new DeclRefExpr(VD, VD->getType().getNonReferenceType(), Loc, + TypeDependent, ValueDependent); } Sema::ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, @@ -1279,6 +1324,11 @@ ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc, FunctionDecl *FDecl = NULL; OverloadedFunctionDecl *Ovl = NULL; + // FIXME: Will need to cache the results of name lookup (including + // ADL) in Fn. + if (Fn->isTypeDependent() || Expr::hasAnyTypeDependentArguments(Args, NumArgs)) + return new CallExpr(Fn, Args, NumArgs, Context.DependentTy, RParenLoc); + // If we're directly calling a function or a set of overloaded // functions, get the appropriate declaration. { @@ -1318,6 +1368,7 @@ ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc, // of arguments and function on error. llvm::OwningPtr TheCall(new CallExpr(Fn, Args, NumArgs, Context.BoolTy, RParenLoc)); + const FunctionType *FuncT; if (!Fn->getType()->isBlockPointerType()) { // C99 6.5.2.2p1 - "The expression that denotes the called function shall @@ -1470,6 +1521,8 @@ bool Sema::CheckCastTypes(SourceRange TyR, QualType castType, Expr *&castExpr) { // type needs to be scalar. if (castType->isVoidType()) { // Cast to void allows any expr type. + } else if (castType->isDependentType() || castExpr->isTypeDependent()) { + // We can't check any more until template instantiation time. } else if (!castType->isScalarType() && !castType->isVectorType()) { // GCC struct/union extension: allow cast to self. if (Context.getCanonicalType(castType) != @@ -1541,13 +1594,17 @@ inline QualType Sema::CheckConditionalOperands( // C99 6.5.15 QualType rexT = rex->getType(); // first, check the condition. - if (!condT->isScalarType()) { // C99 6.5.15p2 - Diag(cond->getLocStart(), diag::err_typecheck_cond_expect_scalar) << condT; - return QualType(); + if (!cond->isTypeDependent()) { + if (!condT->isScalarType()) { // C99 6.5.15p2 + Diag(cond->getLocStart(), diag::err_typecheck_cond_expect_scalar) << condT; + return QualType(); + } } // Now check the two expressions. - + if ((lex && lex->isTypeDependent()) || (rex && rex->isTypeDependent())) + return Context.DependentTy; + // If both operands have arithmetic type, do the usual arithmetic conversions // to find a common type: C99 6.5.15p3,5. if (lexT->isArithmeticType() && rexT->isArithmeticType()) { @@ -2959,6 +3016,17 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, assert((lhs != 0) && "ActOnBinOp(): missing left expression"); assert((rhs != 0) && "ActOnBinOp(): missing right expression"); + // If either expression is type-dependent, just build the AST. + // FIXME: We'll need to perform some caching of the result of name + // lookup for operator+. + if (lhs->isTypeDependent() || rhs->isTypeDependent()) { + if (Opc > BinaryOperator::Assign && Opc <= BinaryOperator::OrAssign) + return new CompoundAssignOperator(lhs, rhs, Opc, Context.DependentTy, + Context.DependentTy, TokLoc); + else + return new BinaryOperator(lhs, rhs, Opc, Context.DependentTy, TokLoc); + } + if (getLangOptions().CPlusPlus && (lhs->getType()->isRecordType() || lhs->getType()->isEnumeralType() || rhs->getType()->isRecordType() || rhs->getType()->isEnumeralType())) { diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 1e9b945a18..c4cab3316b 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -163,9 +163,9 @@ Sema::ActOnCXXTypeConstructExpr(SourceRange TypeRange, TypeTy *TypeRep, // if (Ty->isArrayType()) return Diag(TyBeginLoc, diag::err_value_init_for_array_type) << FullRange; - if (Ty->isIncompleteType() && !Ty->isVoidType()) + if (!Ty->isDependentType() && Ty->isIncompleteType() && !Ty->isVoidType()) return Diag(TyBeginLoc, diag::err_invalid_incomplete_type_use) - << Ty << FullRange; + << Ty << FullRange; return new CXXZeroInitValueExpr(Ty, TyBeginLoc, RParenLoc); } diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 0159f5f0a5..21f897f639 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -782,21 +782,23 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprTy *rex) { return new ReturnStmt(ReturnLoc, (Expr*)0); } - // we have a non-void function with an expression, continue checking - QualType RetValType = RetValExp->getType(); - - // C99 6.8.6.4p3(136): The return statement is not an assignment. The - // overlap restriction of subclause 6.5.16.1 does not apply to the case of - // function return. - - // In C++ the return statement is handled via a copy initialization. - // the C version of which boils down to - // CheckSingleAssignmentConstraints. - if (PerformCopyInitialization(RetValExp, FnRetType, "returning")) - return true; - - if (RetValExp) CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc); + if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) { + // we have a non-void function with an expression, continue checking + QualType RetValType = RetValExp->getType(); + + // C99 6.8.6.4p3(136): The return statement is not an assignment. The + // overlap restriction of subclause 6.5.16.1 does not apply to the case of + // function return. + + // In C++ the return statement is handled via a copy initialization. + // the C version of which boils down to + // CheckSingleAssignmentConstraints. + if (PerformCopyInitialization(RetValExp, FnRetType, "returning")) + return true; + if (RetValExp) CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc); + } + return new ReturnStmt(ReturnLoc, (Expr*)RetValExp); } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 749d181baf..092c9d2236 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -12,6 +12,7 @@ //+//===----------------------------------------------------------------------===/ #include "Sema.h" +#include "clang/AST/Expr.h" #include "clang/Parse/DeclSpec.h" #include "clang/Basic/LangOptions.h" diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 4487c1e416..2423e1271b 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -391,6 +391,8 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { llvm::APSInt ConstVal(32); if (!ArraySize) { T = Context.getIncompleteArrayType(T, ASM, ATI.TypeQuals); + } else if (ArraySize->isValueDependent()) { + T = Context.getDependentSizedArrayType(T, ArraySize, ASM, ATI.TypeQuals); } else if (!ArraySize->isIntegerConstantExpr(ConstVal, Context) || !T->isConstantSizeType()) { // Per C99, a variable array is an array with either a non-constant @@ -416,7 +418,8 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { // If this is not C99, extwarn about VLA's and C99 array size modifiers. if (!getLangOptions().C99 && (ASM != ArrayType::Normal || - (ArraySize && !ArraySize->isIntegerConstantExpr(Context)))) + (ArraySize && !ArraySize->isValueDependent() && + !ArraySize->isIntegerConstantExpr(Context)))) Diag(D.getIdentifierLoc(), diag::ext_vla); break; } diff --git a/test/SemaCXX/dependent-types.cpp b/test/SemaCXX/dependent-types.cpp new file mode 100644 index 0000000000..a5d23ffaa4 --- /dev/null +++ b/test/SemaCXX/dependent-types.cpp @@ -0,0 +1,10 @@ +// RUN: clang -fsyntax-only -pedantic -verify %s + +template void f() { + T x1; + T* x2; + T& x3; // expected-error{{declaration of reference variable 'x3' requires an initializer}} + T x4[]; // expected-error{{variable has incomplete type 'T []'}} + T x5[Size]; + int x6[Size]; +} diff --git a/test/SemaCXX/type-dependent-exprs.cpp b/test/SemaCXX/type-dependent-exprs.cpp new file mode 100644 index 0000000000..d3fd8922ac --- /dev/null +++ b/test/SemaCXX/type-dependent-exprs.cpp @@ -0,0 +1,14 @@ +// RUN: clang -fsyntax-only -verify %s + +void g(int); + +template +T f(T x) { + (void)(x + 0); + (void)T(0); + (void)(x += 0); + (void)(x? x : x); + return g(x); + // h(x); // h is a dependent name + return 0; +}