Implement support for C++11 in-class initialization of non-static data members.

llvm-svn: 132878
This commit is contained in:
Richard Smith 2011-06-11 17:19:42 +00:00
parent 1c2d29e3c3
commit 938f40b5aa
57 changed files with 1564 additions and 283 deletions

View File

@ -1953,20 +1953,33 @@ class FieldDecl : public DeclaratorDecl {
bool Mutable : 1;
mutable unsigned CachedFieldIndex : 31;
Expr *BitWidth;
/// \brief A pointer to either the in-class initializer for this field (if
/// the boolean value is false), or the bit width expression for this bit
/// field (if the boolean value is true).
///
/// We can safely combine these two because in-class initializers are not
/// permitted for bit-fields.
///
/// If the boolean is false and the initializer is null, then this field has
/// an in-class initializer which has not yet been parsed and attached.
llvm::PointerIntPair<Expr *, 1, bool> InitializerOrBitWidth;
protected:
FieldDecl(Kind DK, DeclContext *DC, SourceLocation StartLoc,
SourceLocation IdLoc, IdentifierInfo *Id,
QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable)
QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable,
bool HasInit)
: DeclaratorDecl(DK, DC, IdLoc, Id, T, TInfo, StartLoc),
Mutable(Mutable), CachedFieldIndex(0), BitWidth(BW) {
Mutable(Mutable), CachedFieldIndex(0),
InitializerOrBitWidth(BW, !HasInit) {
assert(!(BW && HasInit) && "got initializer for bitfield");
}
public:
static FieldDecl *Create(const ASTContext &C, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc,
IdentifierInfo *Id, QualType T,
TypeSourceInfo *TInfo, Expr *BW, bool Mutable);
TypeSourceInfo *TInfo, Expr *BW, bool Mutable,
bool HasInit);
/// getFieldIndex - Returns the index of this field within its record,
/// as appropriate for passing to ASTRecordLayout::getFieldOffset.
@ -1979,10 +1992,12 @@ public:
void setMutable(bool M) { Mutable = M; }
/// isBitfield - Determines whether this field is a bitfield.
bool isBitField() const { return BitWidth != NULL; }
bool isBitField() const {
return InitializerOrBitWidth.getInt() && InitializerOrBitWidth.getPointer();
}
/// @brief Determines whether this is an unnamed bitfield.
bool isUnnamedBitfield() const { return BitWidth != NULL && !getDeclName(); }
bool isUnnamedBitfield() const { return isBitField() && !getDeclName(); }
/// isAnonymousStructOrUnion - Determines whether this field is a
/// representative for an anonymous struct or union. Such fields are
@ -1990,8 +2005,37 @@ public:
/// store the data for the anonymous union or struct.
bool isAnonymousStructOrUnion() const;
Expr *getBitWidth() const { return BitWidth; }
void setBitWidth(Expr *BW) { BitWidth = BW; }
Expr *getBitWidth() const {
return isBitField() ? InitializerOrBitWidth.getPointer() : 0;
}
void setBitWidth(Expr *BW) {
assert(!InitializerOrBitWidth.getPointer() &&
"bit width or initializer already set");
InitializerOrBitWidth.setPointer(BW);
InitializerOrBitWidth.setInt(1);
}
/// hasInClassInitializer - Determine whether this member has a C++0x in-class
/// initializer.
bool hasInClassInitializer() const {
return !InitializerOrBitWidth.getInt();
}
/// getInClassInitializer - Get the C++0x in-class initializer for this
/// member, or null if one has not been set. If a valid declaration has an
/// in-class initializer, but this returns null, then we have not parsed and
/// attached it yet.
Expr *getInClassInitializer() const {
return hasInClassInitializer() ? InitializerOrBitWidth.getPointer() : 0;
}
/// setInClassInitializer - Set the C++0x in-class initializer for this member.
void setInClassInitializer(Expr *Init);
/// removeInClassInitializer - Remove the C++0x in-class initializer from this
/// member.
void removeInClassInitializer() {
assert(!InitializerOrBitWidth.getInt() && "no initializer to remove");
InitializerOrBitWidth.setPointer(0);
InitializerOrBitWidth.setInt(1);
}
/// getParent - Returns the parent of this field declaration, which
/// is the struct in which this method is defined.

View File

@ -1378,6 +1378,8 @@ class CXXCtorInitializer {
/// \brief The argument used to initialize the base or member, which may
/// end up constructing an object (when multiple arguments are involved).
/// If 0, this is a field initializer, and the in-class member initializer
/// will be used.
Stmt *Init;
/// LParenLoc - Location of the left paren of the ctor-initializer.
@ -1452,6 +1454,13 @@ public:
return Initializee.is<IndirectFieldDecl*>();
}
/// isInClassMemberInitializer - Returns true when this initializer is an
/// implicit ctor initializer generated for a field with an initializer
/// defined on the member declaration.
bool isInClassMemberInitializer() const {
return !Init;
}
/// isDelegatingInitializer - Returns true when this initializer is creating
/// a delegating constructor.
bool isDelegatingInitializer() const {
@ -1580,7 +1589,14 @@ public:
reinterpret_cast<VarDecl **>(this + 1)[I] = Index;
}
Expr *getInit() const { return static_cast<Expr *>(Init); }
/// \brief Get the initializer. This is 0 if this is an in-class initializer
/// for a non-static data member which has not yet been parsed.
Expr *getInit() const {
if (!Init)
return getAnyMember()->getInClassInitializer();
return static_cast<Expr*>(Init);
}
};
/// CXXConstructorDecl - Represents a C++ constructor within a

View File

@ -724,7 +724,7 @@ private:
QualType T, TypeSourceInfo *TInfo, AccessControl ac, Expr *BW,
bool synthesized)
: FieldDecl(ObjCIvar, DC, StartLoc, IdLoc, Id, T, TInfo, BW,
/*Mutable=*/false),
/*Mutable=*/false, /*HasInit=*/false),
NextIvar(0), DeclAccess(ac), Synthesized(synthesized) {}
public:
@ -779,7 +779,7 @@ private:
QualType T, Expr *BW)
: FieldDecl(ObjCAtDefsField, DC, StartLoc, IdLoc, Id, T,
/*TInfo=*/0, // FIXME: Do ObjCAtDefs have declarators ?
BW, /*Mutable=*/false) {}
BW, /*Mutable=*/false, /*HasInit=*/false) {}
public:
static ObjCAtDefsFieldDecl *Create(ASTContext &C, DeclContext *DC,

View File

@ -2604,6 +2604,7 @@ public:
}
bool isNothrow(ASTContext &Ctx) const {
ExceptionSpecificationType EST = getExceptionSpecType();
assert(EST != EST_Delayed);
if (EST == EST_DynamicNone || EST == EST_BasicNoexcept)
return true;
if (EST != EST_ComputedNoexcept)

View File

@ -193,6 +193,8 @@ def ext_ref_qualifier : ExtWarn<
"reference qualifiers on functions are a C++0x extension">, InGroup<CXX0x>;
def ext_inline_namespace : ExtWarn<
"inline namespaces are a C++0x feature">, InGroup<CXX0x>;
def err_generalized_initializer_lists : Error<
"generalized initializer lists are a C++0x extension unsupported in Clang">;
def ext_generalized_initializer_lists : ExtWarn<
"generalized initializer lists are a C++0x extension unsupported in Clang">,
InGroup<CXX0x>;
@ -444,6 +446,15 @@ def warn_defaulted_function_accepted_as_extension: ExtWarn<
"defaulted function definition accepted as a C++0x extension">,
InGroup<CXX0x>;
// C++0x in-class member initialization
def warn_nonstatic_member_init_accepted_as_extension: ExtWarn<
"in-class initialization of non-static data member accepted as a C++0x extension">,
InGroup<CXX0x>;
def err_bitfield_member_init: Error<
"bitfield member cannot have an in-class initializer">;
def err_incomplete_array_member_init: Error<
"array bound cannot be deduced from an in-class initializer">;
// C++0x alias-declaration
def ext_alias_declaration : ExtWarn<
"alias declarations accepted as a C++0x extension">, InGroup<CXX0x>;

View File

@ -589,6 +589,8 @@ def warn_missing_exception_specification : Warning<
"%0 is missing exception specification '%1'">;
def err_noexcept_needs_constant_expression : Error<
"argument to noexcept specifier must be a constant expression">;
def err_exception_spec_unknown : Error<
"exception specification is not available until end of class definition">;
// C++ access checking
def err_class_redeclared_with_different_access : Error<
@ -973,8 +975,8 @@ def err_illegal_decl_array_of_auto : Error<
def err_new_array_of_auto : Error<
"cannot allocate array of 'auto'">;
def err_auto_not_allowed : Error<
"'auto' not allowed %select{in function prototype|in struct member"
"|in union member|in class member|in exception declaration"
"'auto' not allowed %select{in function prototype|in non-static struct member"
"|in non-static union member|in non-static class member|in exception declaration"
"|in template parameter|in block literal|in template argument"
"|in typedef|in type alias|in function return type|here}0">;
def err_auto_var_requires_init : Error<

View File

@ -18,12 +18,13 @@ namespace clang {
/// \brief The various types of exception specifications that exist in C++0x.
enum ExceptionSpecificationType {
EST_None, ///< no exception specification
EST_DynamicNone, ///< throw()
EST_Dynamic, ///< throw(T1, T2)
EST_MSAny, ///< Microsoft throw(...) extension
EST_BasicNoexcept, ///< noexcept
EST_ComputedNoexcept ///< noexcept(expression)
EST_None, ///< no exception specification
EST_DynamicNone, ///< throw()
EST_Dynamic, ///< throw(T1, T2)
EST_MSAny, ///< Microsoft throw(...) extension
EST_BasicNoexcept, ///< noexcept
EST_ComputedNoexcept, ///< noexcept(expression)
EST_Delayed ///< not known yet
};
inline bool isDynamicExceptionSpec(ExceptionSpecificationType ESpecType) {

View File

@ -580,6 +580,18 @@ private:
/// ExitScope - Pop a scope off the scope stack.
void ExitScope();
/// \brief RAII object used to modify the scope flags for the current scope.
class ParseScopeFlags {
Scope *CurScope;
unsigned OldFlags;
ParseScopeFlags(const ParseScopeFlags &); // do not implement
void operator=(const ParseScopeFlags &); // do not implement
public:
ParseScopeFlags(Parser *Self, unsigned ScopeFlags, bool ManageFlags = true);
~ParseScopeFlags();
};
//===--------------------------------------------------------------------===//
// Diagnostic Emission and Error recovery.
@ -621,8 +633,8 @@ private:
/// - function bodies
/// - default arguments
/// - exception-specifications (TODO: C++0x)
/// - and brace-or-equal-initializers (TODO: C++0x)
/// for non-static data members (including such things in nested classes)."
/// - and brace-or-equal-initializers for non-static data members
/// (including such things in nested classes)."
/// LateParsedDeclarations build the tree of those elements so they can
/// be parsed after parsing the top-level class.
class LateParsedDeclaration {
@ -630,6 +642,7 @@ private:
virtual ~LateParsedDeclaration();
virtual void ParseLexedMethodDeclarations();
virtual void ParseLexedMemberInitializers();
virtual void ParseLexedMethodDefs();
};
@ -641,6 +654,7 @@ private:
virtual ~LateParsedClass();
virtual void ParseLexedMethodDeclarations();
virtual void ParseLexedMemberInitializers();
virtual void ParseLexedMethodDefs();
private:
@ -714,6 +728,25 @@ private:
llvm::SmallVector<LateParsedDefaultArgument, 8> DefaultArgs;
};
/// LateParsedMemberInitializer - An initializer for a non-static class data
/// member whose parsing must to be delayed until the class is completely
/// defined (C++11 [class.mem]p2).
struct LateParsedMemberInitializer : public LateParsedDeclaration {
LateParsedMemberInitializer(Parser *P, Decl *FD)
: Self(P), Field(FD) { }
virtual void ParseLexedMemberInitializers();
Parser *Self;
/// Field - The field declaration.
Decl *Field;
/// CachedTokens - The sequence of tokens that comprises the initializer,
/// including any leading '='.
CachedTokens Toks;
};
/// LateParsedDeclarationsContainer - During parsing of a top (non-nested)
/// C++ class, its method declarations that contain parts that won't be
/// parsed until after the definition is completed (C++ [class.mem]p2),
@ -974,10 +1007,13 @@ private:
Decl *ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D,
const ParsedTemplateInfo &TemplateInfo,
const VirtSpecifiers& VS, ExprResult& Init);
void ParseCXXNonStaticMemberInitializer(Decl *VarD);
void ParseLexedMethodDeclarations(ParsingClass &Class);
void ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM);
void ParseLexedMethodDefs(ParsingClass &Class);
void ParseLexedMethodDef(LexedMethod &LM);
void ParseLexedMemberInitializers(ParsingClass &Class);
void ParseLexedMemberInitializer(LateParsedMemberInitializer &MI);
bool ConsumeAndStoreUntil(tok::TokenKind T1,
CachedTokens &Toks,
bool StopAtSemi = true,
@ -1755,6 +1791,8 @@ bool ParseAsmOperandsOpt(llvm::SmallVectorImpl<IdentifierInfo *> &Names,
bool SuppressDeclarations = false);
void ParseCXXMemberSpecification(SourceLocation StartLoc, unsigned TagType,
Decl *TagDecl);
ExprResult ParseCXXMemberInitializer(bool IsFunction,
SourceLocation &EqualLoc);
void ParseCXXClassMemberDeclaration(AccessSpecifier AS,
const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(),
ParsingDeclRAIIObject *DiagsFromTParams = 0);

View File

@ -1078,8 +1078,8 @@ struct DeclaratorChunk {
/// If this is an invalid location, there is no ref-qualifier.
unsigned RefQualifierLoc;
/// \brief When ExceptionSpecType isn't EST_None, the location of the
/// keyword introducing the spec.
/// \brief When ExceptionSpecType isn't EST_None or EST_Delayed, the
/// location of the keyword introducing the spec.
unsigned ExceptionSpecLoc;
/// ArgInfo - This is a pointer to a new[]'d array of ParamInfo objects that
@ -1616,6 +1616,29 @@ public:
DeclTypeInfo.erase(DeclTypeInfo.begin());
}
/// isArrayOfUnknownBound - This method returns true if the declarator
/// is a declarator for an array of unknown bound (looking through
/// parentheses).
bool isArrayOfUnknownBound() const {
for (unsigned i = 0, i_end = DeclTypeInfo.size(); i < i_end; ++i) {
switch (DeclTypeInfo[i].Kind) {
case DeclaratorChunk::Paren:
continue;
case DeclaratorChunk::Function:
case DeclaratorChunk::Pointer:
case DeclaratorChunk::Reference:
case DeclaratorChunk::BlockPointer:
case DeclaratorChunk::MemberPointer:
return false;
case DeclaratorChunk::Array:
return !DeclTypeInfo[i].Arr.NumElts;
}
llvm_unreachable("Invalid type chunk");
return false;
}
return false;
}
/// isFunctionDeclarator - This method returns true if the declarator
/// is a function declarator (looking through parentheses).
/// If true is returned, then the reference type parameter idx is

View File

@ -79,7 +79,12 @@ public:
ObjCMethodScope = 0x400,
/// SwitchScope - This is a scope that corresponds to a switch statement.
SwitchScope = 0x800
SwitchScope = 0x800,
/// ThisScope - This is the scope of a struct/union/class definition,
/// outside of any member function definition, where 'this' is nonetheless
/// usable.
ThisScope = 0x1000
};
private:
/// The parent scope for this scope. This is null for the translation-unit

View File

@ -1144,13 +1144,13 @@ public:
Declarator &D, Expr *BitfieldWidth);
FieldDecl *HandleField(Scope *S, RecordDecl *TagD, SourceLocation DeclStart,
Declarator &D, Expr *BitfieldWidth,
Declarator &D, Expr *BitfieldWidth, bool HasInit,
AccessSpecifier AS);
FieldDecl *CheckFieldDecl(DeclarationName Name, QualType T,
TypeSourceInfo *TInfo,
RecordDecl *Record, SourceLocation Loc,
bool Mutable, Expr *BitfieldWidth,
bool Mutable, Expr *BitfieldWidth, bool HasInit,
SourceLocation TSSL,
AccessSpecifier AS, NamedDecl *PrevDecl,
Declarator *D = 0);
@ -2612,6 +2612,13 @@ public:
// Then a throw(collected exceptions)
// Finally no specification.
// throw(...) is used instead if any called function uses it.
//
// If this exception specification cannot be known yet (for instance,
// because this is the exception specification for a defaulted default
// constructor and we haven't finished parsing the deferred parts of the
// class yet), the C++0x standard does not specify how to behave. We
// record this as an 'unknown' exception specification, which overrules
// any other specification (even 'none', to keep this rule simple).
ExceptionSpecificationType ComputedEST;
llvm::SmallPtrSet<CanQualType, 4> ExceptionsSeen;
llvm::SmallVector<QualType, 4> Exceptions;
@ -2644,6 +2651,15 @@ public:
/// \brief Integrate another called method into the collected data.
void CalledDecl(CXXMethodDecl *Method);
/// \brief Integrate an invoked expression into the collected data.
void CalledExpr(Expr *E);
/// \brief Specify that the exception specification can't be detemined yet.
void SetDelayed() {
ClearExceptions();
ComputedEST = EST_Delayed;
}
FunctionProtoType::ExtProtoInfo getEPI() const {
FunctionProtoType::ExtProtoInfo EPI;
EPI.ExceptionSpecType = getExceptionSpecType();
@ -2836,10 +2852,9 @@ public:
//// ActOnCXXThis - Parse 'this' pointer.
ExprResult ActOnCXXThis(SourceLocation loc);
/// tryCaptureCXXThis - Try to capture a 'this' pointer. Returns a
/// pointer to an instance method whose 'this' pointer is
/// capturable, or null if this is not possible.
CXXMethodDecl *tryCaptureCXXThis();
/// getAndCaptureCurrentThisType - Try to capture a 'this' pointer. Returns
/// the type of the 'this' pointer, or a null type if this is not possible.
QualType getAndCaptureCurrentThisType();
/// ActOnCXXBoolLiteral - Parse {true,false} literals.
ExprResult ActOnCXXBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind);
@ -3238,7 +3253,10 @@ public:
Declarator &D,
MultiTemplateParamsArg TemplateParameterLists,
Expr *BitfieldWidth, const VirtSpecifiers &VS,
Expr *Init, bool IsDefinition);
Expr *Init, bool HasDeferredInit,
bool IsDefinition);
void ActOnCXXInClassMemberInitializer(Decl *VarDecl, SourceLocation EqualLoc,
Expr *Init);
MemInitResult ActOnMemInitializer(Decl *ConstructorD,
Scope *S,
@ -3342,8 +3360,9 @@ public:
void ActOnStartDelayedMemberDeclarations(Scope *S, Decl *Record);
void ActOnStartDelayedCXXMethodDeclaration(Scope *S, Decl *Method);
void ActOnDelayedCXXMethodParameter(Scope *S, Decl *Param);
void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method);
void ActOnFinishDelayedMemberDeclarations(Scope *S, Decl *Record);
void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method);
void ActOnFinishDelayedMemberInitializers(Decl *Record);
void MarkAsLateParsedTemplate(FunctionDecl *FD, bool Flag = true);
bool IsInsideALocalClassWithinATemplateFunction();

View File

@ -3536,7 +3536,8 @@ QualType ASTContext::getCFConstantStringType() const {
SourceLocation(), 0,
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0,
/*Mutable=*/false);
/*Mutable=*/false,
/*HasInit=*/false);
Field->setAccess(AS_public);
CFConstantStringTypeDecl->addDecl(Field);
}
@ -3577,7 +3578,8 @@ QualType ASTContext::getNSConstantStringType() const {
SourceLocation(), 0,
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0,
/*Mutable=*/false);
/*Mutable=*/false,
/*HasInit=*/false);
Field->setAccess(AS_public);
NSConstantStringTypeDecl->addDecl(Field);
}
@ -3616,7 +3618,8 @@ QualType ASTContext::getObjCFastEnumerationStateType() const {
SourceLocation(), 0,
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0,
/*Mutable=*/false);
/*Mutable=*/false,
/*HasInit=*/false);
Field->setAccess(AS_public);
ObjCFastEnumerationStateTypeDecl->addDecl(Field);
}
@ -3653,7 +3656,8 @@ QualType ASTContext::getBlockDescriptorType() const {
&Idents.get(FieldNames[i]),
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0,
/*Mutable=*/false);
/*Mutable=*/false,
/*HasInit=*/false);
Field->setAccess(AS_public);
T->addDecl(Field);
}
@ -3701,7 +3705,8 @@ QualType ASTContext::getBlockDescriptorExtendedType() const {
&Idents.get(FieldNames[i]),
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0,
/*Mutable=*/false);
/*Mutable=*/false,
/*HasInit=*/false);
Field->setAccess(AS_public);
T->addDecl(Field);
}
@ -3786,7 +3791,8 @@ ASTContext::BuildByRefType(llvm::StringRef DeclName, QualType Ty) const {
SourceLocation(),
&Idents.get(FieldNames[i]),
FieldTypes[i], /*TInfo=*/0,
/*BitWidth=*/0, /*Mutable=*/false);
/*BitWidth=*/0, /*Mutable=*/false,
/*HasInit=*/false);
Field->setAccess(AS_public);
T->addDecl(Field);
}

View File

@ -2512,9 +2512,12 @@ Decl *ASTNodeImporter::VisitFieldDecl(FieldDecl *D) {
FieldDecl *ToField = FieldDecl::Create(Importer.getToContext(), DC,
Importer.Import(D->getInnerLocStart()),
Loc, Name.getAsIdentifierInfo(),
T, TInfo, BitWidth, D->isMutable());
T, TInfo, BitWidth, D->isMutable(),
D->hasInClassInitializer());
ToField->setAccess(D->getAccess());
ToField->setLexicalDeclContext(LexicalDC);
if (ToField->hasInClassInitializer())
ToField->setInClassInitializer(D->getInClassInitializer());
Importer.Imported(D, ToField);
LexicalDC->addDecl(ToField);
return ToField;

View File

@ -2077,9 +2077,10 @@ SourceRange FunctionDecl::getSourceRange() const {
FieldDecl *FieldDecl::Create(const ASTContext &C, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc,
IdentifierInfo *Id, QualType T,
TypeSourceInfo *TInfo, Expr *BW, bool Mutable) {
TypeSourceInfo *TInfo, Expr *BW, bool Mutable,
bool HasInit) {
return new (C) FieldDecl(Decl::Field, DC, StartLoc, IdLoc, Id, T, TInfo,
BW, Mutable);
BW, Mutable, HasInit);
}
bool FieldDecl::isAnonymousStructOrUnion() const {
@ -2124,10 +2125,17 @@ unsigned FieldDecl::getFieldIndex() const {
SourceRange FieldDecl::getSourceRange() const {
if (isBitField())
return SourceRange(getInnerLocStart(), BitWidth->getLocEnd());
return SourceRange(getInnerLocStart(), getBitWidth()->getLocEnd());
return DeclaratorDecl::getSourceRange();
}
void FieldDecl::setInClassInitializer(Expr *Init) {
assert(!InitializerOrBitWidth.getPointer() &&
"bit width or initializer already set");
InitializerOrBitWidth.setPointer(Init);
InitializerOrBitWidth.setInt(0);
}
//===----------------------------------------------------------------------===//
// TagDecl Implementation
//===----------------------------------------------------------------------===//

View File

@ -715,6 +715,22 @@ NotASpecialMember:;
if (!hasNonLiteralTypeFieldsOrBases() && !T->isLiteralType())
data().HasNonLiteralTypeFieldsOrBases = true;
if (Field->hasInClassInitializer()) {
// C++0x [class]p5:
// A default constructor is trivial if [...] no non-static data member
// of its class has a brace-or-equal-initializer.
data().HasTrivialDefaultConstructor = false;
// C++0x [dcl.init.aggr]p1:
// An aggregate is a [...] class with [...] no
// brace-or-equal-initializers for non-static data members.
data().Aggregate = false;
// C++0x [class]p10:
// A POD struct is [...] a trivial class.
data().PlainOldData = false;
}
if (const RecordType *RecordTy = T->getAs<RecordType>()) {
CXXRecordDecl* FieldRec = cast<CXXRecordDecl>(RecordTy->getDecl());
if (FieldRec->getDefinition()) {
@ -1345,11 +1361,21 @@ const Type *CXXCtorInitializer::getBaseClass() const {
SourceLocation CXXCtorInitializer::getSourceLocation() const {
if (isAnyMemberInitializer() || isDelegatingInitializer())
return getMemberLocation();
if (isInClassMemberInitializer())
return getAnyMember()->getLocation();
return getBaseClassLoc().getLocalSourceRange().getBegin();
}
SourceRange CXXCtorInitializer::getSourceRange() const {
if (isInClassMemberInitializer()) {
FieldDecl *D = getAnyMember();
if (Expr *I = D->getInClassInitializer())
return I->getSourceRange();
return SourceRange();
}
return SourceRange(getSourceLocation(), getRParenLoc());
}

View File

@ -450,62 +450,67 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
if (D->hasAttr<NoReturnAttr>())
Proto += " __attribute((noreturn))";
if (CXXConstructorDecl *CDecl = dyn_cast<CXXConstructorDecl>(D)) {
if (CDecl->getNumCtorInitializers() > 0) {
Proto += " : ";
Out << Proto;
Proto.clear();
for (CXXConstructorDecl::init_const_iterator B = CDecl->init_begin(),
E = CDecl->init_end();
B != E; ++B) {
CXXCtorInitializer * BMInitializer = (*B);
if (B != CDecl->init_begin())
Out << ", ";
if (BMInitializer->isAnyMemberInitializer()) {
FieldDecl *FD = BMInitializer->getAnyMember();
Out << FD;
} else {
Out << QualType(BMInitializer->getBaseClass(),
0).getAsString(Policy);
}
bool HasInitializerList = false;
for (CXXConstructorDecl::init_const_iterator B = CDecl->init_begin(),
E = CDecl->init_end();
B != E; ++B) {
CXXCtorInitializer * BMInitializer = (*B);
if (BMInitializer->isInClassMemberInitializer())
continue;
if (!HasInitializerList) {
Proto += " : ";
Out << Proto;
Proto.clear();
HasInitializerList = true;
} else
Out << ", ";
if (BMInitializer->isAnyMemberInitializer()) {
FieldDecl *FD = BMInitializer->getAnyMember();
Out << FD;
} else {
Out << QualType(BMInitializer->getBaseClass(),
0).getAsString(Policy);
}
Out << "(";
if (!BMInitializer->getInit()) {
// Nothing to print
} else {
Expr *Init = BMInitializer->getInit();
if (ExprWithCleanups *Tmp = dyn_cast<ExprWithCleanups>(Init))
Init = Tmp->getSubExpr();
Out << "(";
if (!BMInitializer->getInit()) {
// Nothing to print
} else {
Expr *Init = BMInitializer->getInit();
if (ExprWithCleanups *Tmp = dyn_cast<ExprWithCleanups>(Init))
Init = Tmp->getSubExpr();
Init = Init->IgnoreParens();
Expr *SimpleInit = 0;
Expr **Args = 0;
unsigned NumArgs = 0;
if (ParenListExpr *ParenList = dyn_cast<ParenListExpr>(Init)) {
Args = ParenList->getExprs();
NumArgs = ParenList->getNumExprs();
} else if (CXXConstructExpr *Construct
= dyn_cast<CXXConstructExpr>(Init)) {
Args = Construct->getArgs();
NumArgs = Construct->getNumArgs();
} else
SimpleInit = Init;
if (SimpleInit)
SimpleInit->printPretty(Out, Context, 0, Policy, Indentation);
else {
for (unsigned I = 0; I != NumArgs; ++I) {
if (isa<CXXDefaultArgExpr>(Args[I]))
break;
if (I)
Out << ", ";
Args[I]->printPretty(Out, Context, 0, Policy, Indentation);
}
Init = Init->IgnoreParens();
Expr *SimpleInit = 0;
Expr **Args = 0;
unsigned NumArgs = 0;
if (ParenListExpr *ParenList = dyn_cast<ParenListExpr>(Init)) {
Args = ParenList->getExprs();
NumArgs = ParenList->getNumExprs();
} else if (CXXConstructExpr *Construct
= dyn_cast<CXXConstructExpr>(Init)) {
Args = Construct->getArgs();
NumArgs = Construct->getNumArgs();
} else
SimpleInit = Init;
if (SimpleInit)
SimpleInit->printPretty(Out, Context, 0, Policy, Indentation);
else {
for (unsigned I = 0; I != NumArgs; ++I) {
if (isa<CXXDefaultArgExpr>(Args[I]))
break;
if (I)
Out << ", ";
Args[I]->printPretty(Out, Context, 0, Policy, Indentation);
}
}
Out << ")";
}
Out << ")";
}
}
else
@ -553,6 +558,12 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) {
Out << " : ";
D->getBitWidth()->printPretty(Out, Context, 0, Policy, Indentation);
}
Expr *Init = D->getInClassInitializer();
if (!Policy.SuppressInitializers && Init) {
Out << " = ";
Init->printPretty(Out, Context, 0, Policy, Indentation);
}
}
void DeclPrinter::VisitLabelDecl(LabelDecl *D) {

View File

@ -22,6 +22,7 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/Lex/LiteralSupport.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
@ -1653,7 +1654,8 @@ static Expr::CanThrowResult CanSubExprsThrow(ASTContext &C, const Expr *CE) {
return R;
}
static Expr::CanThrowResult CanCalleeThrow(ASTContext &Ctx, const Decl *D,
static Expr::CanThrowResult CanCalleeThrow(ASTContext &Ctx, const Expr *E,
const Decl *D,
bool NullThrows = true) {
if (!D)
return NullThrows ? Expr::CT_Can : Expr::CT_Cannot;
@ -1683,6 +1685,15 @@ static Expr::CanThrowResult CanCalleeThrow(ASTContext &Ctx, const Decl *D,
if (!FT)
return Expr::CT_Can;
if (FT->getExceptionSpecType() == EST_Delayed) {
assert(isa<CXXConstructorDecl>(D) &&
"only constructor exception specs can be unknown");
Ctx.getDiagnostics().Report(E->getLocStart(),
diag::err_exception_spec_unknown)
<< E->getSourceRange();
return Expr::CT_Can;
}
return FT->isNothrow(Ctx) ? Expr::CT_Cannot : Expr::CT_Can;
}
@ -1757,7 +1768,7 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const {
else if (isa<CXXPseudoDestructorExpr>(CE->getCallee()->IgnoreParens()))
CT = CT_Cannot;
else
CT = CanCalleeThrow(C, CE->getCalleeDecl());
CT = CanCalleeThrow(C, this, CE->getCalleeDecl());
if (CT == CT_Can)
return CT;
return MergeCanThrow(CT, CanSubExprsThrow(C, this));
@ -1765,7 +1776,7 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const {
case CXXConstructExprClass:
case CXXTemporaryObjectExprClass: {
CanThrowResult CT = CanCalleeThrow(C,
CanThrowResult CT = CanCalleeThrow(C, this,
cast<CXXConstructExpr>(this)->getConstructor());
if (CT == CT_Can)
return CT;
@ -1778,8 +1789,8 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const {
CT = CT_Dependent;
else
CT = MergeCanThrow(
CanCalleeThrow(C, cast<CXXNewExpr>(this)->getOperatorNew()),
CanCalleeThrow(C, cast<CXXNewExpr>(this)->getConstructor(),
CanCalleeThrow(C, this, cast<CXXNewExpr>(this)->getOperatorNew()),
CanCalleeThrow(C, this, cast<CXXNewExpr>(this)->getConstructor(),
/*NullThrows*/false));
if (CT == CT_Can)
return CT;
@ -1792,10 +1803,11 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const {
if (DTy.isNull() || DTy->isDependentType()) {
CT = CT_Dependent;
} else {
CT = CanCalleeThrow(C, cast<CXXDeleteExpr>(this)->getOperatorDelete());
CT = CanCalleeThrow(C, this,
cast<CXXDeleteExpr>(this)->getOperatorDelete());
if (const RecordType *RT = DTy->getAs<RecordType>()) {
const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
CT = MergeCanThrow(CT, CanCalleeThrow(C, RD->getDestructor()));
CT = MergeCanThrow(CT, CanCalleeThrow(C, this, RD->getDestructor()));
}
if (CT == CT_Can)
return CT;
@ -1805,7 +1817,7 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const {
case CXXBindTemporaryExprClass: {
// The bound temporary has to be destroyed again, which might throw.
CanThrowResult CT = CanCalleeThrow(C,
CanThrowResult CT = CanCalleeThrow(C, this,
cast<CXXBindTemporaryExpr>(this)->getTemporary()->getDestructor());
if (CT == CT_Can)
return CT;

View File

@ -48,6 +48,11 @@ static void checkMangleDC(const DeclContext *DC, const BlockDecl *BD) {
const DeclContext *ExpectedDC = BD->getDeclContext();
while (isa<BlockDecl>(ExpectedDC) || isa<EnumDecl>(ExpectedDC))
ExpectedDC = ExpectedDC->getParent();
// In-class initializers for non-static data members are lexically defined
// within the class, but are mangled as if they were specified as constructor
// member initializers.
if (isa<CXXRecordDecl>(ExpectedDC) && DC != ExpectedDC)
DC = DC->getParent();
assert(DC == ExpectedDC && "Given decl context did not match expected!");
#endif
}

View File

@ -304,7 +304,11 @@ static void computeBlockInfo(CodeGenModule &CGM, CGBlockInfo &info) {
const DeclContext *DC = block->getDeclContext();
for (; isa<BlockDecl>(DC); DC = cast<BlockDecl>(DC)->getDeclContext())
;
QualType thisType = cast<CXXMethodDecl>(DC)->getThisType(C);
QualType thisType;
if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC))
thisType = C.getPointerType(C.getRecordType(RD));
else
thisType = cast<CXXMethodDecl>(DC)->getThisType(C);
const llvm::Type *llvmType = CGM.getTypes().ConvertType(thisType);
std::pair<CharUnits,CharUnits> tinfo

View File

@ -520,6 +520,7 @@ static void EmitMemberInitializer(CodeGenFunction &CGF,
FunctionArgList &Args) {
assert(MemberInit->isAnyMemberInitializer() &&
"Must have member initializer!");
assert(MemberInit->getInit() && "Must have initializer!");
// non-static data member initializers.
FieldDecl *Field = MemberInit->getAnyMember();

View File

@ -4098,9 +4098,9 @@ ObjCCommonTypesHelper::ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm)
SourceLocation(), SourceLocation(),
&Ctx.Idents.get("_objc_super"));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0,
Ctx.getObjCIdType(), 0, 0, false));
Ctx.getObjCIdType(), 0, 0, false, false));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0,
Ctx.getObjCClassType(), 0, 0, false));
Ctx.getObjCClassType(), 0, 0, false, false));
RD->completeDefinition();
SuperCTy = Ctx.getTagDeclType(RD);
@ -4559,9 +4559,9 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul
SourceLocation(), SourceLocation(),
&Ctx.Idents.get("_message_ref_t"));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0,
Ctx.VoidPtrTy, 0, 0, false));
Ctx.VoidPtrTy, 0, 0, false, false));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0,
Ctx.getObjCSelType(), 0, 0, false));
Ctx.getObjCSelType(), 0, 0, false, false));
RD->completeDefinition();
MessageRefCTy = Ctx.getTagDeclType(RD);

View File

@ -42,6 +42,7 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D,
FnD = Actions.ActOnCXXMemberDeclarator(getCurScope(), AS, D,
move(TemplateParams), 0,
VS, Init.release(),
/*HasInit=*/false,
/*IsDefinition*/true);
}
@ -166,8 +167,50 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D,
return FnD;
}
/// ParseCXXNonStaticMemberInitializer - We parsed and verified that the
/// specified Declarator is a well formed C++ non-static data member
/// declaration. Now lex its initializer and store its tokens for parsing
/// after the class is complete.
void Parser::ParseCXXNonStaticMemberInitializer(Decl *VarD) {
assert((Tok.is(tok::l_brace) || Tok.is(tok::equal)) &&
"Current token not a '{' or '='!");
LateParsedMemberInitializer *MI =
new LateParsedMemberInitializer(this, VarD);
getCurrentClass().LateParsedDeclarations.push_back(MI);
CachedTokens &Toks = MI->Toks;
tok::TokenKind kind = Tok.getKind();
if (kind == tok::equal) {
Toks.push_back(Tok);
ConsumeAnyToken();
}
if (kind == tok::l_brace) {
// Begin by storing the '{' token.
Toks.push_back(Tok);
ConsumeBrace();
// Consume everything up to (and including) the matching right brace.
ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/true);
} else {
// Consume everything up to (but excluding) the comma or semicolon.
ConsumeAndStoreUntil(tok::comma, Toks, /*StopAtSemi=*/true,
/*ConsumeFinalToken=*/false);
}
// Store an artificial EOF token to ensure that we don't run off the end of
// the initializer when we come to parse it.
Token Eof;
Eof.startToken();
Eof.setKind(tok::eof);
Eof.setLocation(Tok.getLocation());
Toks.push_back(Eof);
}
Parser::LateParsedDeclaration::~LateParsedDeclaration() {}
void Parser::LateParsedDeclaration::ParseLexedMethodDeclarations() {}
void Parser::LateParsedDeclaration::ParseLexedMemberInitializers() {}
void Parser::LateParsedDeclaration::ParseLexedMethodDefs() {}
Parser::LateParsedClass::LateParsedClass(Parser *P, ParsingClass *C)
@ -181,6 +224,10 @@ void Parser::LateParsedClass::ParseLexedMethodDeclarations() {
Self->ParseLexedMethodDeclarations(*Class);
}
void Parser::LateParsedClass::ParseLexedMemberInitializers() {
Self->ParseLexedMemberInitializers(*Class);
}
void Parser::LateParsedClass::ParseLexedMethodDefs() {
Self->ParseLexedMethodDefs(*Class);
}
@ -193,6 +240,10 @@ void Parser::LexedMethod::ParseLexedMethodDefs() {
Self->ParseLexedMethodDef(*this);
}
void Parser::LateParsedMemberInitializer::ParseLexedMemberInitializers() {
Self->ParseLexedMemberInitializer(*this);
}
/// ParseLexedMethodDeclarations - We finished parsing the member
/// specification of a top (non-nested) C++ class. Now go over the
/// stack of method declarations with some parts for which parsing was
@ -364,10 +415,72 @@ void Parser::ParseLexedMethodDef(LexedMethod &LM) {
origLoc))
while (Tok.getLocation() != origLoc && Tok.isNot(tok::eof))
ConsumeAnyToken();
}
}
/// ParseLexedMemberInitializers - We finished parsing the member specification
/// of a top (non-nested) C++ class. Now go over the stack of lexed data member
/// initializers that were collected during its parsing and parse them all.
void Parser::ParseLexedMemberInitializers(ParsingClass &Class) {
bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope;
ParseScope ClassTemplateScope(this, Scope::TemplateParamScope,
HasTemplateScope);
if (HasTemplateScope)
Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate);
// Set or update the scope flags to include Scope::ThisScope.
bool AlreadyHasClassScope = Class.TopLevelClass;
unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope|Scope::ThisScope;
ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope);
ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope);
if (!AlreadyHasClassScope)
Actions.ActOnStartDelayedMemberDeclarations(getCurScope(),
Class.TagOrTemplate);
for (size_t i = 0; i < Class.LateParsedDeclarations.size(); ++i) {
Class.LateParsedDeclarations[i]->ParseLexedMemberInitializers();
}
if (!AlreadyHasClassScope)
Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(),
Class.TagOrTemplate);
Actions.ActOnFinishDelayedMemberInitializers(Class.TagOrTemplate);
}
void Parser::ParseLexedMemberInitializer(LateParsedMemberInitializer &MI) {
if (MI.Field->isInvalidDecl())
return;
// Append the current token at the end of the new token stream so that it
// doesn't get lost.
MI.Toks.push_back(Tok);
PP.EnterTokenStream(MI.Toks.data(), MI.Toks.size(), true, false);
// Consume the previously pushed token.
ConsumeAnyToken();
SourceLocation EqualLoc;
ExprResult Init = ParseCXXMemberInitializer(/*IsFunction=*/false, EqualLoc);
Actions.ActOnCXXInClassMemberInitializer(MI.Field, EqualLoc, Init.release());
// The next token should be our artificial terminating EOF token.
if (Tok.isNot(tok::eof)) {
SourceLocation EndLoc = PP.getLocForEndOfToken(PrevTokLocation);
if (!EndLoc.isValid())
EndLoc = Tok.getLocation();
// No fixit; we can't recover as if there were a semicolon here.
Diag(EndLoc, diag::err_expected_semi_decl_list);
// Consume tokens until we hit the artificial EOF.
while (Tok.isNot(tok::eof))
ConsumeAnyToken();
}
ConsumeAnyToken();
}
/// ConsumeAndStoreUntil - Consume and store the token at the passed token
/// container until the token 'T' is reached (which gets
/// consumed/stored too, if ConsumeFinalToken).

View File

@ -1553,6 +1553,7 @@ bool Parser::isCXX0XFinalKeyword() const {
/// member-declarator:
/// declarator virt-specifier-seq[opt] pure-specifier[opt]
/// declarator constant-initializer[opt]
/// [C++11] declarator brace-or-equal-initializer[opt]
/// identifier[opt] ':' constant-expression
///
/// virt-specifier-seq:
@ -1731,10 +1732,14 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
bool IsDefinition = false;
// function-definition:
if (Tok.is(tok::l_brace)) {
//
// In C++11, a non-function declarator followed by an open brace is a
// braced-init-list for an in-class member initialization, not an
// erroneous function definition.
if (Tok.is(tok::l_brace) && !getLang().CPlusPlus0x) {
IsDefinition = true;
} else if (DeclaratorInfo.isFunctionDeclarator()) {
if (Tok.is(tok::colon) || Tok.is(tok::kw_try)) {
if (Tok.is(tok::l_brace) || Tok.is(tok::colon) || Tok.is(tok::kw_try)) {
IsDefinition = true;
} else if (Tok.is(tok::equal)) {
const Token &KW = NextToken();
@ -1790,7 +1795,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
while (1) {
// member-declarator:
// declarator pure-specifier[opt]
// declarator constant-initializer[opt]
// declarator brace-or-equal-initializer[opt]
// identifier[opt] ':' constant-expression
if (Tok.is(tok::colon)) {
ConsumeToken();
@ -1799,38 +1804,6 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
SkipUntil(tok::comma, true, true);
}
ParseOptionalCXX0XVirtSpecifierSeq(VS);
// pure-specifier:
// '= 0'
//
// constant-initializer:
// '=' constant-expression
//
// defaulted/deleted function-definition:
// '=' 'default' [TODO]
// '=' 'delete'
if (Tok.is(tok::equal)) {
ConsumeToken();
if (Tok.is(tok::kw_delete)) {
if (DeclaratorInfo.isFunctionDeclarator())
Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration)
<< 1 /* delete */;
else
Diag(ConsumeToken(), diag::err_deleted_non_function);
} else if (Tok.is(tok::kw_default)) {
if (DeclaratorInfo.isFunctionDeclarator())
Diag(Tok, diag::err_default_delete_in_multiple_declaration)
<< 1 /* delete */;
else
Diag(ConsumeToken(), diag::err_default_special_members);
} else {
Init = ParseInitializer();
if (Init.isInvalid())
SkipUntil(tok::comma, true, true);
}
}
// If a simple-asm-expr is present, parse it.
if (Tok.is(tok::kw_asm)) {
SourceLocation Loc;
@ -1845,6 +1818,30 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
// If attributes exist after the declarator, parse them.
MaybeParseGNUAttributes(DeclaratorInfo);
// FIXME: When g++ adds support for this, we'll need to check whether it
// goes before or after the GNU attributes and __asm__.
ParseOptionalCXX0XVirtSpecifierSeq(VS);
bool HasDeferredInitializer = false;
if (Tok.is(tok::equal) || Tok.is(tok::l_brace)) {
if (BitfieldSize.get()) {
Diag(Tok, diag::err_bitfield_member_init);
SkipUntil(tok::comma, true, true);
} else {
HasDeferredInitializer = !DeclaratorInfo.isFunctionDeclarator() &&
DeclaratorInfo.getDeclSpec().getStorageClassSpec()
!= DeclSpec::SCS_static;
if (!HasDeferredInitializer) {
SourceLocation EqualLoc;
Init = ParseCXXMemberInitializer(
DeclaratorInfo.isFunctionDeclarator(), EqualLoc);
if (Init.isInvalid())
SkipUntil(tok::comma, true, true);
}
}
}
// NOTE: If Sema is the Action module and declarator is an instance field,
// this call will *not* return the created decl; It will return null.
// See Sema::ActOnCXXMemberDeclarator for details.
@ -1860,7 +1857,9 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
DeclaratorInfo,
move(TemplateParams),
BitfieldSize.release(),
VS, Init.release(), false);
VS, Init.release(),
HasDeferredInitializer,
/*IsDefinition*/ false);
}
if (ThisDecl)
DeclsInGroup.push_back(ThisDecl);
@ -1873,6 +1872,24 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
DeclaratorInfo.complete(ThisDecl);
if (HasDeferredInitializer) {
if (!getLang().CPlusPlus0x)
Diag(Tok, diag::warn_nonstatic_member_init_accepted_as_extension);
if (DeclaratorInfo.isArrayOfUnknownBound()) {
// C++0x [dcl.array]p3: An array bound may also be omitted when the
// declarator is followed by an initializer.
//
// A brace-or-equal-initializer for a member-declarator is not an
// initializer in the gramamr, so this is ill-formed.
Diag(Tok, diag::err_incomplete_array_member_init);
SkipUntil(tok::comma, true, true);
// Avoid later warnings about a class member of incomplete type.
ThisDecl->setInvalidDecl();
} else
ParseCXXNonStaticMemberInitializer(ThisDecl);
}
// If we don't have a comma, it is either the end of the list (a ';')
// or an error, bail out.
if (Tok.isNot(tok::comma))
@ -1906,6 +1923,66 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
DeclsInGroup.size());
}
/// ParseCXXMemberInitializer - Parse the brace-or-equal-initializer or
/// pure-specifier. Also detect and reject any attempted defaulted/deleted
/// function definition. The location of the '=', if any, will be placed in
/// EqualLoc.
///
/// pure-specifier:
/// '= 0'
///
/// brace-or-equal-initializer:
/// '=' initializer-expression
/// braced-init-list [TODO]
///
/// initializer-clause:
/// assignment-expression
/// braced-init-list [TODO]
///
/// defaulted/deleted function-definition:
/// '=' 'default'
/// '=' 'delete'
///
/// Prior to C++0x, the assignment-expression in an initializer-clause must
/// be a constant-expression.
ExprResult Parser::ParseCXXMemberInitializer(bool IsFunction,
SourceLocation &EqualLoc) {
assert((Tok.is(tok::equal) || Tok.is(tok::l_brace))
&& "Data member initializer not starting with '=' or '{'");
if (Tok.is(tok::equal)) {
EqualLoc = ConsumeToken();
if (Tok.is(tok::kw_delete)) {
// In principle, an initializer of '= delete p;' is legal, but it will
// never type-check. It's better to diagnose it as an ill-formed expression
// than as an ill-formed deleted non-function member.
// An initializer of '= delete p, foo' will never be parsed, because
// a top-level comma always ends the initializer expression.
const Token &Next = NextToken();
if (IsFunction || Next.is(tok::semi) || Next.is(tok::comma) ||
Next.is(tok::eof)) {
if (IsFunction)
Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration)
<< 1 /* delete */;
else
Diag(ConsumeToken(), diag::err_deleted_non_function);
return ExprResult();
}
} else if (Tok.is(tok::kw_default)) {
Diag(ConsumeToken(), diag::err_default_special_members);
if (IsFunction)
Diag(Tok, diag::err_default_delete_in_multiple_declaration)
<< 0 /* default */;
else
Diag(ConsumeToken(), diag::err_default_special_members);
return ExprResult();
}
return ParseInitializer();
} else
return ExprError(Diag(Tok, diag::err_generalized_initializer_lists));
}
/// ParseCXXMemberSpecification - Parse the class definition.
///
/// member-specification:
@ -2057,19 +2134,20 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
LBraceLoc, RBraceLoc,
attrs.getList());
// C++ 9.2p2: Within the class member-specification, the class is regarded as
// complete within function bodies, default arguments,
// exception-specifications, and constructor ctor-initializers (including
// such things in nested classes).
// C++0x [class.mem]p2: Within the class member-specification, the class is
// regarded as complete within function bodies, default arguments, exception-
// specifications, and brace-or-equal-initializers for non-static data
// members (including such things in nested classes).
//
// FIXME: Only function bodies and constructor ctor-initializers are
// parsed correctly, fix the rest.
// FIXME: Only function bodies and brace-or-equal-initializers are currently
// handled. Fix the others!
if (TagDecl && NonNestedClass) {
// We are not inside a nested class. This class and its nested classes
// are complete and we can parse the delayed portions of method
// declarations and the lexed inline method definitions.
SourceLocation SavedPrevTokLocation = PrevTokLocation;
ParseLexedMethodDeclarations(getCurrentClass());
ParseLexedMemberInitializers(getCurrentClass());
ParseLexedMethodDefs(getCurrentClass());
PrevTokLocation = SavedPrevTokLocation;
}

View File

@ -351,7 +351,23 @@ void Parser::ExitScope() {
ScopeCache[NumCachedScopes++] = OldScope;
}
/// Set the flags for the current scope to ScopeFlags. If ManageFlags is false,
/// this object does nothing.
Parser::ParseScopeFlags::ParseScopeFlags(Parser *Self, unsigned ScopeFlags,
bool ManageFlags)
: CurScope(ManageFlags ? Self->getCurScope() : 0) {
if (CurScope) {
OldFlags = CurScope->getFlags();
CurScope->setFlags(ScopeFlags);
}
}
/// Restore the flags for the current scope to what they were before this
/// object overrode them.
Parser::ParseScopeFlags::~ParseScopeFlags() {
if (CurScope)
CurScope->setFlags(OldFlags);
}
//===----------------------------------------------------------------------===//

View File

@ -2695,7 +2695,8 @@ QualType RewriteObjC::getSuperStructType() {
SourceLocation(), 0,
FieldTypes[i], 0,
/*BitWidth=*/0,
/*Mutable=*/false));
/*Mutable=*/false,
/*HasInit=*/false));
}
SuperStructDecl->completeDefinition();
@ -2727,7 +2728,8 @@ QualType RewriteObjC::getConstantStringStructType() {
SourceLocation(), 0,
FieldTypes[i], 0,
/*BitWidth=*/0,
/*Mutable=*/true));
/*Mutable=*/true,
/*HasInit=*/false));
}
ConstantStringDecl->completeDefinition();
@ -4709,7 +4711,8 @@ Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) {
SourceLocation(),
&Context->Idents.get("FuncPtr"),
Context->VoidPtrTy, 0,
/*BitWidth=*/0, /*Mutable=*/true);
/*BitWidth=*/0, /*Mutable=*/true,
/*HasInit=*/false);
MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(),
FD->getType(), VK_LValue,
OK_Ordinary);
@ -4763,7 +4766,8 @@ Stmt *RewriteObjC::RewriteBlockDeclRefExpr(Expr *DeclRefExp) {
SourceLocation(),
&Context->Idents.get("__forwarding"),
Context->VoidPtrTy, 0,
/*BitWidth=*/0, /*Mutable=*/true);
/*BitWidth=*/0, /*Mutable=*/true,
/*HasInit=*/false);
MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow,
FD, SourceLocation(),
FD->getType(), VK_LValue,
@ -4773,7 +4777,8 @@ Stmt *RewriteObjC::RewriteBlockDeclRefExpr(Expr *DeclRefExp) {
FD = FieldDecl::Create(*Context, 0, SourceLocation(), SourceLocation(),
&Context->Idents.get(Name),
Context->VoidPtrTy, 0,
/*BitWidth=*/0, /*Mutable=*/true);
/*BitWidth=*/0, /*Mutable=*/true,
/*HasInit=*/false);
ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(),
DeclRefExp->getType(), VK_LValue, OK_Ordinary);

View File

@ -2610,7 +2610,8 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
/*IdentifierInfo=*/0,
Context.getTypeDeclType(Record),
TInfo,
/*BitWidth=*/0, /*Mutable=*/false);
/*BitWidth=*/0, /*Mutable=*/false,
/*HasInit=*/false);
Anon->setAccess(AS);
if (getLangOptions().CPlusPlus)
FieldCollector->Add(cast<FieldDecl>(Anon));
@ -2700,7 +2701,8 @@ Decl *Sema::BuildMicrosoftCAnonymousStruct(Scope *S, DeclSpec &DS,
/*IdentifierInfo=*/0,
Context.getTypeDeclType(Record),
TInfo,
/*BitWidth=*/0, /*Mutable=*/false);
/*BitWidth=*/0, /*Mutable=*/false,
/*HasInit=*/false);
Anon->setImplicit();
// Add the anonymous struct object to the current context.
@ -7512,14 +7514,13 @@ bool Sema::VerifyBitField(SourceLocation FieldLoc, IdentifierInfo *FieldName,
return false;
}
/// ActOnField - Each field of a struct/union/class is passed into this in order
/// ActOnField - Each field of a C struct/union is passed into this in order
/// to create a FieldDecl object for it.
Decl *Sema::ActOnField(Scope *S, Decl *TagD,
SourceLocation DeclStart,
Declarator &D, ExprTy *BitfieldWidth) {
Decl *Sema::ActOnField(Scope *S, Decl *TagD, SourceLocation DeclStart,
Declarator &D, ExprTy *BitfieldWidth) {
FieldDecl *Res = HandleField(S, cast_or_null<RecordDecl>(TagD),
DeclStart, D, static_cast<Expr*>(BitfieldWidth),
AS_public);
/*HasInit=*/false, AS_public);
return Res;
}
@ -7527,7 +7528,7 @@ Decl *Sema::ActOnField(Scope *S, Decl *TagD,
///
FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record,
SourceLocation DeclStart,
Declarator &D, Expr *BitWidth,
Declarator &D, Expr *BitWidth, bool HasInit,
AccessSpecifier AS) {
IdentifierInfo *II = D.getIdentifier();
SourceLocation Loc = DeclStart;
@ -7576,8 +7577,8 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record,
= (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_mutable);
SourceLocation TSSL = D.getSourceRange().getBegin();
FieldDecl *NewFD
= CheckFieldDecl(II, T, TInfo, Record, Loc, Mutable, BitWidth, TSSL,
AS, PrevDecl, &D);
= CheckFieldDecl(II, T, TInfo, Record, Loc, Mutable, BitWidth, HasInit,
TSSL, AS, PrevDecl, &D);
if (NewFD->isInvalidDecl())
Record->setInvalidDecl();
@ -7606,7 +7607,7 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record,
FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T,
TypeSourceInfo *TInfo,
RecordDecl *Record, SourceLocation Loc,
bool Mutable, Expr *BitWidth,
bool Mutable, Expr *BitWidth, bool HasInit,
SourceLocation TSSL,
AccessSpecifier AS, NamedDecl *PrevDecl,
Declarator *D) {
@ -7686,7 +7687,7 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T,
}
FieldDecl *NewFD = FieldDecl::Create(Context, Record, TSSL, Loc, II, T, TInfo,
BitWidth, Mutable);
BitWidth, Mutable, HasInit);
if (InvalidDecl)
NewFD->setInvalidDecl();
@ -8289,16 +8290,17 @@ void Sema::ActOnFields(Scope* S,
// Now that the record is complete, do any delayed exception spec checks
// we were missing.
if (!DelayedDestructorExceptionSpecChecks.empty()) {
while (!DelayedDestructorExceptionSpecChecks.empty()) {
const CXXDestructorDecl *Dtor =
DelayedDestructorExceptionSpecChecks.back().first;
if (Dtor->getParent() == Record) {
assert(!Dtor->getParent()->isDependentType() &&
"Should not ever add destructors of templates into the list.");
CheckOverridingFunctionExceptionSpec(Dtor,
DelayedDestructorExceptionSpecChecks.back().second);
DelayedDestructorExceptionSpecChecks.pop_back();
}
if (Dtor->getParent() != Record)
break;
assert(!Dtor->getParent()->isDependentType() &&
"Should not ever add destructors of templates into the list.");
CheckOverridingFunctionExceptionSpec(Dtor,
DelayedDestructorExceptionSpecChecks.back().second);
DelayedDestructorExceptionSpecChecks.pop_back();
}
} else {

View File

@ -113,8 +113,8 @@ namespace {
void Sema::ImplicitExceptionSpecification::CalledDecl(CXXMethodDecl *Method) {
assert(Context && "ImplicitExceptionSpecification without an ASTContext");
// If we have an MSAny spec already, don't bother.
if (!Method || ComputedEST == EST_MSAny)
// If we have an MSAny or unknown spec already, don't bother.
if (!Method || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed)
return;
const FunctionProtoType *Proto
@ -123,12 +123,15 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(CXXMethodDecl *Method) {
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
// If this function can throw any exceptions, make a note of that.
if (EST == EST_MSAny || EST == EST_None) {
if (EST == EST_Delayed || EST == EST_MSAny || EST == EST_None) {
ClearExceptions();
ComputedEST = EST;
return;
}
// FIXME: If the call to this decl is using any of its default arguments, we
// need to search them for potentially-throwing calls.
// If this function has a basic noexcept, it doesn't affect the outcome.
if (EST == EST_BasicNoexcept)
return;
@ -175,6 +178,35 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(CXXMethodDecl *Method) {
Exceptions.push_back(*E);
}
void Sema::ImplicitExceptionSpecification::CalledExpr(Expr *E) {
if (!E || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed)
return;
// FIXME:
//
// C++0x [except.spec]p14:
// [An] implicit exception-specification specifies the type-id T if and
// only if T is allowed by the exception-specification of a function directly
// invoked by fs implicit definition; f shall allow all exceptions if any
// function it directly invokes allows all exceptions, and f shall allow no
// exceptions if every function it directly invokes allows no exceptions.
//
// Note in particular that if an implicit exception-specification is generated
// for a function containing a throw-expression, that specification can still
// be noexcept(true).
//
// Note also that 'directly invoked' is not defined in the standard, and there
// is no indication that we should only consider potentially-evaluated calls.
//
// Ultimately we should implement the intent of the standard: the exception
// specification should be the set of exceptions which can be thrown by the
// implicit definition. For now, we assume that any non-nothrow expression can
// throw any exception.
if (E->CanThrow(*Context))
ComputedEST = EST_None;
}
bool
Sema::SetParamDefaultArgument(ParmVarDecl *Param, Expr *Arg,
SourceLocation EqualLoc) {
@ -1029,13 +1061,15 @@ bool Sema::CheckIfOverriddenFunctionIsMarkedFinal(const CXXMethodDecl *New,
/// ActOnCXXMemberDeclarator - This is invoked when a C++ class member
/// declarator is parsed. 'AS' is the access specifier, 'BW' specifies the
/// bitfield width if there is one and 'InitExpr' specifies the initializer if
/// any.
/// bitfield width if there is one, 'InitExpr' specifies the initializer if
/// one has been parsed, and 'HasDeferredInit' is true if an initializer is
/// present but parsing it has been deferred.
Decl *
Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
MultiTemplateParamsArg TemplateParameterLists,
ExprTy *BW, const VirtSpecifiers &VS,
ExprTy *InitExpr, bool IsDefinition) {
ExprTy *InitExpr, bool HasDeferredInit,
bool IsDefinition) {
const DeclSpec &DS = D.getDeclSpec();
DeclarationNameInfo NameInfo = GetNameForDeclarator(D);
DeclarationName Name = NameInfo.getName();
@ -1050,6 +1084,7 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
assert(isa<CXXRecordDecl>(CurContext));
assert(!DS.isFriendSpecified());
assert(!Init || !HasDeferredInit);
bool isFunc = false;
if (D.isFunctionDeclarator())
@ -1121,9 +1156,11 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
// FIXME: Check for template parameters!
// FIXME: Check that the name is an identifier!
Member = HandleField(S, cast<CXXRecordDecl>(CurContext), Loc, D, BitWidth,
AS);
HasDeferredInit, AS);
assert(Member && "HandleField never returns null");
} else {
assert(!HasDeferredInit);
Member = HandleDeclarator(S, D, move(TemplateParameterLists), IsDefinition);
if (!Member) {
return 0;
@ -1194,6 +1231,14 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
if (Init)
AddInitializerToDecl(Member, Init, false,
DS.getTypeSpecType() == DeclSpec::TST_auto);
else if (DS.getTypeSpecType() == DeclSpec::TST_auto &&
DS.getStorageClassSpec() == DeclSpec::SCS_static) {
// C++0x [dcl.spec.auto]p4: 'auto' can only be used in the type of a static
// data member if a brace-or-equal-initializer is provided.
Diag(Loc, diag::err_auto_var_requires_init)
<< Name << cast<ValueDecl>(Member)->getType();
Member->setInvalidDecl();
}
FinalizeDeclaration(Member);
@ -1202,6 +1247,47 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
return Member;
}
/// ActOnCXXInClassMemberInitializer - This is invoked after parsing an
/// in-class initializer for a non-static C++ class member. Such parsing
/// is deferred until the class is complete.
void
Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation EqualLoc,
Expr *InitExpr) {
FieldDecl *FD = cast<FieldDecl>(D);
if (!InitExpr) {
FD->setInvalidDecl();
FD->removeInClassInitializer();
return;
}
ExprResult Init = InitExpr;
if (!FD->getType()->isDependentType() && !InitExpr->isTypeDependent()) {
// FIXME: if there is no EqualLoc, this is list-initialization.
Init = PerformCopyInitialization(
InitializedEntity::InitializeMember(FD), EqualLoc, InitExpr);
if (Init.isInvalid()) {
FD->setInvalidDecl();
return;
}
CheckImplicitConversions(Init.get(), EqualLoc);
}
// C++0x [class.base.init]p7:
// The initialization of each base and member constitutes a
// full-expression.
Init = MaybeCreateExprWithCleanups(Init);
if (Init.isInvalid()) {
FD->setInvalidDecl();
return;
}
InitExpr = Init.release();
FD->setInClassInitializer(InitExpr);
}
/// \brief Find the direct and/or virtual base specifiers that
/// correspond to the given base type, for use in base initialization
/// within a constructor.
@ -2073,7 +2159,7 @@ struct BaseAndFieldInfo {
};
}
static bool CollectFieldInitializer(BaseAndFieldInfo &Info,
static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info,
FieldDecl *Top, FieldDecl *Field) {
// Overwhelmingly common case: we have a direct initializer for this field.
@ -2082,6 +2168,18 @@ static bool CollectFieldInitializer(BaseAndFieldInfo &Info,
return false;
}
// C++0x [class.base.init]p8: if the entity is a non-static data member that
// has a brace-or-equal-initializer, the entity is initialized as specified
// in [dcl.init].
if (Field->hasInClassInitializer()) {
Info.AllToInit.push_back(
new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field,
SourceLocation(),
SourceLocation(), 0,
SourceLocation()));
return false;
}
if (Info.IIK == IIK_Default && Field->isAnonymousStructOrUnion()) {
const RecordType *FieldClassType = Field->getType()->getAs<RecordType>();
assert(FieldClassType && "anonymous struct/union without record type");
@ -2104,7 +2202,7 @@ static bool CollectFieldInitializer(BaseAndFieldInfo &Info,
// field in the class is also initialized, so exit immediately.
return false;
} else if ((*FA)->isAnonymousStructOrUnion()) {
if (CollectFieldInitializer(Info, Top, *FA))
if (CollectFieldInitializer(SemaRef, Info, Top, *FA))
return true;
}
}
@ -2119,7 +2217,7 @@ static bool CollectFieldInitializer(BaseAndFieldInfo &Info,
// necessary.
for (RecordDecl::field_iterator FA = FieldClassDecl->field_begin(),
EA = FieldClassDecl->field_end(); FA != EA; FA++) {
if (CollectFieldInitializer(Info, Top, *FA))
if (CollectFieldInitializer(SemaRef, Info, Top, *FA))
return true;
}
}
@ -2128,7 +2226,7 @@ static bool CollectFieldInitializer(BaseAndFieldInfo &Info,
// Don't try to build an implicit initializer if there were semantic
// errors in any of the initializers (and therefore we might be
// missing some that the user actually wrote).
if (Info.AnyErrorsInInits)
if (Info.AnyErrorsInInits || Field->isInvalidDecl())
return false;
CXXCtorInitializer *Init = 0;
@ -2260,7 +2358,7 @@ Sema::SetCtorInitializers(CXXConstructorDecl *Constructor,
"Incomplete array type is not valid");
continue;
}
if (CollectFieldInitializer(Info, *Field, *Field))
if (CollectFieldInitializer(*this, Info, *Field, *Field))
HadError = true;
}
@ -2939,6 +3037,9 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
for (RecordDecl::field_iterator F = Record->field_begin(),
FEnd = Record->field_end();
F != FEnd; ++F) {
if (F->hasInClassInitializer())
continue;
if (F->getType()->isReferenceType() ||
(F->getType().isConstQualified() && F->getType()->isScalarType())) {
if (!Complained) {
@ -3066,6 +3167,11 @@ void Sema::CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *CD) {
ImplicitExceptionSpecification Spec
= ComputeDefaultedDefaultCtorExceptionSpec(CD->getParent());
FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
if (EPI.ExceptionSpecType == EST_Delayed) {
// Exception specification depends on some deferred part of the class. We'll
// try again when the class's definition has been fully processed.
return;
}
const FunctionProtoType *CtorType = CD->getType()->getAs<FunctionProtoType>(),
*ExceptionType = Context.getFunctionType(
Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
@ -3383,12 +3489,15 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) {
for (CXXRecordDecl::field_iterator FI = RD->field_begin(),
FE = RD->field_end();
FI != FE; ++FI) {
if (FI->isInvalidDecl())
continue;
QualType FieldType = Context.getBaseElementType(FI->getType());
CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl();
// -- any non-static data member with no brace-or-equal-initializer is of
// reference type
if (FieldType->isReferenceType())
if (FieldType->isReferenceType() && !FI->hasInClassInitializer())
return true;
// -- X is a union and all its variant members are of const-qualified type
@ -3413,6 +3522,7 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) {
// array thereof) with no brace-or-equal-initializer does not have a
// user-provided default constructor
if (FieldType.isConstQualified() &&
!FI->hasInClassInitializer() &&
!FieldRecord->hasUserProvidedDefaultConstructor())
return true;
@ -3445,18 +3555,21 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) {
continue;
}
// -- any non-static data member ... has class type M (or array thereof)
// and either M has no default constructor or overload resolution as
// applied to M's default constructor results in an ambiguity or in a
// function that is deleted or inaccessible from the defaulted default
// constructor.
CXXConstructorDecl *FieldDefault = LookupDefaultConstructor(FieldRecord);
if (!FieldDefault || FieldDefault->isDeleted())
return true;
if (CheckConstructorAccess(Loc, FieldDefault, FieldDefault->getAccess(),
PDiag()) != AR_accessible)
return true;
} else if (!Union && FieldType.isConstQualified()) {
// -- any non-static data member with no brace-or-equal-initializer has
// class type M (or array thereof) and either M has no default
// constructor or overload resolution as applied to M's default
// constructor results in an ambiguity or in a function that is deleted
// or inaccessible from the defaulted default constructor.
if (!FI->hasInClassInitializer()) {
CXXConstructorDecl *FieldDefault = LookupDefaultConstructor(FieldRecord);
if (!FieldDefault || FieldDefault->isDeleted())
return true;
if (CheckConstructorAccess(Loc, FieldDefault, FieldDefault->getAccess(),
PDiag()) != AR_accessible)
return true;
}
} else if (!Union && FieldType.isConstQualified() &&
!FI->hasInClassInitializer()) {
// -- any non-variant non-static data member of const-qualified type (or
// array thereof) with no brace-or-equal-initializer does not have a
// user-provided default constructor
@ -5905,7 +6018,12 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) {
for (RecordDecl::field_iterator F = ClassDecl->field_begin(),
FEnd = ClassDecl->field_end();
F != FEnd; ++F) {
if (const RecordType *RecordTy
if (F->hasInClassInitializer()) {
if (Expr *E = F->getInClassInitializer())
ExceptSpec.CalledExpr(E);
else if (!F->isInvalidDecl())
ExceptSpec.SetDelayed();
} else if (const RecordType *RecordTy
= Context.getBaseElementType(F->getType())->getAs<RecordType>()) {
CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
CXXConstructorDecl *Constructor = LookupDefaultConstructor(FieldRecDecl);
@ -6001,6 +6119,59 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation,
}
}
/// Get any existing defaulted default constructor for the given class. Do not
/// implicitly define one if it does not exist.
static CXXConstructorDecl *getDefaultedDefaultConstructorUnsafe(Sema &Self,
CXXRecordDecl *D) {
ASTContext &Context = Self.Context;
QualType ClassType = Context.getTypeDeclType(D);
DeclarationName ConstructorName
= Context.DeclarationNames.getCXXConstructorName(
Context.getCanonicalType(ClassType.getUnqualifiedType()));
DeclContext::lookup_const_iterator Con, ConEnd;
for (llvm::tie(Con, ConEnd) = D->lookup(ConstructorName);
Con != ConEnd; ++Con) {
// A function template cannot be defaulted.
if (isa<FunctionTemplateDecl>(*Con))
continue;
CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(*Con);
if (Constructor->isDefaultConstructor())
return Constructor->isDefaulted() ? Constructor : 0;
}
return 0;
}
void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) {
if (!D) return;
AdjustDeclIfTemplate(D);
CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(D);
CXXConstructorDecl *CtorDecl
= getDefaultedDefaultConstructorUnsafe(*this, ClassDecl);
if (!CtorDecl) return;
// Compute the exception specification for the default constructor.
const FunctionProtoType *CtorTy =
CtorDecl->getType()->castAs<FunctionProtoType>();
if (CtorTy->getExceptionSpecType() == EST_Delayed) {
ImplicitExceptionSpecification Spec =
ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl);
FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
assert(EPI.ExceptionSpecType != EST_Delayed);
CtorDecl->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
}
// If the default constructor is explicitly defaulted, checking the exception
// specification is deferred until now.
if (!CtorDecl->isInvalidDecl() && CtorDecl->isExplicitlyDefaulted() &&
!ClassDecl->isDependentType())
CheckExplicitlyDefaultedDefaultConstructor(CtorDecl);
}
void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
// We start with an initial pass over the base classes to collect those that
// inherit constructors from. If there are none, we can forgo all further
@ -6094,7 +6265,9 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
// Build up a function type for this particular constructor.
// FIXME: The working paper does not consider that the exception spec
// for the inheriting constructor might be larger than that of the
// source. This code doesn't yet, either.
// source. This code doesn't yet, either. When it does, this code will
// need to be delayed until after exception specifications and in-class
// member initializers are attached.
const Type *NewCtorType;
if (params == maxParams)
NewCtorType = BaseCtorType;

View File

@ -298,8 +298,6 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
// - both are non-throwing, regardless of their form,
// - both have the form noexcept(constant-expression) and the constant-
// expressions are equivalent,
// - one exception-specification is a noexcept-specification allowing all
// exceptions and the other is of the form throw(type-id-list), or
// - both are dynamic-exception-specifications that have the same set of
// adjusted types.
//
@ -307,8 +305,6 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
// of the form throw(), noexcept, or noexcept(constant-expression) where the
// constant-expression yields true.
//
// CWG 1073 Proposed resolution: Strike the third bullet above.
//
// C++0x [except.spec]p4: If any declaration of a function has an exception-
// specifier that is not a noexcept-specification allowing all exceptions,
// all declarations [...] of that function shall have a compatible
@ -320,6 +316,9 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
ExceptionSpecificationType OldEST = Old->getExceptionSpecType();
ExceptionSpecificationType NewEST = New->getExceptionSpecType();
assert(OldEST != EST_Delayed && NewEST != EST_Delayed &&
"Shouldn't see unknown exception specifications here");
// Shortcut the case where both have no spec.
if (OldEST == EST_None && NewEST == EST_None)
return false;
@ -506,6 +505,9 @@ bool Sema::CheckExceptionSpecSubset(
ExceptionSpecificationType SubEST = Subset->getExceptionSpecType();
assert(SuperEST != EST_Delayed && SubEST != EST_Delayed &&
"Shouldn't see unknown exception specifications here");
// It does not. If the subset contains everything, we've failed.
if (SubEST == EST_None || SubEST == EST_MSAny) {
Diag(SubLoc, DiagID);

View File

@ -1333,8 +1333,8 @@ Sema::BuildAnonymousStructUnionMemberReference(const CXXScopeSpec &SS,
// We've found a member of an anonymous struct/union that is
// inside a non-anonymous struct/union, so in a well-formed
// program our base object expression is "this".
CXXMethodDecl *method = tryCaptureCXXThis();
if (!method) {
QualType ThisTy = getAndCaptureCurrentThisType();
if (ThisTy.isNull()) {
Diag(loc, diag::err_invalid_member_use_in_static_method)
<< indirectField->getDeclName();
return ExprError();
@ -1342,10 +1342,9 @@ Sema::BuildAnonymousStructUnionMemberReference(const CXXScopeSpec &SS,
// Our base object expression is "this".
baseObjectExpr =
new (Context) CXXThisExpr(loc, method->getThisType(Context),
/*isImplicit=*/ true);
new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/ true);
baseObjectIsPointer = true;
baseQuals = Qualifiers::fromCVRMask(method->getTypeQualifiers());
baseQuals = ThisTy->castAs<PointerType>()->getPointeeType().getQualifiers();
}
// Build the implicit member references to the field of the
@ -1492,14 +1491,23 @@ enum IMAKind {
/// conservatively answer "yes", in which case some errors will simply
/// not be caught until template-instantiation.
static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
Scope *CurScope,
const LookupResult &R) {
assert(!R.empty() && (*R.begin())->isCXXClassMember());
DeclContext *DC = SemaRef.getFunctionLevelDeclContext();
bool isStaticContext =
(!isa<CXXMethodDecl>(DC) ||
cast<CXXMethodDecl>(DC)->isStatic());
// C++0x [expr.prim]p4:
// Otherwise, if a member-declarator declares a non-static data member
// of a class X, the expression this is a prvalue of type "pointer to X"
// within the optional brace-or-equal-initializer.
if (CurScope->getFlags() & Scope::ThisScope)
isStaticContext = false;
if (R.isUnresolvableResult())
return isStaticContext ? IMA_Unresolved_StaticContext : IMA_Unresolved;
@ -1547,8 +1555,11 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
return IMA_Error_StaticContext;
}
CXXRecordDecl *
contextClass = cast<CXXMethodDecl>(DC)->getParent()->getCanonicalDecl();
CXXRecordDecl *contextClass;
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC))
contextClass = MD->getParent()->getCanonicalDecl();
else
contextClass = cast<CXXRecordDecl>(DC);
// [class.mfct.non-static]p3:
// ...is used in the body of a non-static member function of class X,
@ -2026,7 +2037,7 @@ ExprResult
Sema::BuildPossibleImplicitMemberExpr(const CXXScopeSpec &SS,
LookupResult &R,
const TemplateArgumentListInfo *TemplateArgs) {
switch (ClassifyImplicitMemberAccess(*this, R)) {
switch (ClassifyImplicitMemberAccess(*this, CurScope, R)) {
case IMA_Instance:
return BuildImplicitMemberExpr(SS, R, TemplateArgs, true);
@ -2469,19 +2480,18 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,
// If this is known to be an instance access, go ahead and build an
// implicit 'this' expression now.
// 'this' expression now.
CXXMethodDecl *method = tryCaptureCXXThis();
assert(method && "didn't correctly pre-flight capture of 'this'");
QualType ThisTy = getAndCaptureCurrentThisType();
assert(!ThisTy.isNull() && "didn't correctly pre-flight capture of 'this'");
QualType thisType = method->getThisType(Context);
Expr *baseExpr = 0; // null signifies implicit access
if (IsKnownInstance) {
SourceLocation Loc = R.getNameLoc();
if (SS.getRange().isValid())
Loc = SS.getRange().getBegin();
baseExpr = new (Context) CXXThisExpr(loc, thisType, /*isImplicit=*/true);
baseExpr = new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/true);
}
return BuildMemberReferenceExpr(baseExpr, thisType,
return BuildMemberReferenceExpr(baseExpr, ThisTy,
/*OpLoc*/ SourceLocation(),
/*IsArrow*/ true,
SS,

View File

@ -17,6 +17,7 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/TemplateDeduction.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
@ -575,42 +576,54 @@ ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E) {
return Owned(E);
}
CXXMethodDecl *Sema::tryCaptureCXXThis() {
QualType Sema::getAndCaptureCurrentThisType() {
// Ignore block scopes: we can capture through them.
// Ignore nested enum scopes: we'll diagnose non-constant expressions
// where they're invalid, and other uses are legitimate.
// Don't ignore nested class scopes: you can't use 'this' in a local class.
DeclContext *DC = CurContext;
unsigned NumBlocks = 0;
while (true) {
if (isa<BlockDecl>(DC)) DC = cast<BlockDecl>(DC)->getDeclContext();
else if (isa<EnumDecl>(DC)) DC = cast<EnumDecl>(DC)->getDeclContext();
if (isa<BlockDecl>(DC)) {
DC = cast<BlockDecl>(DC)->getDeclContext();
++NumBlocks;
} else if (isa<EnumDecl>(DC))
DC = cast<EnumDecl>(DC)->getDeclContext();
else break;
}
// If we're not in an instance method, error out.
CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(DC);
if (!method || !method->isInstance())
return 0;
QualType ThisTy;
if (CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(DC)) {
if (method && method->isInstance())
ThisTy = method->getThisType(Context);
} else if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC)) {
// C++0x [expr.prim]p4:
// Otherwise, if a member-declarator declares a non-static data member
// of a class X, the expression this is a prvalue of type "pointer to X"
// within the optional brace-or-equal-initializer.
Scope *S = getScopeForContext(DC);
if (!S || S->getFlags() & Scope::ThisScope)
ThisTy = Context.getPointerType(Context.getRecordType(RD));
}
// Mark that we're closing on 'this' in all the block scopes, if applicable.
for (unsigned idx = FunctionScopes.size() - 1;
isa<BlockScopeInfo>(FunctionScopes[idx]);
--idx)
cast<BlockScopeInfo>(FunctionScopes[idx])->CapturesCXXThis = true;
// Mark that we're closing on 'this' in all the block scopes we ignored.
if (!ThisTy.isNull())
for (unsigned idx = FunctionScopes.size() - 1;
NumBlocks; --idx, --NumBlocks)
cast<BlockScopeInfo>(FunctionScopes[idx])->CapturesCXXThis = true;
return method;
return ThisTy;
}
ExprResult Sema::ActOnCXXThis(SourceLocation loc) {
ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
/// is a non-lvalue expression whose value is the address of the object for
/// which the function is called.
CXXMethodDecl *method = tryCaptureCXXThis();
if (!method) return Diag(loc, diag::err_invalid_this_use);
QualType ThisTy = getAndCaptureCurrentThisType();
if (ThisTy.isNull()) return Diag(Loc, diag::err_invalid_this_use);
return Owned(new (Context) CXXThisExpr(loc, method->getThisType(Context),
/*isImplicit=*/false));
return Owned(new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit=*/false));
}
ExprResult
@ -2663,7 +2676,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT,
return true;
bool FoundAssign = false;
bool AllNoThrow = true;
DeclarationName Name = C.DeclarationNames.getCXXOperatorName(OO_Equal);
LookupResult Res(Self, DeclarationNameInfo(Name, KeyLoc),
Sema::LookupOrdinaryName);
@ -2675,15 +2687,15 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT,
FoundAssign = true;
const FunctionProtoType *CPT
= Operator->getType()->getAs<FunctionProtoType>();
if (!CPT->isNothrow(Self.Context)) {
AllNoThrow = false;
break;
}
if (CPT->getExceptionSpecType() == EST_Delayed)
return false;
if (!CPT->isNothrow(Self.Context))
return false;
}
}
}
return FoundAssign && AllNoThrow;
return FoundAssign;
}
return false;
case UTT_HasNothrowCopy:
@ -2700,7 +2712,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT,
return true;
bool FoundConstructor = false;
bool AllNoThrow = true;
unsigned FoundTQs;
DeclContext::lookup_const_iterator Con, ConEnd;
for (llvm::tie(Con, ConEnd) = Self.LookupConstructors(RD);
@ -2715,16 +2726,16 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT,
FoundConstructor = true;
const FunctionProtoType *CPT
= Constructor->getType()->getAs<FunctionProtoType>();
if (CPT->getExceptionSpecType() == EST_Delayed)
return false;
// FIXME: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
if (!CPT->isNothrow(Self.Context) || CPT->getNumArgs() > 1) {
AllNoThrow = false;
break;
}
if (!CPT->isNothrow(Self.Context) || CPT->getNumArgs() > 1)
return false;
}
}
return FoundConstructor && AllNoThrow;
return FoundConstructor;
}
return false;
case UTT_HasNothrowConstructor:
@ -2750,6 +2761,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT,
if (Constructor->isDefaultConstructor()) {
const FunctionProtoType *CPT
= Constructor->getType()->getAs<FunctionProtoType>();
if (CPT->getExceptionSpecType() == EST_Delayed)
return false;
// TODO: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
return CPT->isNothrow(Self.Context) && CPT->getNumArgs() == 0;

View File

@ -13,6 +13,7 @@
#include "clang/Sema/SemaInternal.h"
#include "TreeTransform.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
@ -1744,6 +1745,8 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
TemplateDeclInstantiator Instantiator(*this, Instantiation, TemplateArgs);
llvm::SmallVector<Decl*, 4> Fields;
llvm::SmallVector<std::pair<FieldDecl*, FieldDecl*>, 4>
FieldsWithMemberInitializers;
for (RecordDecl::decl_iterator Member = Pattern->decls_begin(),
MemberEnd = Pattern->decls_end();
Member != MemberEnd; ++Member) {
@ -1766,9 +1769,13 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
Decl *NewMember = Instantiator.Visit(*Member);
if (NewMember) {
if (FieldDecl *Field = dyn_cast<FieldDecl>(NewMember))
if (FieldDecl *Field = dyn_cast<FieldDecl>(NewMember)) {
Fields.push_back(Field);
else if (NewMember->isInvalidDecl())
FieldDecl *OldField = cast<FieldDecl>(*Member);
if (OldField->getInClassInitializer())
FieldsWithMemberInitializers.push_back(std::make_pair(OldField,
Field));
} else if (NewMember->isInvalidDecl())
Invalid = true;
} else {
// FIXME: Eventually, a NULL return will mean that one of the
@ -1782,6 +1789,43 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
Fields.data(), Fields.size(), SourceLocation(), SourceLocation(),
0);
CheckCompletedCXXClass(Instantiation);
// Attach any in-class member initializers now the class is complete.
for (unsigned I = 0, N = FieldsWithMemberInitializers.size(); I != N; ++I) {
FieldDecl *OldField = FieldsWithMemberInitializers[I].first;
FieldDecl *NewField = FieldsWithMemberInitializers[I].second;
Expr *OldInit = OldField->getInClassInitializer();
ExprResult NewInit = SubstExpr(OldInit, TemplateArgs);
// If the initialization is no longer dependent, check it now.
if ((OldField->getType()->isDependentType() || OldInit->isTypeDependent())
&& !NewField->getType()->isDependentType()
&& !NewInit.get()->isTypeDependent()) {
// FIXME: handle list-initialization
SourceLocation EqualLoc = NewField->getLocation();
NewInit = PerformCopyInitialization(
InitializedEntity::InitializeMember(NewField), EqualLoc,
NewInit.release());
if (!NewInit.isInvalid()) {
CheckImplicitConversions(NewInit.get(), EqualLoc);
// C++0x [class.base.init]p7:
// The initialization of each base and member constitutes a
// full-expression.
NewInit = MaybeCreateExprWithCleanups(NewInit);
}
}
if (NewInit.isInvalid())
NewField->setInvalidDecl();
else
NewField->setInClassInitializer(NewInit.release());
}
if (!FieldsWithMemberInitializers.empty())
ActOnFinishDelayedMemberInitializers(Instantiation);
if (Instantiation->isInvalidDecl())
Invalid = true;
else {

View File

@ -477,6 +477,7 @@ Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) {
D->getLocation(),
D->isMutable(),
BitWidth,
D->hasInClassInitializer(),
D->getTypeSpecStartLoc(),
D->getAccess(),
0);

View File

@ -1593,6 +1593,8 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S,
Error = 0; // Function prototype
break;
case Declarator::MemberContext:
if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)
break;
switch (cast<TagDecl>(CurContext)->getTagKind()) {
case TTK_Enum: assert(0 && "unhandled tag kind"); break;
case TTK_Struct: Error = 1; /* Struct member */ break;

View File

@ -6697,8 +6697,12 @@ template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformCXXThisExpr(CXXThisExpr *E) {
DeclContext *DC = getSema().getFunctionLevelDeclContext();
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC);
QualType T = MD->getThisType(getSema().Context);
QualType T;
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC))
T = MD->getThisType(getSema().Context);
else
T = getSema().Context.getPointerType(
getSema().Context.getRecordType(cast<CXXRecordDecl>(DC)));
if (!getDerived().AlwaysRebuild() && T == E->getType())
return SemaRef.Owned(E);

View File

@ -677,8 +677,11 @@ void ASTDeclReader::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) {
void ASTDeclReader::VisitFieldDecl(FieldDecl *FD) {
VisitDeclaratorDecl(FD);
FD->setMutable(Record[Idx++]);
if (Record[Idx++])
int BitWidthOrInitializer = Record[Idx++];
if (BitWidthOrInitializer == 1)
FD->setBitWidth(Reader.ReadExpr(F));
else if (BitWidthOrInitializer == 2)
FD->setInClassInitializer(Reader.ReadExpr(F));
if (!FD->getDeclName()) {
FieldDecl *Tmpl = cast_or_null<FieldDecl>(Reader.GetDecl(Record[Idx++]));
if (Tmpl)
@ -1649,7 +1652,7 @@ Decl *ASTReader::ReadDeclRecord(unsigned Index, DeclID ID) {
break;
case DECL_FIELD:
D = FieldDecl::Create(*Context, 0, SourceLocation(), SourceLocation(), 0,
QualType(), 0, 0, false);
QualType(), 0, 0, false, false);
break;
case DECL_INDIRECTFIELD:
D = IndirectFieldDecl::Create(*Context, 0, SourceLocation(), 0, QualType(),

View File

@ -599,9 +599,11 @@ void ASTDeclWriter::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) {
void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) {
VisitDeclaratorDecl(D);
Record.push_back(D->isMutable());
Record.push_back(D->getBitWidth()? 1 : 0);
Record.push_back(D->getBitWidth()? 1 : D->hasInClassInitializer() ? 2 : 0);
if (D->getBitWidth())
Writer.AddStmt(D->getBitWidth());
else if (D->hasInClassInitializer())
Writer.AddStmt(D->getInClassInitializer());
if (!D->getDeclName())
Writer.AddDeclRef(Context.getInstantiatedFromUnnamedFieldDecl(D), Record);
@ -612,6 +614,7 @@ void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) {
!D->isReferenced() &&
D->getPCHLevel() == 0 &&
!D->getBitWidth() &&
!D->hasInClassInitializer() &&
!D->hasExtInfo() &&
!ObjCIvarDecl::classofKind(D->getKind()) &&
!ObjCAtDefsFieldDecl::classofKind(D->getKind()) &&

View File

@ -0,0 +1,9 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
int f();
struct S
{
int a = f(); // ok
int b = g(); // expected-error {{use of undeclared identifier 'g'}}
};

View File

@ -22,14 +22,21 @@ void f() {
new const auto (0);
new (auto) (0.0);
#if 0
// When clang supports for-range:
for (auto i : {1,2,3}) {
int arr[] = {1, 2, 3};
for (auto i : arr) {
}
// When clang supports inline initialization of members.
class X {
static const auto &n = 'x';
};
#endif
}
class X {
static const auto n = 'x';
auto m = 0; // expected-error {{'auto' not allowed in non-static class member}}
};
struct S {
static const auto a; // expected-error {{declaration of variable 'a' with type 'auto const' requires an initializer}}
static const auto b = 0;
static const int c;
};
const int S::b;
const auto S::c = 0;

View File

@ -3,13 +3,13 @@
struct S {
virtual ~S();
auto a; // expected-error{{'auto' not allowed in struct member}}
auto *b; // expected-error{{'auto' not allowed in struct member}}
const auto c; // expected-error{{'auto' not allowed in struct member}}
auto a; // expected-error{{'auto' not allowed in non-static struct member}}
auto *b; // expected-error{{'auto' not allowed in non-static struct member}}
const auto c; // expected-error{{'auto' not allowed in non-static struct member}}
void f() throw (auto); // expected-error{{'auto' not allowed here}}
friend auto; // expected-error{{'auto' not allowed in struct member}}
friend auto; // expected-error{{'auto' not allowed in non-static struct member}}
operator auto(); // expected-error{{'auto' not allowed here}}
};

View File

@ -7,8 +7,8 @@ template<typename T> using Y = struct { // expected-error {{can not be defined i
class K {
virtual ~K();
// FIXME: the diagnostic here isn't very good
operator struct S {} (); // expected-error 2{{}}
// FIXME: the diagnostic here is really bad
operator struct S {} (); // expected-error 2{{}} expected-note {{}}
};
void f() {

View File

@ -0,0 +1,63 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
// An aggregate is an array or a class...
struct Aggr {
private:
static const int n;
void f();
protected:
struct Inner { int m; };
public:
bool &br;
};
bool b;
Aggr ag = { b };
// with no user-provided constructors, ...
struct NonAggr1a {
NonAggr1a(int, int);
int k;
};
// In C++03, this is {{non-aggregate type 'NonAggr1a'}}.
// In C++0x, 'user-provided' is only defined for special member functions, so
// this type is considered to be an aggregate. This is probably a langauge
// defect.
NonAggr1a na1a = { 42 };
struct NonAggr1b {
NonAggr1b(const NonAggr1b &);
int k;
};
NonAggr1b na1b = { 42 }; // expected-error {{non-aggregate type 'NonAggr1b'}}
// no brace-or-equal-initializers for non-static data members, ...
struct NonAggr2 {
int m = { 123 };
};
NonAggr2 na2 = { 42 }; // expected-error {{non-aggregate type 'NonAggr2'}}
// no private...
struct NonAggr3 {
private:
int n;
};
NonAggr3 na3 = { 42 }; // expected-error {{non-aggregate type 'NonAggr3'}}
// or protected non-static data members, ...
struct NonAggr4 {
protected:
int n;
};
NonAggr4 na4 = { 42 }; // expected-error {{non-aggregate type 'NonAggr4'}}
// no base classes, ...
struct NonAggr5 : Aggr {
};
NonAggr5 na5 = { b }; // expected-error {{non-aggregate type 'NonAggr5'}}
// and no virtual functions.
struct NonAggr6 {
virtual void f();
int n;
};
NonAggr6 na6 = { 42 }; // expected-error {{non-aggregate type 'NonAggr6'}}

View File

@ -0,0 +1,44 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
struct NoDefault {
NoDefault() = delete; // expected-note {{here}}
NoDefault(int);
};
struct Explicit { // expected-note {{candidate}} expected-note {{here}}
explicit Explicit(int);
};
struct NoCopy {
NoCopy();
NoCopy(const NoCopy &) = delete; // expected-note {{here}}
};
struct NoMove {
NoMove();
NoMove(NoMove &&) = delete; // expected-note {{here}}
};
class Private {
Private(int); // expected-note {{here}}
public:
Private();
};
class Friend {
friend class S;
Friend(int);
};
class S {
NoDefault nd1;
NoDefault nd2 = 42;
Explicit e1; // expected-note {{here}}
Explicit e2 = 42; // expected-error {{no viable conversion}}
NoCopy nc = NoCopy(); // expected-error {{call to deleted}}
NoMove nm = NoMove(); // expected-error {{call to deleted}}
Private p = 42; // expected-error {{private constructor}}
Friend f = 42;
S() {} // expected-error {{call to deleted constructor of 'NoDefault'}} \
expected-error {{must explicitly initialize the member 'e1' which does not have a default constructor}}
S(int) : nd1(42), e1(42) {}
};
// FIXME: test the other forms which use copy-initialization

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -verify %s
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -verify -std=c++0x %s
struct A { };
struct B { };
struct C { };
@ -27,3 +27,15 @@ void test_CA() {
CA2 &(CA2::*captr3)(const CA2&) throw(A) = &CA2::operator=; // expected-error{{target exception specification is not superset of source}}
CA2 &(CA2::*captr4)(const CA2&) throw(B) = &CA2::operator=; // expected-error{{target exception specification is not superset of source}}
}
// In-class member initializers.
struct IC0 {
int inClassInit = 0;
};
struct IC1 {
int inClassInit = (throw B(), 0);
};
// FIXME: the exception specification on the default constructor is wrong:
// we cannot currently compute the set of thrown types.
static_assert(noexcept(IC0()), "IC0() does not throw");
static_assert(!noexcept(IC1()), "IC1() throws");

View File

@ -0,0 +1,12 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
struct S {
int *j = &nonexistent; // expected-error {{use of undeclared identifier 'nonexistent'}}
int *m = &n; // ok
int n = f(); // ok
int f();
};
int i = sizeof(S::m); // ok
int j = sizeof(S::m + 42); // ok

View File

@ -0,0 +1,10 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
struct S {
S *p = this; // ok
decltype(this) q; // expected-error {{invalid use of 'this' outside of a nonstatic member function}} \
expected-error {{C++ requires a type specifier for all declarations}}
int arr[sizeof(this)]; // expected-error {{invalid use of 'this' outside of a nonstatic member function}}
int sz = sizeof(this); // ok
};

View File

@ -0,0 +1,173 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++0x
struct DefaultedDefCtor1 {};
struct DefaultedDefCtor2 { DefaultedDefCtor2() = default; };
struct DeletedDefCtor { DeletedDefCtor() = delete; DeletedDefCtor(int); };
class PrivateDefCtor { PrivateDefCtor() = default; public: PrivateDefCtor(int); };
struct DeletedDtor { ~DeletedDtor() = delete; };
class PrivateDtor { ~PrivateDtor() = default; };
class Friend {
Friend() = default; ~Friend() = default;
friend struct NotDeleted6c;
friend struct NotDeleted7i;
friend struct NotDeleted7j;
friend struct NotDeleted7k;
};
struct UserProvidedDefCtor { UserProvidedDefCtor() {} };
int n;
// A defaulted default constructor for a class X is defined as deleted if:
// - X is a union-like class that has a variant member with a non-trivial
// default constructor,
union Deleted1a { UserProvidedDefCtor u; }; // expected-note {{deleted here}}
Deleted1a d1a; // expected-error {{deleted constructor}}
// FIXME: treating this as having a deleted default constructor is probably a
// bug in the standard.
union Deleted1b { UserProvidedDefCtor u = UserProvidedDefCtor(); }; // expected-note {{deleted here}}
Deleted1b d1b; // expected-error {{deleted constructor}}
union NotDeleted1a { DefaultedDefCtor1 nu; };
NotDeleted1a nd1a;
// FIXME: clang implements the pre-FDIS rule, under which DefaultedDefCtor2's
// default constructor is non-trivial.
union NotDeleted1b { DefaultedDefCtor2 nu; }; // unexpected-note {{deleted here}}
NotDeleted1b nd1b; // unexpected-error {{deleted constructor}}
// - any non-static data member with no brace-or-equal-initializer is of
// reference type,
class Deleted2a { Deleted2a() = default; int &a; }; // expected-note {{deleted here}}
Deleted2a d2a; // expected-error {{deleted constructor}}
class NotDeleted2a { int &a = n; };
NotDeleted2a nd2a;
class NotDeleted2b { int &a = error; }; // expected-error {{undeclared identifier}}
NotDeleted2b nd2b;
// - any non-variant non-static data member of const qualified type (or array
// thereof) with no brace-or-equal-initializer does not have a user-provided
// default constructor,
class Deleted3a { const int a; }; // expected-note {{here}} \
expected-warning {{does not declare any constructor}} \
expected-note {{will never be initialized}}
Deleted3a d3a; // expected-error {{deleted constructor}}
class Deleted3b { const DefaultedDefCtor1 a[42]; }; // expected-note {{here}}
Deleted3b d3b; // expected-error {{deleted constructor}}
// FIXME: clang implements the pre-FDIS rule, under which DefaultedDefCtor2's
// default constructor is user-provided.
class Deleted3c { const DefaultedDefCtor2 a; }; // desired-note {{here}}
Deleted3c d3c; // desired-error {{deleted constructor}}
class NotDeleted3a { const int a = 0; };
NotDeleted3a nd3a;
class NotDeleted3b { const DefaultedDefCtor1 a[42] = {}; };
NotDeleted3b nd3b;
class NotDeleted3c { const DefaultedDefCtor2 a = DefaultedDefCtor2(); };
NotDeleted3c nd3c;
union NotDeleted3d { const int a; int b; };
NotDeleted3d nd3d;
// FIXME: this class should not have a deleted default constructor.
union NotDeleted3e { const DefaultedDefCtor1 a[42]; int b; }; // unexpected-note {{here}}
NotDeleted3e nd3e; // unexpected-error {{deleted constructor}}
// FIXME: clang implements the pre-FDIS rule, under which DefaultedDefCtor2 is
// non-trivial.
union NotDeleted3f { const DefaultedDefCtor2 a; int b; }; // unexpected-note {{here}}
NotDeleted3f nd3f; // unexpected-error {{deleted constructor}}
// - X is a union and all of its variant members are of const-qualified type (or
// array thereof),
union Deleted4a { const int a; const int b; const UserProvidedDefCtor c; }; // expected-note {{here}}
Deleted4a d4a; // expected-error {{deleted constructor}}
union Deleted4b { const int a; int b; };
Deleted4b d4b;
// - X is a non-union class and all members of any anonymous union member are of
// const-qualified type (or array thereof),
struct Deleted5a { union { const int a; }; union { int b; }; }; // expected-note {{here}}
Deleted5a d5a; // expected-error {{deleted constructor}}
struct Deleted5b { union { const int a; int b; }; union { const int c; int d; }; };
Deleted5b d5b;
// - any direct or virtual base class, or non-static data member with no
// brace-or-equal-initializer, has class type M (or array thereof) and either
// M has no default constructor or overload resolution as applied to M's default
// constructor results in an ambiguity or in a function that is deleted or
// inaccessible from the defaulted default constructor, or
struct Deleted6a : Deleted2a {}; // expected-note {{here}}
Deleted6a d6a; // expected-error {{deleted constructor}}
struct Deleted6b : virtual Deleted2a {}; // expected-note {{here}}
Deleted6b d6b; // expected-error {{deleted constructor}}
struct Deleted6c { Deleted2a a; }; // expected-note {{here}}
Deleted6c d6c; // expected-error {{deleted constructor}}
struct Deleted6d { DeletedDefCtor a; }; // expected-note {{here}}
Deleted6d d6d; // expected-error {{deleted constructor}}
struct NotDeleted6a { DeletedDefCtor a = 0; };
NotDeleted6a nd6a;
struct Deleted6e { PrivateDefCtor a; }; // expected-note {{here}}
Deleted6e d6e; // expected-error {{deleted constructor}}
struct NotDeleted6b { PrivateDefCtor a = 0; };
NotDeleted6b nd6b;
struct NotDeleted6c { Friend a; };
NotDeleted6c nd6c;
// - any direct or virtual base class or non-static data member has a type with
// a destructor that is deleted or inaccessible from the defaulted default
// constructor.
struct Deleted7a : DeletedDtor {}; // expected-note {{here}}
Deleted7a d7a; // expected-error {{deleted constructor}}
struct Deleted7b : virtual DeletedDtor {}; // expected-note {{here}}
Deleted7b d7b; // expected-error {{deleted constructor}}
struct Deleted7c { DeletedDtor a; }; // expected-note {{here}}
Deleted7c d7c; // expected-error {{deleted constructor}}
struct Deleted7d { DeletedDtor a = {}; }; // expected-note {{here}}
Deleted7d d7d; // expected-error {{deleted constructor}}
struct Deleted7e : PrivateDtor {}; // expected-note {{here}}
Deleted7e d7e; // expected-error {{deleted constructor}}
struct Deleted7f : virtual PrivateDtor {}; // expected-note {{here}}
Deleted7f d7f; // expected-error {{deleted constructor}}
struct Deleted7g { PrivateDtor a; }; // expected-note {{here}}
Deleted7g d7g; // expected-error {{deleted constructor}}
struct Deleted7h { PrivateDtor a = {}; }; // expected-note {{here}}
Deleted7h d7h; // expected-error {{deleted constructor}}
struct NotDeleted7i : Friend {};
NotDeleted7i d7i;
struct NotDeleted7j : virtual Friend {};
NotDeleted7j d7j;
struct NotDeleted7k { Friend a; };
NotDeleted7k d7k;
class Trivial { static const int n = 42; };
static_assert(__has_trivial_constructor(Trivial), "Trivial is nontrivial");
// A default constructor is trivial if it is not user-provided and if:
class NonTrivialDefCtor1 { NonTrivialDefCtor1(); };
static_assert(!__has_trivial_constructor(NonTrivialDefCtor1), "NonTrivialDefCtor1 is trivial");
// - its class has no virtual functions (10.3) and no virtual base classes (10.1), and
class NonTrivialDefCtor2 { virtual void f(); };
static_assert(!__has_trivial_constructor(NonTrivialDefCtor2), "NonTrivialDefCtor2 is trivial");
class NonTrivialDefCtor3 : virtual Trivial {};
static_assert(!__has_trivial_constructor(NonTrivialDefCtor3), "NonTrivialDefCtor3 is trivial");
// - no non-static data member of its class has a brace-or-equal-initializer, and
class NonTrivialDefCtor4 { int m = 52; };
static_assert(!__has_trivial_constructor(NonTrivialDefCtor4), "NonTrivialDefCtor4 is trivial");
// - all the direct base classes of its class have trivial default constructors, and
class NonTrivialDefCtor5 : NonTrivialDefCtor1 {};
static_assert(!__has_trivial_constructor(NonTrivialDefCtor5), "NonTrivialDefCtor5 is trivial");
// - for all the non-static data members of its class that are of class type (or array thereof), each such class
// has a trivial default constructor.
class NonTrivialDefCtor6 { NonTrivialDefCtor1 t; };
static_assert(!__has_trivial_constructor(NonTrivialDefCtor6), "NonTrivialDefCtor5 is trivial");
// Otherwise, the default constructor is non-trivial.
class Trivial2 { Trivial2() = delete; };
//static_assert(__has_trivial_constructor(Trivial2), "NonTrivialDefCtor2 is trivial");
// FIXME: clang implements the pre-FDIS rule, under which this class is non-trivial.
static_assert(!__has_trivial_constructor(Trivial2), "NonTrivialDefCtor2 is trivial");
class Trivial3 { Trivial3() = default; };
//static_assert(__has_trivial_constructor(Trivial3), "NonTrivialDefCtor3 is trivial");
// FIXME: clang implements the pre-FDIS rule, under which this class is non-trivial.
static_assert(!__has_trivial_constructor(Trivial3), "NonTrivialDefCtor3 is trivial");

View File

@ -0,0 +1,23 @@
// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s
int n;
struct S {
int &a; // expected-note 2{{here}}
int &b = n;
S() {} // expected-error {{constructor for 'S' must explicitly initialize the reference member 'a'}}
S(int) : a(n) {} // ok
S(char) : b(n) {} // expected-error {{constructor for 'S' must explicitly initialize the reference member 'a'}}
S(double) : a(n), b(n) {} // ok
};
union U {
int a = 0;
char b = 'x';
// FIXME: these should all be rejected
U() {} // desired-error {{at most one member of a union may be initialized}}
U(int) : a(1) {} // desired-error {{at most one member of a union may be initialized}}
U(char) : b('y') {} // desired-error {{at most one member of a union may be initialized}}
U(double) : a(1), b('y') {} // desired-error {{at most one member of a union may be initialized}}
};

View File

@ -0,0 +1,36 @@
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++0x %s -O1 -emit-llvm -o - | FileCheck %s
struct S {
int n = 10;
int m = 2 * n;
S() {}
S(int a) : n(a) {}
S(int a, int b) : n(a), m(b) {}
struct T {
T *that = this;
};
};
template<typename T>
struct U {
T *r = &q;
T q = 42;
U *p = this;
};
S a;
// CHECK: @a = {{.*}} { i32 10, i32 20 }
S b(5);
// CHECK: @b = {{.*}} { i32 5, i32 10 }
S c(3, 9);
// CHECK: @c = {{.*}} { i32 3, i32 9 }
S::T d;
// CHECK: @d = {{.*}} { {{.*}} @d }
U<S> e;
// CHECK: @e = {{.*}} { {{.*}} { i32 42, i32 84 }, {{.*}} @e }

View File

@ -0,0 +1,14 @@
// RUN: %clang_cc1 %s -std=c++0x -emit-llvm -o - | FileCheck %s
bool b();
struct S {
int n = b() ? S().n + 1 : 0;
};
S s;
// CHECK: define{{.*}} void @_ZN1SC2Ev(
// CHECK-NOT }
// CHECK: call {{.*}} @_Z1bv()
// CHECK-NOT }
// CHECK: call {{.*}} @_ZN1SC1Ev(

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -x objective-c++ -fblocks -triple x86_64-apple-darwin %s
// RUN: %clang_cc1 -x objective-c++ -fblocks -triple x86_64-apple-darwin %s -verify -emit-llvm -o %t
// rdar://8979379
@interface A
@ -28,3 +28,19 @@ void foo(id <NSObject>(^objectCreationBlock)(void)) {
return bar(objectCreationBlock);
}
// Test4
struct S {
S *(^a)() = ^{ // expected-warning {{C++0x}}
return this;
};
};
S s;
// Test5
struct X {
void f() {
^ {
struct Nested { Nested *ptr = this; }; // expected-warning {{C++0x}}
} ();
};
};

View File

@ -0,0 +1,22 @@
// Test this without pch.
// RUN: %clang_cc1 -x c++ -std=c++0x -DHEADER -DSOURCE -fsyntax-only -emit-llvm -o - %s
// Test with pch.
// RUN: %clang_cc1 -x c++ -std=c++0x -DHEADER -emit-pch -o %t %s
// RUN: %clang_cc1 -x c++ -std=c++0x -DHEADER -include-pch %t -fsyntax-only -emit-llvm -o - %s
#ifdef HEADER
int n;
struct S {
int *p = &m;
int &m = n;
S *that = this;
};
#endif
#ifdef SOURCE
S s;
#elif HEADER
#undef HEADER
#define SOURCE
#endif

View File

@ -0,0 +1,15 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
// Make sure we don't run off the end of the stream when parsing a deferred
// initializer.
int a; // expected-note {{previous}}
struct S {
int n = 4 + ; // expected-error {{expected expression}}
} a; // expected-error {{redefinition}}
// Make sure we use all of the tokens.
struct T {
int a = 1 // expected-error {{expected ';' at end of declaration list}}
int b = 2;
int c = b; // expected-error {{undeclared identifier}}
};

View File

@ -1,13 +1,13 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
class Base {
virtual ~Base();
virtual ~Base(); // expected-note {{implicitly declared private here}}
};
struct Foo : public Base {
const int kBlah = 3; // expected-error{{fields can only be initialized in constructors}}
struct Foo : public Base { // expected-error {{base class 'Base' has private destructor}}
const int kBlah = 3; // expected-warning {{accepted as a C++0x extension}}
Foo();
};
struct Bar : public Foo {
Bar() { }
Bar() { } // expected-note {{implicit default destructor for 'Foo' first required here}}
};
struct Baz {
Foo f;

View File

@ -34,7 +34,7 @@ public:
enum E1 { en1, en2 };
int i = 0; // expected-error {{fields can only be initialized in constructors}}
int i = 0; // expected-warning {{in-class initialization of non-static data member accepted as a C++0x extension}}
static int si = 0; // expected-error {{non-const static data member must be initialized out of line}}
static const NestedC ci = 0; // expected-error {{static data member of type 'const C::NestedC' must be initialized out of line}}
static const int nci = vs; // expected-error {{in-class initializer is not a constant expression}}

View File

@ -0,0 +1,63 @@
// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -std=c++0x -Wall %s
template<bool b> struct ExceptionIf { static int f(); };
template<> struct ExceptionIf<false> { typedef int f; };
// The exception specification of a defaulted default constructor depends on
// the contents of in-class member initializers. However, the in-class member
// initializers can depend on the exception specification of the constructor,
// since the class is considered complete within them. We reject any such cases.
namespace InClassInitializers {
// Noexcept::Noexcept() is implicitly declared as noexcept(false), because it
// directly invokes ThrowSomething(). However...
//
// If noexcept(Noexcept()) is false, then Noexcept() is a constant expression,
// so noexcept(Noexcept()) is true. But if noexcept(Noexcept()) is true, then
// Noexcept::Noexcept is not declared constexpr, therefore noexcept(Noexcept())
// is false.
bool ThrowSomething() noexcept(false);
struct ConstExpr {
bool b = noexcept(ConstExpr()) && ThrowSomething(); // expected-error {{exception specification is not available until end of class definition}}
};
// We can use it now.
bool w = noexcept(ConstExpr());
// Much more obviously broken: we can't parse the initializer without already
// knowing whether it produces a noexcept expression.
struct TemplateArg {
int n = ExceptionIf<noexcept(TemplateArg())>::f(); // expected-error {{exception specification is not available until end of class definition}}
};
bool x = noexcept(TemplateArg());
// And within a nested class.
struct Nested {
struct Inner {
int n = ExceptionIf<noexcept(Nested())>::f(); // expected-error {{exception specification is not available until end of class definition}}
} inner;
};
bool y = noexcept(Nested());
bool z = noexcept(Nested::Inner());
}
// FIXME:
// The same problem arises in delayed parsing of exception specifications,
// which clang does not yet support.
namespace ExceptionSpecification {
struct Nested { // expected-note {{not complete}}
struct T {
T() noexcept(!noexcept(Nested())); // expected-error {{incomplete type}}
} t;
};
}
// FIXME:
// The same problem arises in delayed parsing of default arguments,
// which clang does not yet support.
namespace DefaultArgument {
// FIXME: this diagnostic is completely wrong.
struct Default { // expected-note {{explicitly marked deleted here}}
struct T {
T(int = ExceptionIf<noexcept(Default())::f()); // expected-error {{call to deleted constructor}}
} t;
};
}

View File

@ -0,0 +1,50 @@
// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -std=c++0x -Wall %s
struct Bitfield {
int n : 3 = 7; // expected-error {{bitfield member cannot have an in-class initializer}}
};
int a;
class NoWarning {
int &n = a;
public:
int &GetN() { return n; }
};
bool b();
int k;
struct Recurse {
int &n = b() ? Recurse().n : k; // ok
};
struct UnknownBound {
int as[] = { 1, 2, 3 }; // expected-error {{array bound cannot be deduced from an in-class initializer}}
int bs[4] = { 4, 5, 6, 7 };
int cs[] = { 8, 9, 10 }; // expected-error {{array bound cannot be deduced from an in-class initializer}}
};
template<int n> struct T { static const int B; };
template<> struct T<2> { template<int C, int D> using B = int; };
const int C = 0, D = 0;
struct S {
int as[] = { decltype(x)::B<C, D>(0) }; // expected-error {{array bound cannot be deduced from an in-class initializer}}
T<sizeof(as) / sizeof(int)> x; // expected-error {{requires a type specifier}}
};
struct ThrowCtor { ThrowCtor(int) noexcept(false); };
struct NoThrowCtor { NoThrowCtor(int) noexcept(true); };
struct Throw { ThrowCtor tc = 42; };
struct NoThrow { NoThrowCtor tc = 42; };
static_assert(!noexcept(Throw()), "incorrect exception specification");
static_assert(noexcept(NoThrow()), "incorrect exception specification");
struct CheckExcSpec {
CheckExcSpec() noexcept(true) = default;
int n = 0;
};
struct CheckExcSpecFail {
CheckExcSpecFail() noexcept(true) = default; // expected-error {{exception specification of explicitly defaulted default constructor does not match the calculated one}}
ThrowCtor tc = 123;
};

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=gnu++0x %s
#define T(b) (b) ? 1 : -1
#define F(b) (b) ? -1 : 1
@ -38,8 +38,7 @@ typedef Derives DerivesArNB[];
struct DerivesEmpty : Empty {};
struct HasCons { HasCons(int); };
struct HasCopyAssign { HasCopyAssign operator =(const HasCopyAssign&); };
struct HasMoveAssign { HasMoveAssign operator =(const HasMoveAssign&&); }; // \
// expected-warning {{rvalue references}}
struct HasMoveAssign { HasMoveAssign operator =(const HasMoveAssign&&); };
struct HasDest { ~HasDest(); };
class HasPriv { int priv; };
class HasProt { protected: int prot; };
@ -1069,7 +1068,7 @@ struct HasCopy {
};
struct HasMove {
HasMove(HasMove&& cp); // expected-warning {{rvalue references}}
HasMove(HasMove&& cp);
};
struct HasTemplateCons {
@ -1253,6 +1252,9 @@ void has_nothrow_copy() {
{ int arr[F(__has_nothrow_copy(cvoid))]; }
}
template<bool b> struct assert_expr;
template<> struct assert_expr<true> {};
void has_nothrow_constructor() {
{ int arr[T(__has_nothrow_constructor(Int))]; }
{ int arr[T(__has_nothrow_constructor(IntAr))]; }
@ -1280,6 +1282,11 @@ void has_nothrow_constructor() {
{ int arr[F(__has_nothrow_constructor(void))]; }
{ int arr[F(__has_nothrow_constructor(cvoid))]; }
{ int arr[F(__has_nothrow_constructor(HasTemplateCons))]; }
// While parsing an in-class initializer, the constructor is not known to be
// non-throwing yet.
struct HasInClassInit { int n = (assert_expr<!__has_nothrow_constructor(HasInClassInit)>(), 0); };
{ int arr[T(__has_nothrow_constructor(HasInClassInit))]; }
}
void has_virtual_destructor() {