From: Douglas Gregor Date: Fri, 13 Mar 2009 21:01:28 +0000 (+0000) Subject: Implement template instantiation for several more kinds of expressions: X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ba49817c5b9f502602672861cf369fd0e53966e8;p=clang Implement template instantiation for several more kinds of expressions: - C++ function casts, e.g., T(foo) - sizeof(), alignof() More importantly, this allows us to verify that we're performing overload resolution during template instantiation, with argument-dependent lookup and the "cached" results of name lookup from the template definition. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66947 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Driver/RewriteObjC.cpp b/Driver/RewriteObjC.cpp index 1e2cac39de..c3cc2c3852 100644 --- a/Driver/RewriteObjC.cpp +++ b/Driver/RewriteObjC.cpp @@ -2553,8 +2553,8 @@ Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp) { FT->getResultType(), SourceLocation()); // Build sizeof(returnType) - SizeOfAlignOfExpr *sizeofExpr = new (Context) SizeOfAlignOfExpr(true, true, - returnType.getAsOpaquePtr(), + SizeOfAlignOfExpr *sizeofExpr = new (Context) SizeOfAlignOfExpr(true, + returnType, Context->getSizeType(), SourceLocation(), SourceLocation()); // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 3af207789c..bfd8538788 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -692,20 +692,26 @@ class SizeOfAlignOfExpr : public Expr { } Argument; SourceLocation OpLoc, RParenLoc; public: - SizeOfAlignOfExpr(bool issizeof, bool istype, void *argument, + SizeOfAlignOfExpr(bool issizeof, QualType T, QualType resultType, SourceLocation op, SourceLocation rp) : Expr(SizeOfAlignOfExprClass, resultType, - false, // Never type-dependent. + false, // Never type-dependent (C++ [temp.dep.expr]p3). // Value-dependent if the argument is type-dependent. - (istype ? QualType::getFromOpaquePtr(argument)->isDependentType() - : static_cast(argument)->isTypeDependent())), - isSizeof(issizeof), isType(istype), OpLoc(op), RParenLoc(rp) { - if (isType) - Argument.Ty = argument; - else - // argument was an Expr*, so cast it back to that to be safe - Argument.Ex = static_cast(argument); + T->isDependentType()), + isSizeof(issizeof), isType(true), OpLoc(op), RParenLoc(rp) { + Argument.Ty = T.getAsOpaquePtr(); + } + + SizeOfAlignOfExpr(bool issizeof, Expr *E, + QualType resultType, SourceLocation op, + SourceLocation rp) : + Expr(SizeOfAlignOfExprClass, resultType, + false, // Never type-dependent (C++ [temp.dep.expr]p3). + // Value-dependent if the argument is type-dependent. + E->isTypeDependent()), + isSizeof(issizeof), isType(false), OpLoc(op), RParenLoc(rp) { + Argument.Ex = E; } virtual void Destroy(ASTContext& C); diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 37fead0cbf..8bf91c2c61 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -467,6 +467,8 @@ public: const_arg_iterator arg_begin() const { return Args; } const_arg_iterator arg_end() const { return Args + NumArgs; } + unsigned getNumArgs() const { return NumArgs; } + virtual SourceRange getSourceRange() const { return SourceRange(TyBeginLoc, RParenLoc); } diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 184c9fe490..1d4a3ba3a6 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -216,7 +216,10 @@ CXXTemporaryObjectExpr::CXXTemporaryObjectExpr(CXXConstructorDecl *Cons, Expr **Args, unsigned NumArgs, SourceLocation rParenLoc) - : Expr(CXXTemporaryObjectExprClass, writtenTy), + : Expr(CXXTemporaryObjectExprClass, writtenTy, + writtenTy->isDependentType(), + (writtenTy->isDependentType() || + CallExpr::hasAnyValueDependentArguments(Args, NumArgs))), TyBeginLoc(tyBeginLoc), RParenLoc(rParenLoc), Constructor(Cons), Args(0), NumArgs(NumArgs) { if (NumArgs > 0) { diff --git a/lib/AST/StmtSerialization.cpp b/lib/AST/StmtSerialization.cpp index 4a90edd7a1..78366a1514 100644 --- a/lib/AST/StmtSerialization.cpp +++ b/lib/AST/StmtSerialization.cpp @@ -832,9 +832,14 @@ SizeOfAlignOfExpr::CreateImpl(Deserializer& D, ASTContext& C) { QualType Res = QualType::ReadVal(D); SourceLocation OpLoc = SourceLocation::ReadVal(D); SourceLocation RParenLoc = SourceLocation::ReadVal(D); - - return new SizeOfAlignOfExpr(isSizeof, isType, Argument, Res, - OpLoc, RParenLoc); + + if (isType) + return new (C) SizeOfAlignOfExpr(isSizeof, + QualType::getFromOpaquePtr(Argument), + Res, OpLoc, RParenLoc); + + return new (C) SizeOfAlignOfExpr(isSizeof, (Expr *)Argument, + Res, OpLoc, RParenLoc); } void StmtExpr::EmitImpl(Serializer& S) const { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 1b7fc1be4a..fc71097c12 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -967,9 +967,12 @@ ClassTemplateSpecializationType(TemplateDecl *T, const TemplateArgument *Args, } void ClassTemplateSpecializationType::Destroy(ASTContext& C) { - for (unsigned Arg = 0; Arg < NumArgs; ++Arg) - if (Expr *E = getArg(Arg).getAsExpr()) - E->Destroy(C); + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) { + // FIXME: Not all expressions get cloned, so we can't yet perform + // this destruction. + // if (Expr *E = getArg(Arg).getAsExpr()) + // E->Destroy(C); + } } ClassTemplateSpecializationType::iterator diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index c6fe6c164a..b29790e7b5 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1188,6 +1188,11 @@ public: ActOnSizeOfAlignOfExpr(SourceLocation OpLoc, bool isSizeof, bool isType, void *TyOrEx, const SourceRange &ArgRange); + OwningExprResult CreateSizeOfAlignOfExpr(QualType T, SourceLocation OpLoc, + bool isSizeOf, SourceRange R); + OwningExprResult CreateSizeOfAlignOfExpr(Expr *E, SourceLocation OpLoc, + bool isSizeOf, SourceRange R); + bool CheckAlignOfExpr(Expr *E, SourceLocation OpLoc, const SourceRange &R); bool CheckSizeOfAlignOfOperand(QualType type, SourceLocation OpLoc, const SourceRange &R, bool isSizeof); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 0855932ac4..c04006cb1c 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1220,6 +1220,50 @@ bool Sema::CheckAlignOfExpr(Expr *E, SourceLocation OpLoc, return CheckSizeOfAlignOfOperand(E->getType(), OpLoc, ExprRange, false); } +/// \brief Build a sizeof or alignof expression given a type operand. +Action::OwningExprResult +Sema::CreateSizeOfAlignOfExpr(QualType T, SourceLocation OpLoc, + bool isSizeOf, SourceRange R) { + if (T.isNull()) + return ExprError(); + + if (!T->isDependentType() && + CheckSizeOfAlignOfOperand(T, OpLoc, R, isSizeOf)) + return ExprError(); + + // C99 6.5.3.4p4: the type (an unsigned integer type) is size_t. + return Owned(new (Context) SizeOfAlignOfExpr(isSizeOf, T, + Context.getSizeType(), OpLoc, + R.getEnd())); +} + +/// \brief Build a sizeof or alignof expression given an expression +/// operand. +Action::OwningExprResult +Sema::CreateSizeOfAlignOfExpr(Expr *E, SourceLocation OpLoc, + bool isSizeOf, SourceRange R) { + // Verify that the operand is valid. + bool isInvalid = false; + if (E->isTypeDependent()) { + // Delay type-checking for type-dependent expressions. + } else if (!isSizeOf) { + isInvalid = CheckAlignOfExpr(E, OpLoc, R); + } else if (E->isBitField()) { // C99 6.5.3.4p1. + Diag(OpLoc, diag::err_sizeof_alignof_bitfield) << 0; + isInvalid = true; + } else { + isInvalid = CheckSizeOfAlignOfOperand(E->getType(), OpLoc, R, true); + } + + if (isInvalid) + return ExprError(); + + // C99 6.5.3.4p4: the type (an unsigned integer type) is size_t. + return Owned(new (Context) SizeOfAlignOfExpr(isSizeOf, E, + Context.getSizeType(), OpLoc, + R.getEnd())); +} + /// ActOnSizeOfAlignOfExpr - Handle @c sizeof(type) and @c sizeof @c expr and /// the same for @c alignof and @c __alignof /// Note that the ArgRange is invalid if isType is false. @@ -1229,42 +1273,20 @@ Sema::ActOnSizeOfAlignOfExpr(SourceLocation OpLoc, bool isSizeof, bool isType, // If error parsing type, ignore. if (TyOrEx == 0) return ExprError(); - QualType ArgTy; - SourceRange Range; if (isType) { - ArgTy = QualType::getFromOpaquePtr(TyOrEx); - Range = ArgRange; - - // Verify that the operand is valid. - if (CheckSizeOfAlignOfOperand(ArgTy, OpLoc, Range, isSizeof)) - return ExprError(); - } else { - // Get the end location. - Expr *ArgEx = (Expr *)TyOrEx; - Range = ArgEx->getSourceRange(); - ArgTy = ArgEx->getType(); - - // Verify that the operand is valid. - bool isInvalid; - if (!isSizeof) { - isInvalid = CheckAlignOfExpr(ArgEx, OpLoc, Range); - } else if (ArgEx->isBitField()) { // C99 6.5.3.4p1. - Diag(OpLoc, diag::err_sizeof_alignof_bitfield) << 0; - isInvalid = true; - } else { - isInvalid = CheckSizeOfAlignOfOperand(ArgTy, OpLoc, Range, true); - } - - if (isInvalid) { - DeleteExpr(ArgEx); - return ExprError(); - } - } + QualType ArgTy = QualType::getFromOpaquePtr(TyOrEx); + return CreateSizeOfAlignOfExpr(ArgTy, OpLoc, isSizeof, ArgRange); + } - // C99 6.5.3.4p4: the type (an unsigned integer type) is size_t. - return Owned(new (Context) SizeOfAlignOfExpr(isSizeof, isType, TyOrEx, - Context.getSizeType(), OpLoc, - Range.getEnd())); + // Get the end location. + Expr *ArgEx = (Expr *)TyOrEx; + Action::OwningExprResult Result + = CreateSizeOfAlignOfExpr(ArgEx, OpLoc, isSizeof, ArgEx->getSourceRange()); + + if (Result.isInvalid()) + DeleteExpr(ArgEx); + + return move(Result); } QualType Sema::CheckRealImagOperand(Expr *&V, SourceLocation Loc, bool isReal) { diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 501eda7443..e5a252043e 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -122,6 +122,13 @@ Sema::ActOnCXXTypeConstructExpr(SourceRange TypeRange, TypeTy *TypeRep, SourceLocation TyBeginLoc = TypeRange.getBegin(); SourceRange FullRange = SourceRange(TyBeginLoc, RParenLoc); + if (Ty->isDependentType() || + CallExpr::hasAnyTypeDependentArguments(Exprs, NumExprs)) { + return new (Context) CXXTemporaryObjectExpr(0, Ty, TyBeginLoc, + Exprs, NumExprs, RParenLoc); + } + + // C++ [expr.type.conv]p1: // If the expression list is a single expression, the type conversion // expression is equivalent (in definedness, and if defined in meaning) to the @@ -134,8 +141,6 @@ Sema::ActOnCXXTypeConstructExpr(SourceRange TypeRange, TypeTy *TypeRep, TyBeginLoc, Exprs[0], RParenLoc); } - // FIXME: What AST node to create when the type is dependent? - if (const RecordType *RT = Ty->getAsRecordType()) { CXXRecordDecl *Record = cast(RT->getDecl()); diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index d391c07b3f..57683b30ec 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -1392,6 +1392,9 @@ static bool IsAcceptableNonMemberOperatorCandidate(FunctionDecl *Fn, QualType T1, QualType T2, ASTContext &Context) { + if (T1->isDependentType() || (!T2.isNull() && T2->isDependentType())) + return true; + if (T1->isRecordType() || (!T2.isNull() && T2->isRecordType())) return true; diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 6fc4515571..3780259d43 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1275,7 +1275,14 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, QualType IntegerType = Context.getCanonicalType(ParamType); if (const EnumType *Enum = IntegerType->getAsEnumType()) IntegerType = Enum->getDecl()->getIntegerType(); - + + if (Arg->isValueDependent()) { + // The argument is value-dependent. Create a new + // TemplateArgument with the converted expression. + Converted->push_back(TemplateArgument(Arg)); + return false; + } + llvm::APInt CanonicalArg(Context.getTypeSize(IntegerType), 0, IntegerType->isSignedIntegerType()); CanonicalArg = Value; diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index b3e4148a6a..4bd7ad4e83 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/StmtVisitor.h" #include "clang/Parse/DeclSpec.h" +#include "clang/Lex/Preprocessor.h" // for the identifier table #include "clang/Basic/LangOptions.h" #include "llvm/Support/Compiler.h" @@ -441,7 +442,12 @@ InstantiateClassTemplateSpecializationType( break; case TemplateArgument::Expression: - assert(false && "Cannot instantiate expressions yet"); + Sema::OwningExprResult E + = SemaRef.InstantiateExpr(Arg->getAsExpr(), TemplateArgs, + NumTemplateArgs); + if (E.isInvalid()) + return QualType(); + InstantiatedTemplateArgs.push_back((Expr *)E.release()); break; } } @@ -564,6 +570,8 @@ namespace { unsigned NumTemplateArgs; public: + typedef Sema::OwningExprResult OwningExprResult; + TemplateExprInstantiator(Sema &SemaRef, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs) @@ -573,11 +581,13 @@ namespace { // FIXME: Once we get closer to completion, replace these // manually-written declarations with automatically-generated ones // from clang/AST/StmtNodes.def. - Sema::OwningExprResult VisitIntegerLiteral(IntegerLiteral *E); - Sema::OwningExprResult VisitDeclRefExpr(DeclRefExpr *E); - Sema::OwningExprResult VisitParenExpr(ParenExpr *E); - Sema::OwningExprResult VisitBinaryOperator(BinaryOperator *E); - Sema::OwningExprResult VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E); + OwningExprResult VisitIntegerLiteral(IntegerLiteral *E); + OwningExprResult VisitDeclRefExpr(DeclRefExpr *E); + OwningExprResult VisitParenExpr(ParenExpr *E); + OwningExprResult VisitBinaryOperator(BinaryOperator *E); + OwningExprResult VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E); + OwningExprResult VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); + OwningExprResult VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *E); // Base case. I'm supposed to ignore this. Sema::OwningExprResult VisitStmt(Stmt *) { @@ -692,6 +702,9 @@ TemplateExprInstantiator::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) { DeclRefExpr *DRE = cast(E->getCallee()); OverloadedFunctionDecl *Overloads = cast(DRE->getDecl()); + + // FIXME: Do we have to check + // IsAcceptableNonMemberOperatorCandidate for each of these? for (OverloadedFunctionDecl::function_iterator F = Overloads->function_begin(), FEnd = Overloads->function_end(); @@ -716,6 +729,90 @@ TemplateExprInstantiator::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) { return move(Result); } +Sema::OwningExprResult +TemplateExprInstantiator::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { + bool isSizeOf = E->isSizeOf(); + + if (E->isArgumentType()) { + QualType T = E->getArgumentType(); + if (T->isDependentType()) { + T = SemaRef.InstantiateType(T, TemplateArgs, NumTemplateArgs, + /*FIXME*/E->getOperatorLoc(), + &SemaRef.PP.getIdentifierTable().get("sizeof")); + if (T.isNull()) + return SemaRef.ExprError(); + } + + return SemaRef.CreateSizeOfAlignOfExpr(T, E->getOperatorLoc(), isSizeOf, + E->getSourceRange()); + } + + Sema::OwningExprResult Arg = Visit(E->getArgumentExpr()); + if (Arg.isInvalid()) + return SemaRef.ExprError(); + + Sema::OwningExprResult Result + = SemaRef.CreateSizeOfAlignOfExpr((Expr *)Arg.get(), E->getOperatorLoc(), + isSizeOf, E->getSourceRange()); + if (Result.isInvalid()) + return SemaRef.ExprError(); + + Arg.release(); + return move(Result); +} + +Sema::OwningExprResult +TemplateExprInstantiator::VisitCXXTemporaryObjectExpr( + CXXTemporaryObjectExpr *E) { + QualType T = E->getType(); + if (T->isDependentType()) { + T = SemaRef.InstantiateType(T, TemplateArgs, NumTemplateArgs, + E->getTypeBeginLoc(), DeclarationName()); + if (T.isNull()) + return SemaRef.ExprError(); + } + + llvm::SmallVector Args; + Args.reserve(E->getNumArgs()); + bool Invalid = false; + for (CXXTemporaryObjectExpr::arg_iterator Arg = E->arg_begin(), + ArgEnd = E->arg_end(); + Arg != ArgEnd; ++Arg) { + OwningExprResult InstantiatedArg = Visit(*Arg); + if (InstantiatedArg.isInvalid()) { + Invalid = true; + break; + } + + Args.push_back((Expr *)InstantiatedArg.release()); + } + + if (!Invalid) { + SourceLocation CommaLoc; + // FIXME: HACK! + if (Args.size() > 1) + CommaLoc + = SemaRef.PP.getLocForEndOfToken(Args[0]->getSourceRange().getEnd()); + Sema::ExprResult Result + = SemaRef.ActOnCXXTypeConstructExpr(SourceRange(E->getTypeBeginLoc() + /*, FIXME*/), + T.getAsOpaquePtr(), + /*FIXME*/E->getTypeBeginLoc(), + (void**)&Args[0], Args.size(), + /*HACK*/&CommaLoc, + E->getSourceRange().getEnd()); + if (!Result.isInvalid()) + return SemaRef.Owned(Result); + } + + // Clean up the instantiated arguments. + // FIXME: Would rather do this with RAII. + for (unsigned Idx = 0; Idx < Args.size(); ++Idx) + SemaRef.DeleteExpr(Args[Idx]); + + return SemaRef.ExprError(); +} + Sema::OwningExprResult Sema::InstantiateExpr(Expr *E, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs) { diff --git a/test/SemaTemplate/instantiate-expr-1.cpp b/test/SemaTemplate/instantiate-expr-1.cpp index 6fceddf05b..869d558e33 100644 --- a/test/SemaTemplate/instantiate-expr-1.cpp +++ b/test/SemaTemplate/instantiate-expr-1.cpp @@ -43,3 +43,13 @@ void test_BitfieldDivide() { (void)sizeof(BitfieldDivide<5, 1>); (void)sizeof(BitfieldDivide<5, 0>); // expected-note{{in instantiation of template class 'struct BitfieldDivide<5, 0>' requested here}} } + +template +struct BitfieldDep { + int bitfield : I + J; +}; + +void test_BitfieldDep() { + (void)sizeof(BitfieldDep); +} +