Represent C++ direct initializers as ParenListExprs before semantic analysis

instead of having a special-purpose function.

- ActOnCXXDirectInitializer, which was mostly duplication of
  AddInitializerToDecl (leading e.g. to PR10620, which Eli fixed a few days
  ago), is dropped completely.
- MultiInitializer, which was an ugly hack I added, is dropped again.
- We now have the infrastructure in place to distinguish between
  int x = {1};
  int x({1});
  int x{1};
-- VarDecl now has getInitStyle(), which indicates which of the above was used.
-- CXXConstructExpr now has a flag to indicate that it represents list-
   initialization, although this is not yet used.
- InstantiateInitializer was renamed to SubstInitializer and simplified.
- ActOnParenOrParenListExpr has been replaced by ActOnParenListExpr, which
  always produces a ParenListExpr. Placed that so far failed to convert that
  back to a ParenExpr containing comma operators have been fixed. I'm pretty
  sure I could have made a crashing test case before this.

The end result is a (I hope) considerably cleaner design of initializers.
More importantly, the fact that I can now distinguish between the various
initialization kinds means that I can get the tricky generalized initializer
test cases Johannes Schaub supplied to work. (This is not yet done.)

This commit passed self-host, with the resulting compiler passing the tests. I
hope it doesn't break more complicated code. It's a pretty big change, but one
that I feel is necessary.

llvm-svn: 150318
This commit is contained in:
Sebastian Redl 2012-02-11 23:51:47 +00:00
parent 46afb55177
commit a935179ab7
27 changed files with 407 additions and 683 deletions

View File

@ -681,6 +681,13 @@ public:
/// It is illegal to call this function with SC == None.
static const char *getStorageClassSpecifierString(StorageClass SC);
/// \brief Initialization styles.
enum InitializationStyle {
CInit, ///< C-style initialization with assignment
CallInit, ///< Call-style initialization (C++98)
ListInit ///< Direct list-initialization (C++11)
};
protected:
/// \brief Placeholder type used in Init to denote an unparsed C++ default
/// argument.
@ -706,7 +713,7 @@ private:
unsigned SClass : 3;
unsigned SClassAsWritten : 3;
unsigned ThreadSpecified : 1;
unsigned HasCXXDirectInit : 1;
unsigned InitStyle : 2;
/// \brief Whether this variable is the exception variable in a C++ catch
/// or an Objective-C @catch statement.
@ -728,7 +735,7 @@ private:
/// \brief Whether this variable is (C++0x) constexpr.
unsigned IsConstexpr : 1;
};
enum { NumVarDeclBits = 13 };
enum { NumVarDeclBits = 14 };
friend class ASTDeclReader;
friend class StmtIteratorBase;
@ -756,7 +763,7 @@ protected:
/// Otherwise, the number of function parameter scopes enclosing
/// the function parameter scope in which this parameter was
/// declared.
unsigned ScopeDepthOrObjCQuals : 8;
unsigned ScopeDepthOrObjCQuals : 7;
/// The number of parameters preceding this parameter in the
/// function parameter scope in which it was declared.
@ -1073,16 +1080,27 @@ public:
/// declaration is an integral constant expression.
bool checkInitIsICE() const;
void setCXXDirectInitializer(bool T) { VarDeclBits.HasCXXDirectInit = T; }
void setInitStyle(InitializationStyle Style) {
VarDeclBits.InitStyle = Style;
}
/// hasCXXDirectInitializer - If true, the initializer was a direct
/// initializer, e.g: "int x(1);". The Init expression will be the expression
/// inside the parens or a "ClassType(a,b,c)" class constructor expression for
/// class types. Clients can distinguish between "int x(1);" and "int x=1;"
/// by checking hasCXXDirectInitializer.
/// \brief The style of initialization for this declaration.
///
bool hasCXXDirectInitializer() const {
return VarDeclBits.HasCXXDirectInit;
/// C-style initialization is "int x = 1;". Call-style initialization is
/// a C++98 direct-initializer, e.g. "int x(1);". The Init expression will be
/// the expression inside the parens or a "ClassType(a,b,c)" class constructor
/// expression for class types. List-style initialization is C++11 syntax,
/// e.g. "int x{1};". Clients can distinguish between different forms of
/// initialization by checking this value. In particular, "int x = {1};" is
/// C-style, "int x({1})" is call-style, and "int x{1};" is list-style; the
/// Init expression in all three cases is an InitListExpr.
InitializationStyle getInitStyle() const {
return static_cast<InitializationStyle>(VarDeclBits.InitStyle);
}
/// \brief Whether the initializer is a direct-initializer (list or call).
bool isDirectInit() const {
return getInitStyle() != CInit;
}
/// \brief Determine whether this variable is the exception variable in a

View File

@ -3977,7 +3977,7 @@ class ParenListExpr : public Expr {
public:
ParenListExpr(ASTContext& C, SourceLocation lparenloc, Expr **exprs,
unsigned numexprs, SourceLocation rparenloc, QualType T);
unsigned numexprs, SourceLocation rparenloc);
/// \brief Build an empty paren list.
explicit ParenListExpr(EmptyShell Empty) : Expr(ParenListExprClass, Empty) { }

View File

@ -812,6 +812,7 @@ private:
unsigned NumArgs : 16;
bool Elidable : 1;
bool HadMultipleCandidates : 1;
bool ListInitialization : 1;
bool ZeroInitialization : 1;
unsigned ConstructKind : 2;
Stmt **Args;
@ -822,32 +823,36 @@ protected:
CXXConstructorDecl *d, bool elidable,
Expr **args, unsigned numargs,
bool HadMultipleCandidates,
bool ZeroInitialization = false,
ConstructionKind ConstructKind = CK_Complete,
SourceRange ParenRange = SourceRange());
bool ListInitialization,
bool ZeroInitialization,
ConstructionKind ConstructKind,
SourceRange ParenRange);
/// \brief Construct an empty C++ construction expression.
CXXConstructExpr(StmtClass SC, EmptyShell Empty)
: Expr(SC, Empty), Constructor(0), NumArgs(0), Elidable(0),
HadMultipleCandidates(false), ZeroInitialization(0),
ConstructKind(0), Args(0) { }
: Expr(SC, Empty), Constructor(0), NumArgs(0), Elidable(false),
HadMultipleCandidates(false), ListInitialization(false),
ZeroInitialization(false), ConstructKind(0), Args(0)
{ }
public:
/// \brief Construct an empty C++ construction expression.
explicit CXXConstructExpr(EmptyShell Empty)
: Expr(CXXConstructExprClass, Empty), Constructor(0),
NumArgs(0), Elidable(0), HadMultipleCandidates(false),
ZeroInitialization(0), ConstructKind(0), Args(0) { }
NumArgs(0), Elidable(false), HadMultipleCandidates(false),
ListInitialization(false), ZeroInitialization(false),
ConstructKind(0), Args(0)
{ }
static CXXConstructExpr *Create(ASTContext &C, QualType T,
SourceLocation Loc,
CXXConstructorDecl *D, bool Elidable,
Expr **Args, unsigned NumArgs,
bool HadMultipleCandidates,
bool ZeroInitialization = false,
ConstructionKind ConstructKind = CK_Complete,
SourceRange ParenRange = SourceRange());
bool ListInitialization,
bool ZeroInitialization,
ConstructionKind ConstructKind,
SourceRange ParenRange);
CXXConstructorDecl* getConstructor() const { return Constructor; }
void setConstructor(CXXConstructorDecl *C) { Constructor = C; }
@ -864,6 +869,10 @@ public:
bool hadMultipleCandidates() const { return HadMultipleCandidates; }
void setHadMultipleCandidates(bool V) { HadMultipleCandidates = V; }
/// \brief Whether this constructor call was written as list-initialization.
bool isListInitialization() const { return ListInitialization; }
void setListInitialization(bool V) { ListInitialization = V; }
/// \brief Whether this construction first requires
/// zero-initialization before the initializer is called.
bool requiresZeroInitialization() const { return ZeroInitialization; }

View File

@ -327,6 +327,7 @@ public:
/// \brief The kind of initialization being performed.
enum InitKind {
IK_Direct, ///< Direct initialization
IK_DirectList, ///< Direct list-initialization
IK_Copy, ///< Copy initialization
IK_Default, ///< Default initialization
IK_Value ///< Value initialization
@ -336,6 +337,7 @@ private:
/// \brief The kind of initialization that we're storing.
enum StoredInitKind {
SIK_Direct = IK_Direct, ///< Direct initialization
SIK_DirectList = IK_DirectList, ///< Direct list-initialization
SIK_Copy = IK_Copy, ///< Copy initialization
SIK_Default = IK_Default, ///< Default initialization
SIK_Value = IK_Value, ///< Value initialization
@ -370,6 +372,10 @@ public:
return InitializationKind(SIK_Direct, InitLoc, LParenLoc, RParenLoc);
}
static InitializationKind CreateDirectList(SourceLocation InitLoc) {
return InitializationKind(SIK_DirectList, InitLoc, InitLoc, InitLoc);
}
/// \brief Create a direct initialization due to a cast that isn't a C-style
/// or functional cast.
static InitializationKind CreateCast(SourceRange TypeRange) {

View File

@ -1,72 +0,0 @@
//===--- MultiInitializer.h - Initializer expression group ------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the MultiInitializer class, which can represent a list
// initializer or a parentheses-wrapped group of expressions in a C++ member
// initializer.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_SEMA_MULTIINITIALIZER_H
#define LLVM_CLANG_SEMA_MULTIINITIALIZER_H
#include "clang/Sema/Ownership.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/PointerUnion.h"
namespace clang {
class ASTContext;
class Expr;
class InitializationKind;
class InitializedEntity;
class InitListExpr;
class Sema;
class MultiInitializer {
llvm::PointerUnion<Expr*, Expr**> InitListOrExpressions;
unsigned NumInitializers;
SourceLocation LParenLoc;
SourceLocation RParenLoc;
InitListExpr *getInitList() const;
Expr **getExpressions() const { return InitListOrExpressions.get<Expr**>(); }
public:
MultiInitializer(Expr* InitList)
: InitListOrExpressions(InitList)
{}
MultiInitializer(SourceLocation LParenLoc, Expr **Exprs, unsigned NumInits,
SourceLocation RParenLoc)
: InitListOrExpressions(Exprs), NumInitializers(NumInits),
LParenLoc(LParenLoc), RParenLoc(RParenLoc)
{}
bool isInitializerList() const { return InitListOrExpressions.is<Expr*>(); }
SourceLocation getStartLoc() const;
SourceLocation getEndLoc() const;
typedef Expr **iterator;
iterator begin() const;
iterator end() const;
bool isTypeDependent() const;
bool DiagnoseUnexpandedParameterPack(Sema &SemaRef) const;
// Return the InitListExpr or create a ParenListExpr.
Expr *CreateInitExpr(ASTContext &Ctx, QualType T) const;
ExprResult PerformInit(Sema &SemaRef, InitializedEntity Entity,
InitializationKind Kind) const;
};
}
#endif

View File

@ -22,7 +22,6 @@
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/ExternalSemaSource.h"
#include "clang/Sema/LocInfoType.h"
#include "clang/Sema/MultiInitializer.h"
#include "clang/Sema/TypoCorrection.h"
#include "clang/Sema/Weak.h"
#include "clang/AST/Expr.h"
@ -2387,7 +2386,7 @@ public:
ExprResult ActOnNumericConstant(const Token &Tok);
ExprResult ActOnCharacterConstant(const Token &Tok);
ExprResult ActOnParenExpr(SourceLocation L, SourceLocation R, Expr *E);
ExprResult ActOnParenOrParenListExpr(SourceLocation L,
ExprResult ActOnParenListExpr(SourceLocation L,
SourceLocation R,
MultiExprArg Val);
@ -2769,15 +2768,6 @@ public:
UnqualifiedId &Name,
TypeResult Type);
/// AddCXXDirectInitializerToDecl - This action is called immediately after
/// ActOnDeclarator, when a C++ direct initializer is present.
/// e.g: "int x(1);"
void AddCXXDirectInitializerToDecl(Decl *Dcl,
SourceLocation LParenLoc,
MultiExprArg Exprs,
SourceLocation RParenLoc,
bool TypeMayContainAuto);
/// InitializeVarWithConstructor - Creates an CXXConstructExpr
/// and sets it as the initializer for the the passed in VarDecl.
bool InitializeVarWithConstructor(VarDecl *VD,
@ -3574,21 +3564,21 @@ public:
ParsedType TemplateTypeTy,
const DeclSpec &DS,
SourceLocation IdLoc,
const MultiInitializer &Init,
Expr *Init,
SourceLocation EllipsisLoc);
MemInitResult BuildMemberInitializer(ValueDecl *Member,
const MultiInitializer &Args,
Expr *Init,
SourceLocation IdLoc);
MemInitResult BuildBaseInitializer(QualType BaseType,
TypeSourceInfo *BaseTInfo,
const MultiInitializer &Args,
Expr *Init,
CXXRecordDecl *ClassDecl,
SourceLocation EllipsisLoc);
MemInitResult BuildDelegatingInitializer(TypeSourceInfo *TInfo,
const MultiInitializer &Args,
Expr *Init,
CXXRecordDecl *ClassDecl);
bool SetDelegatingInitializer(CXXConstructorDecl *Constructor,
@ -5186,6 +5176,10 @@ public:
Decl *SubstDecl(Decl *D, DeclContext *Owner,
const MultiLevelTemplateArgumentList &TemplateArgs);
ExprResult SubstInitializer(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs,
bool CXXDirectInit);
bool
SubstBaseSpecifiers(CXXRecordDecl *Instantiation,
CXXRecordDecl *Pattern,
@ -5259,11 +5253,6 @@ public:
void InstantiateMemInitializers(CXXConstructorDecl *New,
const CXXConstructorDecl *Tmpl,
const MultiLevelTemplateArgumentList &TemplateArgs);
bool InstantiateInitializer(Expr *Init,
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation &LParenLoc,
ASTOwningVector<Expr*> &NewArgs,
SourceLocation &RParenLoc);
NamedDecl *FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
const MultiLevelTemplateArgumentList &TemplateArgs);

View File

@ -621,17 +621,21 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
Out << Name;
Expr *Init = D->getInit();
if (!Policy.SuppressInitializers && Init) {
if (D->hasCXXDirectInitializer())
bool ImplicitInit = false;
if (CXXConstructExpr *Construct = dyn_cast<CXXConstructExpr>(Init))
ImplicitInit = D->getInitStyle() == VarDecl::CallInit &&
Construct->getNumArgs() == 0 && !Construct->isListInitialization();
if (!ImplicitInit) {
if (D->getInitStyle() == VarDecl::CallInit)
Out << "(";
else {
CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(Init);
if (!CCE || CCE->getConstructor()->isCopyOrMoveConstructor())
else if (D->getInitStyle() == VarDecl::CInit) {
Out << " = ";
}
Init->printPretty(Out, Context, 0, Policy, Indentation);
if (D->hasCXXDirectInitializer())
if (D->getInitStyle() == VarDecl::CallInit)
Out << ")";
}
}
prettyPrintAttributes(D);
}

View File

@ -461,7 +461,13 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
if (D->getStorageClass() != SC_None)
set("storage",
VarDecl::getStorageClassSpecifierString(D->getStorageClass()));
setFlag("directinit", D->hasCXXDirectInitializer());
StringRef initStyle = "";
switch (D->getInitStyle()) {
case VarDecl::CInit: initStyle = "c"; break;
case VarDecl::CallInit: initStyle = "call"; break;
case VarDecl::ListInit: initStyle = "list"; break;
}
set("initstyle", initStyle);
setFlag("nrvo", D->isNRVOVariable());
// TODO: instantiation, etc.
}

View File

@ -3414,11 +3414,10 @@ void DesignatedInitExpr::ExpandDesignator(ASTContext &C, unsigned Idx,
ParenListExpr::ParenListExpr(ASTContext& C, SourceLocation lparenloc,
Expr **exprs, unsigned nexprs,
SourceLocation rparenloc, QualType T)
: Expr(ParenListExprClass, T, VK_RValue, OK_Ordinary,
SourceLocation rparenloc)
: Expr(ParenListExprClass, QualType(), VK_RValue, OK_Ordinary,
false, false, false, false),
NumExprs(nexprs), LParenLoc(lparenloc), RParenLoc(rparenloc) {
assert(!T.isNull() && "ParenListExpr must have a valid type");
Exprs = new (C) Stmt*[nexprs];
for (unsigned i = 0; i != nexprs; ++i) {
if (exprs[i]->isTypeDependent())

View File

@ -653,7 +653,7 @@ CXXTemporaryObjectExpr::CXXTemporaryObjectExpr(ASTContext &C,
Type->getType().getNonReferenceType(),
Type->getTypeLoc().getBeginLoc(),
Cons, false, Args, NumArgs,
HadMultipleCandidates, ZeroInitialization,
HadMultipleCandidates, /*FIXME*/false, ZeroInitialization,
CXXConstructExpr::CK_Complete, parenRange),
Type(Type) {
}
@ -668,13 +668,15 @@ CXXConstructExpr *CXXConstructExpr::Create(ASTContext &C, QualType T,
CXXConstructorDecl *D, bool Elidable,
Expr **Args, unsigned NumArgs,
bool HadMultipleCandidates,
bool ListInitialization,
bool ZeroInitialization,
ConstructionKind ConstructKind,
SourceRange ParenRange) {
return new (C) CXXConstructExpr(C, CXXConstructExprClass, T, Loc, D,
Elidable, Args, NumArgs,
HadMultipleCandidates, ZeroInitialization,
ConstructKind, ParenRange);
HadMultipleCandidates, ListInitialization,
ZeroInitialization, ConstructKind,
ParenRange);
}
CXXConstructExpr::CXXConstructExpr(ASTContext &C, StmtClass SC, QualType T,
@ -682,6 +684,7 @@ CXXConstructExpr::CXXConstructExpr(ASTContext &C, StmtClass SC, QualType T,
CXXConstructorDecl *D, bool elidable,
Expr **args, unsigned numargs,
bool HadMultipleCandidates,
bool ListInitialization,
bool ZeroInitialization,
ConstructionKind ConstructKind,
SourceRange ParenRange)
@ -691,6 +694,7 @@ CXXConstructExpr::CXXConstructExpr(ASTContext &C, StmtClass SC, QualType T,
T->containsUnexpandedParameterPack()),
Constructor(D), Loc(Loc), ParenRange(ParenRange), NumArgs(numargs),
Elidable(elidable), HadMultipleCandidates(HadMultipleCandidates),
ListInitialization(ListInitialization),
ZeroInitialization(ZeroInitialization),
ConstructKind(ConstructKind), Args(0)
{

View File

@ -2741,6 +2741,7 @@ CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction(
CXXConstExpr->isElidable(),
&ConstructorArgs[0], ConstructorArgs.size(),
CXXConstExpr->hadMultipleCandidates(),
CXXConstExpr->isListInitialization(),
CXXConstExpr->requiresZeroInitialization(),
CXXConstExpr->getConstructionKind(), SourceRange());

View File

@ -1344,10 +1344,11 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D,
ExitScope();
}
Actions.AddCXXDirectInitializerToDecl(ThisDecl, T.getOpenLocation(),
move_arg(Exprs),
ExprResult Initializer = Actions.ActOnParenListExpr(T.getOpenLocation(),
T.getCloseLocation(),
TypeContainsAuto);
move_arg(Exprs));
Actions.AddInitializerToDecl(ThisDecl, Initializer.take(),
/*DirectInit=*/true, TypeContainsAuto);
}
} else if (getLang().CPlusPlus0x && Tok.is(tok::l_brace)) {
// Parse C++0x braced-init-list.

View File

@ -2006,7 +2006,7 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
if (!ParseExpressionList(ArgExprs, CommaLocs)) {
ExprType = SimpleExpr;
Result = Actions.ActOnParenOrParenListExpr(OpenLoc, Tok.getLocation(),
Result = Actions.ActOnParenListExpr(OpenLoc, Tok.getLocation(),
move_arg(ArgExprs));
}
} else {

View File

@ -8,7 +8,6 @@ add_clang_library(clangSema
DelayedDiagnostic.cpp
IdentifierResolver.cpp
JumpDiagnostics.cpp
MultiInitializer.cpp
Scope.cpp
Sema.cpp
SemaAccess.cpp

View File

@ -1,93 +0,0 @@
//===--- MultiInitializer.cpp - Initializer expression group ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the MultiInitializer class, which can represent a list
// initializer or a parentheses-wrapped group of expressions in a C++ member
// initializer.
//
//===----------------------------------------------------------------------===//
#include "clang/Sema/MultiInitializer.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Sema.h"
#include "clang/AST/Expr.h"
using namespace clang;
InitListExpr *MultiInitializer::getInitList() const {
return cast<InitListExpr>(InitListOrExpressions.get<Expr*>());
}
SourceLocation MultiInitializer::getStartLoc() const {
return isInitializerList() ? getInitList()->getLBraceLoc() : LParenLoc;
}
SourceLocation MultiInitializer::getEndLoc() const {
return isInitializerList() ? getInitList()->getRBraceLoc() : RParenLoc;
}
MultiInitializer::iterator MultiInitializer::begin() const {
return isInitializerList() ? getInitList()->getInits() : getExpressions();
}
MultiInitializer::iterator MultiInitializer::end() const {
if (isInitializerList()) {
InitListExpr *ILE = getInitList();
return ILE->getInits() + ILE->getNumInits();
}
return getExpressions() + NumInitializers;
}
bool MultiInitializer::isTypeDependent() const {
if (isInitializerList())
return getInitList()->isTypeDependent();
for (iterator I = begin(), E = end(); I != E; ++I) {
if ((*I)->isTypeDependent())
return true;
}
return false;
}
bool MultiInitializer::DiagnoseUnexpandedParameterPack(Sema &SemaRef) const {
if (isInitializerList())
return SemaRef.DiagnoseUnexpandedParameterPack(getInitList(),
Sema::UPPC_Initializer);
for (iterator I = begin(), E = end(); I != E; ++I) {
if (SemaRef.DiagnoseUnexpandedParameterPack(*I, Sema::UPPC_Initializer))
return true;
}
return false;
}
Expr *MultiInitializer::CreateInitExpr(ASTContext &Ctx, QualType T) const {
if (isInitializerList())
return InitListOrExpressions.get<Expr*>();
return new (Ctx) ParenListExpr(Ctx, LParenLoc, getExpressions(),
NumInitializers, RParenLoc, T);
}
ExprResult MultiInitializer::PerformInit(Sema &SemaRef,
InitializedEntity Entity,
InitializationKind Kind) const {
Expr *Single;
Expr **Args;
unsigned NumArgs;
if (isInitializerList()) {
Single = InitListOrExpressions.get<Expr*>();
Args = &Single;
NumArgs = 1;
} else {
Args = getExpressions();
NumArgs = NumInitializers;
}
InitializationSequence InitSeq(SemaRef, Entity, Kind, Args, NumArgs);
return InitSeq.Perform(SemaRef, Entity, Kind,
MultiExprArg(SemaRef, Args, NumArgs), 0);
}

View File

@ -2139,9 +2139,8 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod,
/// emitting diagnostics as appropriate.
///
/// Declarations using the auto type specifier (C++ [decl.spec.auto]) call back
/// to here in AddInitializerToDecl and AddCXXDirectInitializerToDecl. We can't
/// check them before the initializer is attached.
///
/// to here in AddInitializerToDecl. We can't check them before the initializer
/// is attached.
void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old) {
if (New->isInvalidDecl() || Old->isInvalidDecl())
return;
@ -5983,17 +5982,6 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
if (RealDecl == 0 || RealDecl->isInvalidDecl())
return;
// Check for self-references within variable initializers.
if (VarDecl *vd = dyn_cast<VarDecl>(RealDecl)) {
// Variables declared within a function/method body are handled
// by a dataflow analysis.
if (!vd->hasLocalStorage() && !vd->isStaticLocal())
CheckSelfReference(RealDecl, Init);
}
else {
CheckSelfReference(RealDecl, Init);
}
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(RealDecl)) {
// With declarators parsed the way they are, the parser cannot
// distinguish between a normal initializer and a pure-specifier.
@ -6018,12 +6006,44 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
return;
}
// Check for self-references within variable initializers.
// Variables declared within a function/method body are handled
// by a dataflow analysis.
if (!VDecl->hasLocalStorage() && !VDecl->isStaticLocal())
CheckSelfReference(RealDecl, Init);
ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init);
// C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for.
if (TypeMayContainAuto && VDecl->getType()->getContainedAutoType()) {
Expr *DeduceInit = Init;
// Initializer could be a C++ direct-initializer. Deduction only works if it
// contains exactly one expression.
if (CXXDirectInit) {
if (CXXDirectInit->getNumExprs() == 0) {
// It isn't possible to write this directly, but it is possible to
// end up in this situation with "auto x(some_pack...);"
Diag(CXXDirectInit->getSourceRange().getBegin(),
diag::err_auto_var_init_no_expression)
<< VDecl->getDeclName() << VDecl->getType()
<< VDecl->getSourceRange();
RealDecl->setInvalidDecl();
return;
} else if (CXXDirectInit->getNumExprs() > 1) {
Diag(CXXDirectInit->getExpr(1)->getSourceRange().getBegin(),
diag::err_auto_var_init_multiple_expressions)
<< VDecl->getDeclName() << VDecl->getType()
<< VDecl->getSourceRange();
RealDecl->setInvalidDecl();
return;
} else {
DeduceInit = CXXDirectInit->getExpr(0);
}
}
TypeSourceInfo *DeducedType = 0;
if (DeduceAutoType(VDecl->getTypeSourceInfo(), Init, DeducedType) ==
if (DeduceAutoType(VDecl->getTypeSourceInfo(), DeduceInit, DeducedType) ==
DAR_Failed)
DiagnoseAutoDeductionFailure(VDecl, Init);
DiagnoseAutoDeductionFailure(VDecl, DeduceInit);
if (!DeducedType) {
RealDecl->setInvalidDecl();
return;
@ -6048,10 +6068,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
return;
}
if (!VDecl->getType()->isDependentType()) {
// A definition must end up with a complete type, which means it must be
// complete with the restriction that an array type might be completed by the
// initializer; note that later code assumes this restriction.
// complete with the restriction that an array type might be completed by
// the initializer; note that later code assumes this restriction.
QualType BaseDeclType = VDecl->getType();
if (const ArrayType *Array = Context.getAsIncompleteArrayType(BaseDeclType))
BaseDeclType = Array->getElementType();
@ -6066,6 +6086,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
diag::err_abstract_type_in_decl,
AbstractVariableType))
VDecl->setInvalidDecl();
}
const VarDecl *Def;
if ((Def = VDecl->getDefinition()) && Def != VDecl) {
@ -6128,9 +6149,15 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
: InitializationKind::CreateCopy(VDecl->getLocation(),
Init->getLocStart());
InitializationSequence InitSeq(*this, Entity, Kind, &Init, 1);
Expr **Args = &Init;
unsigned NumArgs = 1;
if (CXXDirectInit) {
Args = CXXDirectInit->getExprs();
NumArgs = CXXDirectInit->getNumExprs();
}
InitializationSequence InitSeq(*this, Entity, Kind, Args, NumArgs);
ExprResult Result = InitSeq.Perform(*this, Entity, Kind,
MultiExprArg(*this, &Init, 1),
MultiExprArg(*this, Args,NumArgs),
&DclT);
if (Result.isInvalid()) {
VDecl->setInvalidDecl();
@ -6266,6 +6293,28 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
CheckForConstantInitializer(Init, DclT);
}
// We will represent direct-initialization similarly to copy-initialization:
// int x(1); -as-> int x = 1;
// ClassType x(a,b,c); -as-> ClassType x = ClassType(a,b,c);
//
// Clients that want to distinguish between the two forms, can check for
// direct initializer using VarDecl::getInitStyle().
// A major benefit is that clients that don't particularly care about which
// exactly form was it (like the CodeGen) can handle both cases without
// special case code.
// C++ 8.5p11:
// The form of initialization (using parentheses or '=') is generally
// insignificant, but does matter when the entity being initialized has a
// class type.
if (CXXDirectInit) {
assert(DirectInit && "Call-style initializer must be direct init.");
VDecl->setInitStyle(VarDecl::CallInit);
} else if (DirectInit) {
// This must be list-initialization. No other way is direct-initialization.
VDecl->setInitStyle(VarDecl::ListInit);
}
CheckCompleteVariableDeclaration(VDecl);
}
@ -6496,8 +6545,11 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl,
MultiExprArg(*this, 0, 0));
if (Init.isInvalid())
Var->setInvalidDecl();
else if (Init.get())
else if (Init.get()) {
Var->setInit(MaybeCreateExprWithCleanups(Init.get()));
// This is important for template substitution.
Var->setInitStyle(VarDecl::CallInit);
}
CheckCompleteVariableDeclaration(Var);
}

View File

@ -1768,7 +1768,7 @@ Sema::ActOnMemInitializer(Decl *ConstructorD,
Expr *InitList,
SourceLocation EllipsisLoc) {
return BuildMemInitializer(ConstructorD, S, SS, MemberOrBase, TemplateTypeTy,
DS, IdLoc, MultiInitializer(InitList),
DS, IdLoc, InitList,
EllipsisLoc);
}
@ -1785,10 +1785,10 @@ Sema::ActOnMemInitializer(Decl *ConstructorD,
Expr **Args, unsigned NumArgs,
SourceLocation RParenLoc,
SourceLocation EllipsisLoc) {
Expr *List = new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs,
RParenLoc);
return BuildMemInitializer(ConstructorD, S, SS, MemberOrBase, TemplateTypeTy,
DS, IdLoc, MultiInitializer(LParenLoc, Args,
NumArgs, RParenLoc),
EllipsisLoc);
DS, IdLoc, List, EllipsisLoc);
}
namespace {
@ -1825,7 +1825,7 @@ Sema::BuildMemInitializer(Decl *ConstructorD,
ParsedType TemplateTypeTy,
const DeclSpec &DS,
SourceLocation IdLoc,
const MultiInitializer &Args,
Expr *Init,
SourceLocation EllipsisLoc) {
if (!ConstructorD)
return true;
@ -1864,9 +1864,10 @@ Sema::BuildMemInitializer(Decl *ConstructorD,
(Member = dyn_cast<IndirectFieldDecl>(*Result.first))) {
if (EllipsisLoc.isValid())
Diag(EllipsisLoc, diag::err_pack_expansion_member_init)
<< MemberOrBase << SourceRange(IdLoc, Args.getEndLoc());
<< MemberOrBase
<< SourceRange(IdLoc, Init->getSourceRange().getEnd());
return BuildMemberInitializer(Member, Args, IdLoc);
return BuildMemberInitializer(Member, Init, IdLoc);
}
}
}
@ -1927,7 +1928,7 @@ Sema::BuildMemInitializer(Decl *ConstructorD,
Diag(Member->getLocation(), diag::note_previous_decl)
<< CorrectedQuotedStr;
return BuildMemberInitializer(Member, Args, IdLoc);
return BuildMemberInitializer(Member, Init, IdLoc);
} else if (TypeDecl *Type = Corr.getCorrectionDeclAs<TypeDecl>()) {
const CXXBaseSpecifier *DirectBaseSpec;
const CXXBaseSpecifier *VirtualBaseSpec;
@ -1955,7 +1956,7 @@ Sema::BuildMemInitializer(Decl *ConstructorD,
if (!TyD && BaseType.isNull()) {
Diag(IdLoc, diag::err_mem_init_not_member_or_class)
<< MemberOrBase << SourceRange(IdLoc, Args.getEndLoc());
<< MemberOrBase << SourceRange(IdLoc,Init->getSourceRange().getEnd());
return true;
}
}
@ -1975,7 +1976,7 @@ Sema::BuildMemInitializer(Decl *ConstructorD,
if (!TInfo)
TInfo = Context.getTrivialTypeSourceInfo(BaseType, IdLoc);
return BuildBaseInitializer(BaseType, TInfo, Args, ClassDecl, EllipsisLoc);
return BuildBaseInitializer(BaseType, TInfo, Init, ClassDecl, EllipsisLoc);
}
/// Checks a member initializer expression for cases where reference (or
@ -2102,15 +2103,14 @@ static bool InitExprContainsUninitializedFields(const Stmt *S,
}
MemInitResult
Sema::BuildMemberInitializer(ValueDecl *Member,
const MultiInitializer &Args,
Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init,
SourceLocation IdLoc) {
FieldDecl *DirectMember = dyn_cast<FieldDecl>(Member);
IndirectFieldDecl *IndirectMember = dyn_cast<IndirectFieldDecl>(Member);
assert((DirectMember || IndirectMember) &&
"Member must be a FieldDecl or IndirectFieldDecl");
if (Args.DiagnoseUnexpandedParameterPack(*this))
if (DiagnoseUnexpandedParameterPack(Init, UPPC_Initializer))
return true;
if (Member->isInvalidDecl())
@ -2120,13 +2120,19 @@ Sema::BuildMemberInitializer(ValueDecl *Member,
// foo(foo)
// where foo is not also a parameter to the constructor.
// TODO: implement -Wuninitialized and fold this into that framework.
for (MultiInitializer::iterator I = Args.begin(), E = Args.end();
I != E; ++I) {
Expr **Args;
unsigned NumArgs;
if (ParenListExpr *ParenList = dyn_cast<ParenListExpr>(Init)) {
Args = ParenList->getExprs();
NumArgs = ParenList->getNumExprs();
} else {
InitListExpr *InitList = cast<InitListExpr>(Init);
Args = InitList->getInits();
NumArgs = InitList->getNumInits();
}
for (unsigned i = 0; i < NumArgs; ++i) {
SourceLocation L;
Expr *Arg = *I;
if (DesignatedInitExpr *DIE = dyn_cast<DesignatedInitExpr>(Arg))
Arg = DIE->getInit();
if (InitExprContainsUninitializedFields(Arg, Member, &L)) {
if (InitExprContainsUninitializedFields(Args[i], Member, &L)) {
// FIXME: Return true in the case when other fields are used before being
// uninitialized. For example, let this field be the i'th field. When
// initializing the i'th field, throw a warning if any of the >= i'th
@ -2137,14 +2143,11 @@ Sema::BuildMemberInitializer(ValueDecl *Member,
}
}
bool HasDependentArg = Args.isTypeDependent();
SourceRange InitRange = Init->getSourceRange();
Expr *Init;
if (Member->getType()->isDependentType() || HasDependentArg) {
if (Member->getType()->isDependentType() || Init->isTypeDependent()) {
// Can't check initialization for a member of dependent type or when
// any of the arguments are type-dependent expressions.
Init = Args.CreateInitExpr(Context,Member->getType().getNonReferenceType());
DiscardCleanupsInEvaluationContext();
} else {
// Initialize the member.
@ -2152,14 +2155,22 @@ Sema::BuildMemberInitializer(ValueDecl *Member,
DirectMember ? InitializedEntity::InitializeMember(DirectMember, 0)
: InitializedEntity::InitializeMember(IndirectMember, 0);
InitializationKind Kind =
InitializationKind::CreateDirect(IdLoc, Args.getStartLoc(),
Args.getEndLoc());
InitializationKind::CreateDirect(IdLoc, InitRange.getBegin(),
InitRange.getEnd());
ExprResult MemberInit = Args.PerformInit(*this, MemberEntity, Kind);
if (isa<InitListExpr>(Init)) {
Args = &Init;
NumArgs = 1;
}
InitializationSequence InitSeq(*this, MemberEntity, Kind, Args, NumArgs);
ExprResult MemberInit = InitSeq.Perform(*this, MemberEntity, Kind,
MultiExprArg(*this, Args, NumArgs),
0);
if (MemberInit.isInvalid())
return true;
CheckImplicitConversions(MemberInit.get(), Args.getStartLoc());
CheckImplicitConversions(MemberInit.get(),
InitRange.getBegin());
// C++0x [class.base.init]p7:
// The initialization of each base and member constitutes a
@ -2170,14 +2181,13 @@ Sema::BuildMemberInitializer(ValueDecl *Member,
// If we are in a dependent context, template instantiation will
// perform this type-checking again. Just save the arguments that we
// received in a ParenListExpr.
// received.
// FIXME: This isn't quite ideal, since our ASTs don't capture all
// of the information that we have about the member
// initializer. However, deconstructing the ASTs is a dicey process,
// and this approach is far more likely to get the corner cases right.
if (CurContext->isDependentContext()) {
Init = Args.CreateInitExpr(Context,
Member->getType().getNonReferenceType());
// The existing Init will do fine.
} else {
Init = MemberInit.get();
CheckForDanglingReferenceOrPointer(*this, Member, Init, IdLoc);
@ -2185,19 +2195,18 @@ Sema::BuildMemberInitializer(ValueDecl *Member,
}
if (DirectMember) {
return new (Context) CXXCtorInitializer(Context, DirectMember,
IdLoc, Args.getStartLoc(),
Init, Args.getEndLoc());
return new (Context) CXXCtorInitializer(Context, DirectMember, IdLoc,
InitRange.getBegin(), Init,
InitRange.getEnd());
} else {
return new (Context) CXXCtorInitializer(Context, IndirectMember,
IdLoc, Args.getStartLoc(),
Init, Args.getEndLoc());
return new (Context) CXXCtorInitializer(Context, IndirectMember, IdLoc,
InitRange.getBegin(), Init,
InitRange.getEnd());
}
}
MemInitResult
Sema::BuildDelegatingInitializer(TypeSourceInfo *TInfo,
const MultiInitializer &Args,
Sema::BuildDelegatingInitializer(TypeSourceInfo *TInfo, Expr *Init,
CXXRecordDecl *ClassDecl) {
SourceLocation NameLoc = TInfo->getTypeLoc().getLocalSourceRange().getBegin();
if (!LangOpts.CPlusPlus0x)
@ -2205,21 +2214,31 @@ Sema::BuildDelegatingInitializer(TypeSourceInfo *TInfo,
<< TInfo->getTypeLoc().getLocalSourceRange();
Diag(NameLoc, diag::warn_cxx98_compat_delegating_ctor);
SourceRange InitRange = Init->getSourceRange();
// Initialize the object.
InitializedEntity DelegationEntity = InitializedEntity::InitializeDelegation(
QualType(ClassDecl->getTypeForDecl(), 0));
InitializationKind Kind =
InitializationKind::CreateDirect(NameLoc, Args.getStartLoc(),
Args.getEndLoc());
InitializationKind::CreateDirect(NameLoc, InitRange.getBegin(),
InitRange.getEnd());
ExprResult DelegationInit = Args.PerformInit(*this, DelegationEntity, Kind);
Expr **Args = &Init;
unsigned NumArgs = 1;
if (ParenListExpr *ParenList = dyn_cast<ParenListExpr>(Init)) {
Args = ParenList->getExprs();
NumArgs = ParenList->getNumExprs();
}
InitializationSequence InitSeq(*this, DelegationEntity, Kind, Args, NumArgs);
ExprResult DelegationInit = InitSeq.Perform(*this, DelegationEntity, Kind,
MultiExprArg(*this, Args,NumArgs),
0);
if (DelegationInit.isInvalid())
return true;
assert(cast<CXXConstructExpr>(DelegationInit.get())->getConstructor() &&
"Delegating constructor with no target?");
CheckImplicitConversions(DelegationInit.get(), Args.getStartLoc());
CheckImplicitConversions(DelegationInit.get(), InitRange.getBegin());
// C++0x [class.base.init]p7:
// The initialization of each base and member constitutes a
@ -2228,18 +2247,15 @@ Sema::BuildDelegatingInitializer(TypeSourceInfo *TInfo,
if (DelegationInit.isInvalid())
return true;
return new (Context) CXXCtorInitializer(Context, TInfo, Args.getStartLoc(),
return new (Context) CXXCtorInitializer(Context, TInfo, InitRange.getBegin(),
DelegationInit.takeAs<Expr>(),
Args.getEndLoc());
InitRange.getEnd());
}
MemInitResult
Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo,
const MultiInitializer &Args,
CXXRecordDecl *ClassDecl,
Expr *Init, CXXRecordDecl *ClassDecl,
SourceLocation EllipsisLoc) {
bool HasDependentArg = Args.isTypeDependent();
SourceLocation BaseLoc
= BaseTInfo->getTypeLoc().getLocalSourceRange().getBegin();
@ -2253,13 +2269,14 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo,
// of that class, the mem-initializer is ill-formed. A
// mem-initializer-list can initialize a base class using any
// name that denotes that base class type.
bool Dependent = BaseType->isDependentType() || HasDependentArg;
bool Dependent = BaseType->isDependentType() || Init->isTypeDependent();
SourceRange InitRange = Init->getSourceRange();
if (EllipsisLoc.isValid()) {
// This is a pack expansion.
if (!BaseType->containsUnexpandedParameterPack()) {
Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
<< SourceRange(BaseLoc, Args.getEndLoc());
<< SourceRange(BaseLoc, InitRange.getEnd());
EllipsisLoc = SourceLocation();
}
@ -2268,7 +2285,7 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo,
if (DiagnoseUnexpandedParameterPack(BaseLoc, BaseTInfo, UPPC_Initializer))
return true;
if (Args.DiagnoseUnexpandedParameterPack(*this))
if (DiagnoseUnexpandedParameterPack(Init, UPPC_Initializer))
return true;
}
@ -2278,7 +2295,7 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo,
if (!Dependent) {
if (Context.hasSameUnqualifiedType(QualType(ClassDecl->getTypeForDecl(),0),
BaseType))
return BuildDelegatingInitializer(BaseTInfo, Args, ClassDecl);
return BuildDelegatingInitializer(BaseTInfo, Init, ClassDecl);
FindBaseInitializer(*this, ClassDecl, BaseType, DirectBaseSpec,
VirtualBaseSpec);
@ -2303,16 +2320,12 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo,
}
if (Dependent) {
// Can't check initialization for a base of dependent type or when
// any of the arguments are type-dependent expressions.
Expr *BaseInit = Args.CreateInitExpr(Context, BaseType);
DiscardCleanupsInEvaluationContext();
return new (Context) CXXCtorInitializer(Context, BaseTInfo,
/*IsVirtual=*/false,
Args.getStartLoc(), BaseInit,
Args.getEndLoc(), EllipsisLoc);
InitRange.getBegin(), Init,
InitRange.getEnd(), EllipsisLoc);
}
// C++ [base.class.init]p2:
@ -2323,8 +2336,7 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo,
return Diag(BaseLoc, diag::err_base_init_direct_and_virtual)
<< BaseType << BaseTInfo->getTypeLoc().getLocalSourceRange();
CXXBaseSpecifier *BaseSpec
= const_cast<CXXBaseSpecifier *>(DirectBaseSpec);
CXXBaseSpecifier *BaseSpec = const_cast<CXXBaseSpecifier *>(DirectBaseSpec);
if (!BaseSpec)
BaseSpec = const_cast<CXXBaseSpecifier *>(VirtualBaseSpec);
@ -2332,14 +2344,23 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo,
InitializedEntity BaseEntity =
InitializedEntity::InitializeBase(Context, BaseSpec, VirtualBaseSpec);
InitializationKind Kind =
InitializationKind::CreateDirect(BaseLoc, Args.getStartLoc(),
Args.getEndLoc());
InitializationKind::CreateDirect(BaseLoc, InitRange.getBegin(),
InitRange.getEnd());
ExprResult BaseInit = Args.PerformInit(*this, BaseEntity, Kind);
Expr **Args = &Init;
unsigned NumArgs = 1;
if (ParenListExpr *ParenList = dyn_cast<ParenListExpr>(Init)) {
Args = ParenList->getExprs();
NumArgs = ParenList->getNumExprs();
}
InitializationSequence InitSeq(*this, BaseEntity, Kind, Args, NumArgs);
ExprResult BaseInit = InitSeq.Perform(*this, BaseEntity, Kind,
MultiExprArg(*this, Args, NumArgs),
0);
if (BaseInit.isInvalid())
return true;
CheckImplicitConversions(BaseInit.get(), Args.getStartLoc());
CheckImplicitConversions(BaseInit.get(), InitRange.getBegin());
// C++0x [class.base.init]p7:
// The initialization of each base and member constitutes a
@ -2356,13 +2377,13 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo,
// initializer. However, deconstructing the ASTs is a dicey process,
// and this approach is far more likely to get the corner cases right.
if (CurContext->isDependentContext())
BaseInit = Owned(Args.CreateInitExpr(Context, BaseType));
BaseInit = Owned(Init);
return new (Context) CXXCtorInitializer(Context, BaseTInfo,
BaseSpec->isVirtual(),
Args.getStartLoc(),
InitRange.getBegin(),
BaseInit.takeAs<Expr>(),
Args.getEndLoc(), EllipsisLoc);
InitRange.getEnd(), EllipsisLoc);
}
// Create a static_cast\<T&&>(expr).
@ -9064,7 +9085,8 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
MarkFunctionReferenced(ConstructLoc, Constructor);
return Owned(CXXConstructExpr::Create(Context, DeclInitType, ConstructLoc,
Constructor, Elidable, Exprs, NumExprs,
HadMultipleCandidates, RequiresZeroInit,
HadMultipleCandidates, /*FIXME*/false,
RequiresZeroInit,
static_cast<CXXConstructExpr::ConstructionKind>(ConstructKind),
ParenRange));
}
@ -9116,187 +9138,6 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) {
Diag(VD->getLocation(), diag::warn_global_destructor);
}
/// AddCXXDirectInitializerToDecl - This action is called immediately after
/// ActOnDeclarator, when a C++ direct initializer is present.
/// e.g: "int x(1);"
void Sema::AddCXXDirectInitializerToDecl(Decl *RealDecl,
SourceLocation LParenLoc,
MultiExprArg Exprs,
SourceLocation RParenLoc,
bool TypeMayContainAuto) {
// If there is no declaration, there was an error parsing it. Just ignore
// the initializer.
if (RealDecl == 0)
return;
VarDecl *VDecl = dyn_cast<VarDecl>(RealDecl);
if (!VDecl) {
Diag(RealDecl->getLocation(), diag::err_illegal_initializer);
RealDecl->setInvalidDecl();
return;
}
// C++0x [dcl.spec.auto]p6. Deduce the type which 'auto' stands in for.
if (TypeMayContainAuto && VDecl->getType()->getContainedAutoType()) {
if (Exprs.size() == 0) {
// It isn't possible to write this directly, but it is possible to
// end up in this situation with "auto x(some_pack...);"
Diag(LParenLoc, diag::err_auto_var_init_no_expression)
<< VDecl->getDeclName() << VDecl->getType()
<< VDecl->getSourceRange();
RealDecl->setInvalidDecl();
return;
}
if (Exprs.size() > 1) {
Diag(Exprs.get()[1]->getSourceRange().getBegin(),
diag::err_auto_var_init_multiple_expressions)
<< VDecl->getDeclName() << VDecl->getType()
<< VDecl->getSourceRange();
RealDecl->setInvalidDecl();
return;
}
Expr *Init = Exprs.get()[0];
TypeSourceInfo *DeducedType = 0;
if (DeduceAutoType(VDecl->getTypeSourceInfo(), Init, DeducedType) ==
DAR_Failed)
DiagnoseAutoDeductionFailure(VDecl, Init);
if (!DeducedType) {
RealDecl->setInvalidDecl();
return;
}
VDecl->setTypeSourceInfo(DeducedType);
VDecl->setType(DeducedType->getType());
// In ARC, infer lifetime.
if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(VDecl))
VDecl->setInvalidDecl();
// If this is a redeclaration, check that the type we just deduced matches
// the previously declared type.
if (VarDecl *Old = VDecl->getPreviousDecl())
MergeVarDeclTypes(VDecl, Old);
}
// We will represent direct-initialization similarly to copy-initialization:
// int x(1); -as-> int x = 1;
// ClassType x(a,b,c); -as-> ClassType x = ClassType(a,b,c);
//
// Clients that want to distinguish between the two forms, can check for
// direct initializer using VarDecl::hasCXXDirectInitializer().
// A major benefit is that clients that don't particularly care about which
// exactly form was it (like the CodeGen) can handle both cases without
// special case code.
// C++ 8.5p11:
// The form of initialization (using parentheses or '=') is generally
// insignificant, but does matter when the entity being initialized has a
// class type.
if (!VDecl->getType()->isDependentType() &&
!VDecl->getType()->isIncompleteArrayType() &&
RequireCompleteType(VDecl->getLocation(), VDecl->getType(),
diag::err_typecheck_decl_incomplete_type)) {
VDecl->setInvalidDecl();
return;
}
// The variable can not have an abstract class type.
if (RequireNonAbstractType(VDecl->getLocation(), VDecl->getType(),
diag::err_abstract_type_in_decl,
AbstractVariableType))
VDecl->setInvalidDecl();
const VarDecl *Def;
if ((Def = VDecl->getDefinition()) && Def != VDecl) {
Diag(VDecl->getLocation(), diag::err_redefinition)
<< VDecl->getDeclName();
Diag(Def->getLocation(), diag::note_previous_definition);
VDecl->setInvalidDecl();
return;
}
// C++ [class.static.data]p4
// If a static data member is of const integral or const
// enumeration type, its declaration in the class definition can
// specify a constant-initializer which shall be an integral
// constant expression (5.19). In that case, the member can appear
// in integral constant expressions. The member shall still be
// defined in a namespace scope if it is used in the program and the
// namespace scope definition shall not contain an initializer.
//
// We already performed a redefinition check above, but for static
// data members we also need to check whether there was an in-class
// declaration with an initializer.
const VarDecl* PrevInit = 0;
if (VDecl->isStaticDataMember() && VDecl->getAnyInitializer(PrevInit)) {
Diag(VDecl->getLocation(), diag::err_redefinition) << VDecl->getDeclName();
Diag(PrevInit->getLocation(), diag::note_previous_definition);
return;
}
if (VDecl->hasLocalStorage())
getCurFunction()->setHasBranchProtectedScope();
bool IsDependent = false;
for (unsigned I = 0, N = Exprs.size(); I != N; ++I) {
if (DiagnoseUnexpandedParameterPack(Exprs.get()[I], UPPC_Expression)) {
VDecl->setInvalidDecl();
return;
}
if (Exprs.get()[I]->isTypeDependent())
IsDependent = true;
}
// If either the declaration has a dependent type or if any of the
// expressions is type-dependent, we represent the initialization
// via a ParenListExpr for later use during template instantiation.
if (VDecl->getType()->isDependentType() || IsDependent) {
// Let clients know that initialization was done with a direct initializer.
VDecl->setCXXDirectInitializer(true);
// Store the initialization expressions as a ParenListExpr.
unsigned NumExprs = Exprs.size();
VDecl->setInit(new (Context) ParenListExpr(
Context, LParenLoc, (Expr **)Exprs.release(), NumExprs, RParenLoc,
VDecl->getType().getNonReferenceType()));
return;
}
// Capture the variable that is being initialized and the style of
// initialization.
InitializedEntity Entity = InitializedEntity::InitializeVariable(VDecl);
// FIXME: Poor source location information.
InitializationKind Kind
= InitializationKind::CreateDirect(VDecl->getLocation(),
LParenLoc, RParenLoc);
QualType T = VDecl->getType();
InitializationSequence InitSeq(*this, Entity, Kind,
Exprs.get(), Exprs.size());
ExprResult Result = InitSeq.Perform(*this, Entity, Kind, move(Exprs), &T);
if (Result.isInvalid()) {
VDecl->setInvalidDecl();
return;
} else if (T != VDecl->getType()) {
VDecl->setType(T);
Result.get()->setType(T);
}
Expr *Init = Result.get();
CheckImplicitConversions(Init, LParenLoc);
Init = MaybeCreateExprWithCleanups(Init);
VDecl->setInit(Init);
VDecl->setCXXDirectInitializer(true);
CheckCompleteVariableDeclaration(VDecl);
}
/// \brief Given a constructor and the set of arguments provided for the
/// constructor, convert the arguments and add any required default arguments
/// to form a proper call to this constructor.

View File

@ -2920,6 +2920,11 @@ Sema::ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc,
case tok::minusminus: Opc = UO_PostDec; break;
}
// Since this might is a postfix expression, get rid of ParenListExprs.
ExprResult Result = MaybeConvertParenListExprToParenExpr(S, Input);
if (Result.isInvalid()) return ExprError();
Input = Result.take();
return BuildUnaryOp(S, OpLoc, Opc, Input);
}
@ -4251,8 +4256,8 @@ ExprResult Sema::BuildVectorLiteral(SourceLocation LParenLoc,
return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, initE);
}
/// This is not an AltiVec-style cast, so turn the ParenListExpr into a sequence
/// of comma binary operators.
/// This is not an AltiVec-style cast or or C++ direct-initialization, so turn
/// the ParenListExpr into a sequence of comma binary operators.
ExprResult
Sema::MaybeConvertParenListExprToParenExpr(Scope *S, Expr *OrigExpr) {
ParenListExpr *E = dyn_cast<ParenListExpr>(OrigExpr);
@ -4270,18 +4275,13 @@ Sema::MaybeConvertParenListExprToParenExpr(Scope *S, Expr *OrigExpr) {
return ActOnParenExpr(E->getLParenLoc(), E->getRParenLoc(), Result.get());
}
ExprResult Sema::ActOnParenOrParenListExpr(SourceLocation L,
ExprResult Sema::ActOnParenListExpr(SourceLocation L,
SourceLocation R,
MultiExprArg Val) {
unsigned nexprs = Val.size();
Expr **exprs = reinterpret_cast<Expr**>(Val.release());
assert((exprs != 0) && "ActOnParenOrParenListExpr() missing expr list");
Expr *expr;
if (nexprs == 1)
expr = new (Context) ParenExpr(L, R, exprs[0]);
else
expr = new (Context) ParenListExpr(Context, L, exprs, nexprs, R,
exprs[nexprs-1]->getType());
Expr *expr = new (Context) ParenListExpr(Context, L, exprs, nexprs, R);
return Owned(expr);
}

View File

@ -4708,6 +4708,13 @@ InitializationSequence::Perform(Sema &S,
}
}
if (Kind.getKind() == InitializationKind::IK_Direct &&
!Kind.isExplicitCast()) {
// Rebuild the ParenListExpr.
SourceRange ParenRange = Kind.getParenRange();
return S.ActOnParenListExpr(ParenRange.getBegin(), ParenRange.getEnd(),
move(Args));
}
assert(Kind.getKind() == InitializationKind::IK_Copy ||
Kind.isExplicitCast());
return ExprResult(Args.release()[0]);

View File

@ -1833,14 +1833,16 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
FieldDecl *NewField = FieldsWithMemberInitializers[I].second;
Expr *OldInit = OldField->getInClassInitializer();
SourceLocation LParenLoc, RParenLoc;
ASTOwningVector<Expr*> NewArgs(*this);
if (InstantiateInitializer(OldInit, TemplateArgs, LParenLoc, NewArgs,
RParenLoc))
ExprResult NewInit = SubstInitializer(OldInit, TemplateArgs,
/*CXXDirectInit=*/false);
if (NewInit.isInvalid())
NewField->setInvalidDecl();
else {
assert(NewArgs.size() == 1 && "wrong number of in-class initializers");
ActOnCXXInClassMemberInitializer(NewField, LParenLoc, NewArgs[0]);
Expr *Init = NewInit.take();
assert(Init && "no-argument initializer in class");
assert(!isa<ParenListExpr>(Init) && "call-style init in class");
ActOnCXXInClassMemberInitializer(NewField,
Init->getSourceRange().getBegin(), Init);
}
}

View File

@ -252,66 +252,6 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
return Inst;
}
/// \brief Instantiate an initializer, breaking it into separate
/// initialization arguments.
///
/// \param Init The initializer to instantiate.
///
/// \param TemplateArgs Template arguments to be substituted into the
/// initializer.
///
/// \param NewArgs Will be filled in with the instantiation arguments.
///
/// \returns true if an error occurred, false otherwise
bool Sema::InstantiateInitializer(Expr *Init,
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation &LParenLoc,
ASTOwningVector<Expr*> &NewArgs,
SourceLocation &RParenLoc) {
NewArgs.clear();
LParenLoc = SourceLocation();
RParenLoc = SourceLocation();
if (!Init)
return false;
if (ExprWithCleanups *ExprTemp = dyn_cast<ExprWithCleanups>(Init))
Init = ExprTemp->getSubExpr();
while (CXXBindTemporaryExpr *Binder = dyn_cast<CXXBindTemporaryExpr>(Init))
Init = Binder->getSubExpr();
if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Init))
Init = ICE->getSubExprAsWritten();
if (ParenListExpr *ParenList = dyn_cast<ParenListExpr>(Init)) {
LParenLoc = ParenList->getLParenLoc();
RParenLoc = ParenList->getRParenLoc();
return SubstExprs(ParenList->getExprs(), ParenList->getNumExprs(),
true, TemplateArgs, NewArgs);
}
if (CXXConstructExpr *Construct = dyn_cast<CXXConstructExpr>(Init)) {
if (!isa<CXXTemporaryObjectExpr>(Construct)) {
if (SubstExprs(Construct->getArgs(), Construct->getNumArgs(), true,
TemplateArgs, NewArgs))
return true;
// FIXME: Fake locations!
LParenLoc = PP.getLocForEndOfToken(Init->getLocStart());
RParenLoc = LParenLoc;
return false;
}
}
ExprResult Result = SubstExpr(Init, TemplateArgs);
if (Result.isInvalid())
return true;
NewArgs.push_back(Result.takeAs<Expr>());
return false;
}
Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
// If this is the variable for an anonymous struct or union,
// instantiate the anonymous struct/union type first.
@ -342,7 +282,7 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
D->getStorageClass(),
D->getStorageClassAsWritten());
Var->setThreadSpecified(D->isThreadSpecified());
Var->setCXXDirectInitializer(D->hasCXXDirectInitializer());
Var->setInitStyle(D->getInitStyle());
Var->setCXXForRangeDecl(D->isCXXForRangeDecl());
Var->setConstexpr(D->isConstexpr());
@ -403,25 +343,16 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
SemaRef.PushExpressionEvaluationContext(Sema::PotentiallyEvaluated);
// Instantiate the initializer.
SourceLocation LParenLoc, RParenLoc;
ASTOwningVector<Expr*> InitArgs(SemaRef);
if (!SemaRef.InstantiateInitializer(D->getInit(), TemplateArgs, LParenLoc,
InitArgs, RParenLoc)) {
ExprResult Init = SemaRef.SubstInitializer(D->getInit(), TemplateArgs,
D->getInitStyle() == VarDecl::CallInit);
if (!Init.isInvalid()) {
bool TypeMayContainAuto = true;
if (D->hasCXXDirectInitializer()) {
// Add the direct initializer to the declaration.
SemaRef.AddCXXDirectInitializerToDecl(Var,
LParenLoc,
move_arg(InitArgs),
RParenLoc,
if (Init.get()) {
bool DirectInit = D->isDirectInit();
SemaRef.AddInitializerToDecl(Var, Init.take(), DirectInit,
TypeMayContainAuto);
} else if (InitArgs.size() == 0) {
} else
SemaRef.ActOnUninitializedDecl(Var, TypeMayContainAuto);
} else {
assert(InitArgs.size() == 1);
Expr *Init = InitArgs.take()[0];
SemaRef.AddInitializerToDecl(Var, Init, false, TypeMayContainAuto);
}
} else {
// FIXME: Not too happy about invalidating the declaration
// because of a bogus initializer.
@ -2743,18 +2674,6 @@ void Sema::InstantiateStaticDataMemberDefinition(
}
}
static MultiInitializer CreateMultiInitializer(SmallVectorImpl<Expr*> &Args,
const CXXCtorInitializer *Init) {
// FIXME: This is a hack that will do slightly the wrong thing for an
// initializer of the form foo({...}).
// The right thing to do would be to modify InstantiateInitializer to create
// the MultiInitializer.
if (Args.size() == 1 && isa<InitListExpr>(Args[0]))
return MultiInitializer(Args[0]);
return MultiInitializer(Init->getLParenLoc(), Args.data(),
Args.size(), Init->getRParenLoc());
}
void
Sema::InstantiateMemInitializers(CXXConstructorDecl *New,
const CXXConstructorDecl *Tmpl,
@ -2774,9 +2693,6 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New,
if (!Init->isWritten())
continue;
SourceLocation LParenLoc, RParenLoc;
ASTOwningVector<Expr*> NewArgs(*this);
SourceLocation EllipsisLoc;
if (Init->isPackExpansion()) {
@ -2804,8 +2720,9 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New,
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(*this, I);
// Instantiate the initializer.
if (InstantiateInitializer(Init->getInit(), TemplateArgs,
LParenLoc, NewArgs, RParenLoc)) {
ExprResult TempInit = SubstInitializer(Init->getInit(), TemplateArgs,
/*CXXDirectInit=*/true);
if (TempInit.isInvalid()) {
AnyErrors = true;
break;
}
@ -2821,9 +2738,8 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New,
}
// Build the initializer.
MultiInitializer MultiInit(CreateMultiInitializer(NewArgs, Init));
MemInitResult NewInit = BuildBaseInitializer(BaseTInfo->getType(),
BaseTInfo, MultiInit,
BaseTInfo, TempInit.take(),
New->getParent(),
SourceLocation());
if (NewInit.isInvalid()) {
@ -2832,15 +2748,15 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New,
}
NewInits.push_back(NewInit.get());
NewArgs.clear();
}
continue;
}
// Instantiate the initializer.
if (InstantiateInitializer(Init->getInit(), TemplateArgs,
LParenLoc, NewArgs, RParenLoc)) {
ExprResult TempInit = SubstInitializer(Init->getInit(), TemplateArgs,
/*CXXDirectInit=*/true);
if (TempInit.isInvalid()) {
AnyErrors = true;
continue;
}
@ -2857,13 +2773,11 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New,
continue;
}
MultiInitializer MultiInit(CreateMultiInitializer(NewArgs, Init));
if (Init->isBaseInitializer())
NewInit = BuildBaseInitializer(TInfo->getType(), TInfo, MultiInit,
NewInit = BuildBaseInitializer(TInfo->getType(), TInfo, TempInit.take(),
New->getParent(), EllipsisLoc);
else
NewInit = BuildDelegatingInitializer(TInfo, MultiInit,
NewInit = BuildDelegatingInitializer(TInfo, TempInit.take(),
cast<CXXRecordDecl>(CurContext->getParent()));
} else if (Init->isMemberInitializer()) {
FieldDecl *Member = cast_or_null<FieldDecl>(FindInstantiatedDecl(
@ -2876,8 +2790,7 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New,
continue;
}
MultiInitializer MultiInit(CreateMultiInitializer(NewArgs, Init));
NewInit = BuildMemberInitializer(Member, MultiInit,
NewInit = BuildMemberInitializer(Member, TempInit.take(),
Init->getSourceLocation());
} else if (Init->isIndirectMemberInitializer()) {
IndirectFieldDecl *IndirectMember =
@ -2891,8 +2804,7 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New,
continue;
}
MultiInitializer MultiInit(CreateMultiInitializer(NewArgs, Init));
NewInit = BuildMemberInitializer(IndirectMember, MultiInit,
NewInit = BuildMemberInitializer(IndirectMember, TempInit.take(),
Init->getSourceLocation());
}
@ -2900,9 +2812,6 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New,
AnyErrors = true;
New->setInvalidDecl();
} else {
// FIXME: It would be nice if ASTOwningVector had a release function.
NewArgs.take();
NewInits.push_back(NewInit.get());
}
}
@ -2915,6 +2824,45 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New,
AnyErrors);
}
ExprResult Sema::SubstInitializer(Expr *Init,
const MultiLevelTemplateArgumentList &TemplateArgs,
bool CXXDirectInit) {
// Initializers are instantiated like expressions, except that various outer
// layers are stripped.
if (!Init)
return Owned(Init);
if (ExprWithCleanups *ExprTemp = dyn_cast<ExprWithCleanups>(Init))
Init = ExprTemp->getSubExpr();
while (CXXBindTemporaryExpr *Binder = dyn_cast<CXXBindTemporaryExpr>(Init))
Init = Binder->getSubExpr();
if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Init))
Init = ICE->getSubExprAsWritten();
// If this is a direct-initializer, we take apart CXXConstructExprs.
// Everything else is passed through.
CXXConstructExpr *Construct;
if (!CXXDirectInit || !(Construct = dyn_cast<CXXConstructExpr>(Init)) ||
isa<CXXTemporaryObjectExpr>(Construct))
return SubstExpr(Init, TemplateArgs);
ASTOwningVector<Expr*> NewArgs(*this);
if (SubstExprs(Construct->getArgs(), Construct->getNumArgs(), true,
TemplateArgs, NewArgs))
return ExprError();
// Treat an empty initializer like none.
if (NewArgs.empty())
return Owned((Expr*)0);
// Build a ParenListExpr to represent anything else.
// FIXME: Fake locations!
SourceLocation Loc = PP.getLocForEndOfToken(Init->getLocStart());
return ActOnParenListExpr(Loc, Loc, move_arg(NewArgs));
}
// TODO: this could be templated if the various decl types used the
// same method name.
static bool isInstantiationOf(ClassTemplateDecl *Pattern,

View File

@ -1648,8 +1648,7 @@ public:
ExprResult RebuildParenListExpr(SourceLocation LParenLoc,
MultiExprArg SubExprs,
SourceLocation RParenLoc) {
return getSema().ActOnParenOrParenListExpr(LParenLoc, RParenLoc,
move(SubExprs));
return getSema().ActOnParenListExpr(LParenLoc, RParenLoc, move(SubExprs));
}
/// \brief Build a new address-of-label expression.

View File

@ -877,7 +877,7 @@ void ASTDeclReader::VisitVarDecl(VarDecl *VD) {
VD->VarDeclBits.SClass = (StorageClass)Record[Idx++];
VD->VarDeclBits.SClassAsWritten = (StorageClass)Record[Idx++];
VD->VarDeclBits.ThreadSpecified = Record[Idx++];
VD->VarDeclBits.HasCXXDirectInit = Record[Idx++];
VD->VarDeclBits.InitStyle = Record[Idx++];
VD->VarDeclBits.ExceptionVar = Record[Idx++];
VD->VarDeclBits.NRVOVariable = Record[Idx++];
VD->VarDeclBits.CXXForRangeDecl = Record[Idx++];

View File

@ -656,7 +656,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
Record.push_back(D->getStorageClass()); // FIXME: stable encoding
Record.push_back(D->getStorageClassAsWritten());
Record.push_back(D->isThreadSpecified());
Record.push_back(D->hasCXXDirectInitializer());
Record.push_back(D->getInitStyle());
Record.push_back(D->isExceptionVariable());
Record.push_back(D->isNRVOVariable());
Record.push_back(D->isCXXForRangeDecl());
@ -688,7 +688,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
D->getDeclName().getNameKind() == DeclarationName::Identifier &&
!D->hasExtInfo() &&
D->getFirstDeclaration() == D->getMostRecentDecl() &&
!D->hasCXXDirectInitializer() &&
D->getInitStyle() == VarDecl::CInit &&
D->getInit() == 0 &&
!isa<ParmVarDecl>(D) &&
!SpecInfo)
@ -728,7 +728,7 @@ void ASTDeclWriter::VisitParmVarDecl(ParmVarDecl *D) {
D->getAccess() == AS_none &&
!D->isModulePrivate() &&
D->getStorageClass() == 0 &&
!D->hasCXXDirectInitializer() && // Can params have this ever?
D->getInitStyle() == VarDecl::CInit && // Can params have anything else?
D->getFunctionScopeDepth() == 0 &&
D->getObjCDeclQualifier() == 0 &&
!D->isKNRPromoted() &&

View File

@ -185,7 +185,7 @@ struct alignas(Types) TestUnexpandedDecls : T{ // expected-error{{expression con
void test_initializers() {
T copy_init = static_cast<Types>(0); // expected-error{{initializer contains unexpanded parameter pack 'Types'}}
T direct_init(0, static_cast<Types>(0)); // expected-error{{expression contains unexpanded parameter pack 'Types'}}
T direct_init(0, static_cast<Types>(0)); // expected-error{{initializer contains unexpanded parameter pack 'Types'}}
T list_init = { static_cast<Types>(0) }; // expected-error{{initializer contains unexpanded parameter pack 'Types'}}
}

View File

@ -1,17 +0,0 @@
// RUN: %clang -cc1 -ast-dump %s | not grep NULL
// Makes sure that we don't introduce null types when handling
// ParenListExpr.
template<typename T> class X { void f() { X x(*this); } };
template<typename T> class Y { Y() : t(1) {} T t; };
template<typename T> class Z { Z() : b(true) {} const bool b; };
template<typename T> class A : public Z<T> { A() : Z<T>() {} };
class C {};
template<typename T> class D : public C { D(): C() {} };
void f() { (int)(1, 2); }

View File

@ -0,0 +1,21 @@
// RUN: %clang_cc1 -fsyntax-only %s
template <typename T>
struct foo {
struct bar;
bar fn() {
// Should not get errors about bar being incomplete here.
bar b = bar(1, 2);
return b;
}
};
template <typename T>
struct foo<T>::bar {
bar(int, int);
};
void fn() {
foo<int>().fn();
}