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.

llvm-svn: 66947
This commit is contained in:
Douglas Gregor 2009-03-13 21:01:28 +00:00
parent 5b86bf527e
commit 0950e41b73
13 changed files with 230 additions and 62 deletions

View File

@ -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(...))

View File

@ -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<Expr*>(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<Expr*>(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);

View File

@ -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);
}

View File

@ -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) {

View File

@ -833,8 +833,13 @@ SizeOfAlignOfExpr::CreateImpl(Deserializer& D, ASTContext& C) {
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 {

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -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<CXXRecordDecl>(RT->getDecl());

View File

@ -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;

View File

@ -1276,6 +1276,13 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
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;

View File

@ -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<DeclRefExpr>(E->getCallee());
OverloadedFunctionDecl *Overloads
= cast<OverloadedFunctionDecl>(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<Expr *, 16> 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) {

View File

@ -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<typename T, T I, int J>
struct BitfieldDep {
int bitfield : I + J;
};
void test_BitfieldDep() {
(void)sizeof(BitfieldDep<int, 1, 5>);
}