diff options
-rw-r--r-- | include/clang/AST/Expr.h | 1 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticKinds.def | 19 | ||||
-rw-r--r-- | lib/AST/Expr.cpp | 4 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 14 | ||||
-rw-r--r-- | lib/Sema/CXXFieldCollector.h | 76 | ||||
-rw-r--r-- | lib/Sema/Sema.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 28 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 110 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 170 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 24 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 18 | ||||
-rw-r--r-- | test/Sema/cxx-class.cpp | 70 | ||||
-rw-r--r-- | test/Sema/cxx-this.cpp | 6 |
13 files changed, 505 insertions, 37 deletions
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index e05899821d..b2071869ad 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -223,6 +223,7 @@ public: Func, Function, PrettyFunction, + CXXThis, ObjCSuper // super }; diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 9ea8d51249..9713f96148 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -589,6 +589,18 @@ DIAG(err_declarator_need_ident, ERROR, DIAG(err_bad_language, ERROR, "unknown linkage language") +// C++ class members +DIAG(err_storageclass_invalid_for_member, ERROR, + "storage class specified for a member declaration") +DIAG(err_not_bitfield_type, ERROR, + "cannot declare '%0' to be a bit-field type") +DIAG(err_static_not_bitfield, ERROR, + "static member '%0' cannot be a bit-field") +DIAG(err_not_integral_type_bitfield, ERROR, + "bit-field '%0' with non-integral type") +DIAG(err_member_initialization, ERROR, + "'%0' can only be initialized if it is a static const integral data member") + // Attributes DIAG(err_attribute_wrong_number_arguments, ERROR, "attribute requires %0 argument(s)") @@ -898,6 +910,13 @@ DIAG(ext_typecheck_comparison_of_distinct_pointers, WARNING, DIAG(err_typecheck_assign_const, ERROR, "read-only variable is not assignable") +DIAG(err_invalid_this_use, ERROR, + "invalid use of 'this' outside of a nonstatic member function") +DIAG(err_invalid_member_use_in_static_method, ERROR, + "invalid use of member '%0' in static member function") +DIAG(err_invalid_non_static_member_use, ERROR, + "invalid use of nonstatic data member '%0'") + // assignment related diagnostics (also for argument passing, returning, etc). DIAG(err_typecheck_convert_incompatible, ERROR, "incompatible type %2 '%1', expected '%0'") diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index cdbafde0c0..58dadb0801 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -420,7 +420,9 @@ Expr::isLvalueResult Expr::isLvalue() const { case ObjCPropertyRefExprClass: // FIXME: check if read-only property. return LV_Valid; case PreDefinedExprClass: - return LV_Valid; + return (cast<PreDefinedExpr>(this)->getIdentType() == PreDefinedExpr::CXXThis + ? LV_InvalidExpression + : LV_Valid); case CXXDefaultArgExprClass: return cast<CXXDefaultArgExpr>(this)->getExpr()->isLvalue(); default: diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index c84b5e7de6..87dcdfcb64 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -229,11 +229,9 @@ void Parser::ParseClassSpecifier(DeclSpec &DS) { // If there is a body, parse it and inform the actions module. if (Tok.is(tok::l_brace)) - // FIXME: Temporarily disable parsing for C++ classes until the Sema support - // is in place. - //if (getLang().CPlusPlus) - // ParseCXXMemberSpecification(StartLoc, TagType, TagDecl); - //else + if (getLang().CPlusPlus) + ParseCXXMemberSpecification(StartLoc, TagType, TagDecl); + else ParseStructUnionBody(StartLoc, TagType, TagDecl); else if (TK == Action::TK_Definition) { // FIXME: Complain that we have a base-specifier list but no @@ -411,7 +409,7 @@ Parser::DeclTy *Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) { return 0; } } - + Declarator DeclaratorInfo(DS, Declarator::MemberContext); if (Tok.isNot(tok::colon)) { @@ -492,6 +490,10 @@ Parser::DeclTy *Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) { if (Tok.is(tok::kw___attribute)) DeclaratorInfo.AddAttributes(ParseAttributes()); + // NOTE: If Sema is the Action module and declarator is an instance field, + // this call will *not* return the created decl; LastDeclInGroup will be + // returned instead. + // See Sema::ActOnCXXMemberDeclarator for details. LastDeclInGroup = Actions.ActOnCXXMemberDeclarator(CurScope, AS, DeclaratorInfo, BitfieldSize, Init, diff --git a/lib/Sema/CXXFieldCollector.h b/lib/Sema/CXXFieldCollector.h new file mode 100644 index 0000000000..6857fef9ab --- /dev/null +++ b/lib/Sema/CXXFieldCollector.h @@ -0,0 +1,76 @@ +//===- CXXFieldCollector.h - Utility class for C++ class semantic analysis ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides CXXFieldCollector that is used during parsing & semantic +// analysis of C++ classes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SEMA_CXXFIELDCOLLECTOR_H +#define LLVM_CLANG_SEMA_CXXFIELDCOLLECTOR_H + +#include "llvm/ADT/SmallVector.h" + +namespace clang { + class CXXFieldDecl; + +/// CXXFieldCollector - Used to keep track of CXXFieldDecls during parsing of +/// C++ classes. +class CXXFieldCollector { + /// Fields - Contains all CXXFieldDecls collected during parsing of a C++ + /// class. When a nested class is entered, its fields are appended to the + /// fields of its parent class, when it is exited its fields are removed. + llvm::SmallVector<CXXFieldDecl*, 32> Fields; + + /// FieldCount - Each entry represents the number of fields collected during + /// the parsing of a C++ class. When a nested class is entered, a new field + /// count is pushed, when it is exited, the field count is popped. + llvm::SmallVector<size_t, 4> FieldCount; + + // Example: + // + // class C { + // int x,y; + // class NC { + // int q; + // // At this point, Fields contains [x,y,q] decls and FieldCount contains + // // [2,1]. + // }; + // int z; + // // At this point, Fields contains [x,y,z] decls and FieldCount contains + // // [3]. + // }; + +public: + /// StartClass - Called by Sema::ActOnStartCXXClassDef. + void StartClass() { FieldCount.push_back(0); } + + /// Add - Called by Sema::ActOnCXXMemberDeclarator. + void Add(CXXFieldDecl *D) { + Fields.push_back(D); + ++FieldCount.back(); + } + + /// getNumField - The number of fields added to the currently parsed class. + size_t getCurNumFields() const { return FieldCount.back(); } + + /// getFields - Pointer to array of fields added to the currently parsed + /// class. + CXXFieldDecl **getCurFields() { return &*(Fields.end() - getCurNumFields()); } + + /// FinishClass - Called by Sema::ActOnFinishCXXClassDef. + void FinishClass() { + Fields.resize(Fields.size() - getCurNumFields()); + FieldCount.pop_back(); + } +}; + +} // end namespace clang + +#endif diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index c87e615f90..0e1a8ee56e 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -116,6 +116,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer) KnownFunctionIDs[id_vprintf] = &IT.get("vprintf"); TUScope = 0; + if (getLangOptions().CPlusPlus) + FieldCollector.reset(new CXXFieldCollector()); } /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast. diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 3e687b9659..9df94d4ae4 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -16,11 +16,13 @@ #define LLVM_CLANG_AST_SEMA_H #include "IdentifierResolver.h" +#include "CXXFieldCollector.h" #include "clang/Parse/Action.h" #include "clang/Parse/DeclSpec.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/OwningPtr.h" #include <vector> #include <string> @@ -109,6 +111,9 @@ public: ObjCCompatibleAliasDecl*> ObjCAliasTy; ObjCAliasTy ObjCAliasDecls; + /// FieldCollector - Collects CXXFieldDecls during parsing of C++ classes. + llvm::OwningPtr<CXXFieldCollector> FieldCollector; + IdentifierResolver IdResolver; // Enum values used by KnownFunctionIDs (see below). @@ -215,9 +220,7 @@ private: virtual DeclTy *FinalizeDeclaratorGroup(Scope *S, DeclTy *Group); virtual DeclTy *ActOnStartOfFunctionDef(Scope *S, Declarator &D); - // Until 'real' implementation is in place, override both - // 'ActOnStartOfFunctionDef' to satisfy the compiler. - virtual DeclTy *ActOnStartOfFunctionDef(Scope *S, DeclTy *D) { return D; } + virtual DeclTy *ActOnStartOfFunctionDef(Scope *S, DeclTy *D); virtual void ObjCActOnStartOfMethodDef(Scope *S, DeclTy *D); virtual DeclTy *ActOnFinishFunctionBody(DeclTy *Decl, StmtTy *Body); @@ -258,6 +261,8 @@ private: virtual void ActOnEnumBody(SourceLocation EnumLoc, DeclTy *EnumDecl, DeclTy **Elements, unsigned NumElements); private: + DeclContext *getDCParent(DeclContext *DC); + /// Set the current declaration context until it gets popped. void PushDeclContext(DeclContext *DC); void PopDeclContext(); @@ -545,6 +550,9 @@ public: SourceLocation LParenLoc, ExprTy *E, SourceLocation RParenLoc); + //// ActOnCXXThis - Parse 'this' pointer. + virtual ExprResult ActOnCXXThis(SourceLocation ThisLoc); + /// ActOnCXXBoolLiteral - Parse {true,false} literals. virtual ExprResult ActOnCXXBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind); @@ -585,6 +593,20 @@ public: bool Virtual, AccessSpecifier Access, DeclTy *basetype, SourceLocation BaseLoc); + virtual void ActOnStartCXXClassDef(Scope *S, DeclTy *TagDecl, + SourceLocation LBrace); + + virtual DeclTy *ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, + Declarator &D, ExprTy *BitfieldWidth, + ExprTy *Init, DeclTy *LastInGroup); + + virtual void ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, + DeclTy *TagDecl, + SourceLocation LBrac, + SourceLocation RBrac); + + virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl,SourceLocation RBrace); + // Objective-C declarations. virtual DeclTy *ActOnStartClassInterface( diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 6b528b6bed..3b0e2e6f38 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -44,19 +44,38 @@ Sema::DeclTy *Sema::isTypeName(const IdentifierInfo &II, Scope *S) { return 0; } +DeclContext *Sema::getDCParent(DeclContext *DC) { + // If CurContext is a ObjC method, getParent() will return NULL. + if (isa<ObjCMethodDecl>(DC)) + return Context.getTranslationUnitDecl(); + + // A C++ inline method is parsed *after* the topmost class it was declared in + // is fully parsed (it's "complete"). + // The parsing of a C++ inline method happens at the declaration context of + // the topmost (non-nested) class it is declared in. + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC)) { + assert(isa<CXXRecordDecl>(MD->getParent()) && "C++ method not in Record."); + DC = MD->getParent(); + while (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC->getParent())) + DC = RD; + + // Return the declaration context of the topmost class the inline method is + // declared in. + return DC; + } + + return DC->getParent(); +} + void Sema::PushDeclContext(DeclContext *DC) { - assert( ( (isa<ObjCMethodDecl>(DC) && isa<TranslationUnitDecl>(CurContext)) - || DC->getParent() == CurContext ) && - "The next DeclContext should be directly contained in the current one."); + assert(getDCParent(DC) == CurContext && + "The next DeclContext should be directly contained in the current one."); CurContext = DC; } void Sema::PopDeclContext() { assert(CurContext && "DeclContext imbalance!"); - // If CurContext is a ObjC method, getParent() will return NULL. - CurContext = isa<ObjCMethodDecl>(CurContext) - ? Context.getTranslationUnitDecl() - : CurContext->getParent(); + CurContext = getDCParent(CurContext); } /// Add this decl to the scope shadowed decl chains. @@ -646,10 +665,19 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { } bool isInline = D.getDeclSpec().isInlineSpecified(); - FunctionDecl *NewFD = FunctionDecl::Create(Context, CurContext, - D.getIdentifierLoc(), - II, R, SC, isInline, - LastDeclarator); + FunctionDecl *NewFD; + if (D.getContext() == Declarator::MemberContext) { + // This is a C++ method declaration. + NewFD = CXXMethodDecl::Create(Context, cast<CXXRecordDecl>(CurContext), + D.getIdentifierLoc(), II, R, + (SC == FunctionDecl::Static), isInline, + LastDeclarator); + } else { + NewFD = FunctionDecl::Create(Context, CurContext, + D.getIdentifierLoc(), + II, R, SC, isInline, + LastDeclarator); + } // Handle attributes. ProcessDeclAttributes(NewFD, D); @@ -728,19 +756,27 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { case DeclSpec::SCS_register: SC = VarDecl::Register; break; case DeclSpec::SCS_private_extern: SC = VarDecl::PrivateExtern; break; } - if (S->getFnParent() == 0) { - // C99 6.9p2: The storage-class specifiers auto and register shall not - // appear in the declaration specifiers in an external declaration. - if (SC == VarDecl::Auto || SC == VarDecl::Register) { - Diag(D.getIdentifierLoc(), diag::err_typecheck_sclass_fscope, - R.getAsString()); - InvalidDecl = true; - } - NewVD = VarDecl::Create(Context, CurContext, D.getIdentifierLoc(), - II, R, SC, LastDeclarator); + if (D.getContext() == Declarator::MemberContext) { + assert(SC == VarDecl::Static && "Invalid storage class for member!"); + // This is a static data member for a C++ class. + NewVD = CXXClassVarDecl::Create(Context, cast<CXXRecordDecl>(CurContext), + D.getIdentifierLoc(), II, + R, LastDeclarator); } else { - NewVD = VarDecl::Create(Context, CurContext, D.getIdentifierLoc(), - II, R, SC, LastDeclarator); + if (S->getFnParent() == 0) { + // C99 6.9p2: The storage-class specifiers auto and register shall not + // appear in the declaration specifiers in an external declaration. + if (SC == VarDecl::Auto || SC == VarDecl::Register) { + Diag(D.getIdentifierLoc(), diag::err_typecheck_sclass_fscope, + R.getAsString()); + InvalidDecl = true; + } + NewVD = VarDecl::Create(Context, CurContext, D.getIdentifierLoc(), + II, R, SC, LastDeclarator); + } else { + NewVD = VarDecl::Create(Context, CurContext, D.getIdentifierLoc(), + II, R, SC, LastDeclarator); + } } // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(NewVD, D); @@ -1506,7 +1542,13 @@ Sema::DeclTy *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D) { } } } - Decl *decl = static_cast<Decl*>(ActOnDeclarator(GlobalScope, D, 0)); + + return ActOnStartOfFunctionDef(FnBodyScope, + ActOnDeclarator(GlobalScope, D, 0)); +} + +Sema::DeclTy *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, DeclTy *D) { + Decl *decl = static_cast<Decl*>(D); FunctionDecl *FD = cast<FunctionDecl>(decl); PushDeclContext(FD); @@ -1707,8 +1749,12 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK, // FIXME: Tag decls should be chained to any simultaneous vardecls, e.g.: // struct X { int A; } D; D should chain to X. - New = RecordDecl::Create(Context, Kind, CurContext, Loc, Name, 0); - } + if (getLangOptions().CPlusPlus) + // FIXME: Look for a way to use RecordDecl for simple structs. + New = CXXRecordDecl::Create(Context, Kind, CurContext, Loc, Name, 0); + else + New = RecordDecl::Create(Context, Kind, CurContext, Loc, Name, 0); + } // If this has an identifier, add it to the scope stack. if (Name) { @@ -1843,7 +1889,17 @@ Sema::DeclTy *Sema::ActOnField(Scope *S, } } // FIXME: Chain fielddecls together. - FieldDecl *NewFD = FieldDecl::Create(Context, Loc, II, T, BitWidth); + FieldDecl *NewFD; + + if (getLangOptions().CPlusPlus) { + // FIXME: Replace CXXFieldDecls with FieldDecls for simple structs. + NewFD = CXXFieldDecl::Create(Context, cast<CXXRecordDecl>(CurContext), + Loc, II, T, BitWidth); + if (II) + PushOnScopeChains(NewFD, S); + } + else + NewFD = FieldDecl::Create(Context, Loc, II, T, BitWidth); ProcessDeclAttributes(NewFD, D); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 45affcab16..8050557075 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -14,6 +14,7 @@ #include "Sema.h" #include "clang/Basic/LangOptions.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/StmtVisitor.h" @@ -307,6 +308,175 @@ void Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange, // FIXME: Attach base class to the record. } +//===----------------------------------------------------------------------===// +// C++ class member Handling +//===----------------------------------------------------------------------===// + +/// ActOnStartCXXClassDef - This is called at the start of a class/struct/union +/// definition, when on C++. +void Sema::ActOnStartCXXClassDef(Scope *S, DeclTy *D, SourceLocation LBrace) { + Decl *Dcl = static_cast<Decl *>(D); + PushDeclContext(cast<CXXRecordDecl>(Dcl)); + FieldCollector->StartClass(); +} + +/// 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. 'LastInGroup' is non-null for cases where one declspec has multiple +/// declarators on it. +/// +/// NOTE: Because of CXXFieldDecl's inability to be chained like ScopedDecls, if +/// an instance field is declared, a new CXXFieldDecl is created but the method +/// does *not* return it; it returns LastInGroup instead. The other C++ members +/// (which are all ScopedDecls) are returned after appending them to +/// LastInGroup. +Sema::DeclTy * +Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, + ExprTy *BW, ExprTy *InitExpr, + DeclTy *LastInGroup) { + const DeclSpec &DS = D.getDeclSpec(); + IdentifierInfo *II = D.getIdentifier(); + Expr *BitWidth = static_cast<Expr*>(BW); + Expr *Init = static_cast<Expr*>(InitExpr); + SourceLocation Loc = D.getIdentifierLoc(); + + // C++ 9.2p6: A member shall not be declared to have automatic storage + // duration (auto, register) or with the extern storage-class-specifier. + switch (DS.getStorageClassSpec()) { + case DeclSpec::SCS_unspecified: + case DeclSpec::SCS_typedef: + case DeclSpec::SCS_static: + // FALL THROUGH. + break; + default: + if (DS.getStorageClassSpecLoc().isValid()) + Diag(DS.getStorageClassSpecLoc(), + diag::err_storageclass_invalid_for_member); + else + Diag(DS.getThreadSpecLoc(), diag::err_storageclass_invalid_for_member); + D.getMutableDeclSpec().ClearStorageClassSpecs(); + } + + QualType T = GetTypeForDeclarator(D, S); + + // T->isFunctionType() is used instead of D.isFunctionDeclarator() to cover + // this case: + // + // typedef int f(); + // f a; + bool isInstField = (DS.getStorageClassSpec() == DeclSpec::SCS_unspecified && + !T->isFunctionType()); + + Decl *Member; + bool InvalidDecl = false; + + if (isInstField) + Member = static_cast<Decl*>(ActOnField(S, Loc, D, BitWidth)); + else + Member = static_cast<Decl*>(ActOnDeclarator(S, D, LastInGroup)); + + if (!Member) return LastInGroup; + + assert(II || isInstField && "No identifier for non-field ?"); + + // set/getAccess is not part of Decl's interface to avoid bloating it with C++ + // specific methods. Use a wrapper class that can be used with all C++ class + // member decls. + CXXClassMemberWrapper(Member).setAccess(AS); + + if (BitWidth) { + // C++ 9.6p2: Only when declaring an unnamed bit-field may the + // constant-expression be a value equal to zero. + // FIXME: Check this. + + if (D.isFunctionDeclarator()) { + // FIXME: Emit diagnostic about only constructors taking base initializers + // or something similar, when constructor support is in place. + Diag(Loc, diag::err_not_bitfield_type, + II->getName(), BitWidth->getSourceRange()); + InvalidDecl = true; + + } else if (isInstField || isa<FunctionDecl>(Member)) { + // An instance field or a function typedef ("typedef int f(); f a;"). + // C++ 9.6p3: A bit-field shall have integral or enumeration type. + if (!T->isIntegralType()) { + Diag(Loc, diag::err_not_integral_type_bitfield, + II->getName(), BitWidth->getSourceRange()); + InvalidDecl = true; + } + + } else if (isa<TypedefDecl>(Member)) { + // "cannot declare 'A' to be a bit-field type" + Diag(Loc, diag::err_not_bitfield_type, II->getName(), + BitWidth->getSourceRange()); + InvalidDecl = true; + + } else { + assert(isa<CXXClassVarDecl>(Member) && + "Didn't we cover all member kinds?"); + // C++ 9.6p3: A bit-field shall not be a static member. + // "static member 'A' cannot be a bit-field" + Diag(Loc, diag::err_static_not_bitfield, II->getName(), + BitWidth->getSourceRange()); + InvalidDecl = true; + } + } + + if (Init) { + // C++ 9.2p4: A member-declarator can contain a constant-initializer only + // if it declares a static member of const integral or const enumeration + // type. + if (CXXClassVarDecl *CVD = + dyn_cast<CXXClassVarDecl>(Member)) { // ...static member of... + CVD->setInit(Init); + QualType MemberTy = CVD->getType().getCanonicalType(); + // ...const integral or const enumeration type. + if (MemberTy.isConstQualified() && MemberTy->isIntegralType()) { + if (CheckForConstantInitializer(Init, MemberTy)) // constant-initializer + InvalidDecl = true; + + } else { + // not const integral. + Diag(Loc, diag::err_member_initialization, + II->getName(), Init->getSourceRange()); + InvalidDecl = true; + } + + } else { + // not static member. + Diag(Loc, diag::err_member_initialization, + II->getName(), Init->getSourceRange()); + InvalidDecl = true; + } + } + + if (InvalidDecl) + Member->setInvalidDecl(); + + if (isInstField) { + FieldCollector->Add(cast<CXXFieldDecl>(Member)); + return LastInGroup; + } + return Member; +} + +void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, + DeclTy *TagDecl, + SourceLocation LBrac, + SourceLocation RBrac) { + ActOnFields(S, RLoc, TagDecl, + (DeclTy**)FieldCollector->getCurFields(), + FieldCollector->getCurNumFields(), LBrac, RBrac); +} + +void Sema::ActOnFinishCXXClassDef(DeclTy *D,SourceLocation RBrace) { + Decl *Dcl = static_cast<Decl *>(D); + assert(isa<CXXRecordDecl>(Dcl) && + "Invalid parameter, expected CXXRecordDecl"); + FieldCollector->FinishClass(); + PopDeclContext(); +} //===----------------------------------------------------------------------===// // Namespace Handling diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 32d7a6086d..71f7717e5d 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -14,6 +14,7 @@ #include "Sema.h" #include "SemaUtil.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" @@ -130,6 +131,29 @@ Sema::ExprResult Sema::ActOnIdentifierExpr(Scope *S, SourceLocation Loc, return true; return new DeclRefExpr(VD, VD->getType(), Loc); } + + if (CXXFieldDecl *FD = dyn_cast<CXXFieldDecl>(D)) { + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CurContext)) { + if (MD->isStatic()) + // "invalid use of member 'x' in static member function" + return Diag(Loc, diag::err_invalid_member_use_in_static_method, + FD->getName()); + if (cast<CXXRecordDecl>(MD->getParent()) != FD->getParent()) + // "invalid use of nonstatic data member 'x'" + return Diag(Loc, diag::err_invalid_non_static_member_use, + FD->getName()); + + if (FD->isInvalidDecl()) + return true; + + // FIXME: Use DeclRefExpr or a new Expr for a direct CXXField reference. + ExprResult ThisExpr = ActOnCXXThis(SourceLocation()); + return new MemberExpr(static_cast<Expr*>(ThisExpr.Val), + true, FD, Loc, FD->getType()); + } + + return Diag(Loc, diag::err_invalid_non_static_member_use, FD->getName()); + } if (isa<TypedefDecl>(D)) return Diag(Loc, diag::err_unexpected_typedef, II.getName()); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index e49a43c472..be7cf069ec 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -49,3 +49,21 @@ Action::ExprResult Sema::ActOnCXXThrow(SourceLocation OpLoc, ExprTy *E) { return new CXXThrowExpr((Expr*)E, Context.VoidTy, OpLoc); } + +Action::ExprResult Sema::ActOnCXXThis(SourceLocation ThisLoc) { + /// 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. + + if (!isa<FunctionDecl>(CurContext)) { + Diag(ThisLoc, diag::err_invalid_this_use); + return ExprResult(true); + } + + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CurContext)) + if (MD->isInstance()) + return new PreDefinedExpr(ThisLoc, MD->getThisType(Context), + PreDefinedExpr::CXXThis); + + return Diag(ThisLoc, diag::err_invalid_this_use); +} diff --git a/test/Sema/cxx-class.cpp b/test/Sema/cxx-class.cpp new file mode 100644 index 0000000000..b7702dd24a --- /dev/null +++ b/test/Sema/cxx-class.cpp @@ -0,0 +1,70 @@ +// RUN: clang -fsyntax-only -verify %s +class C { +public: + auto int errx; // expected-error {{error: storage class specified for a member declaration}} + register int erry; // expected-error {{error: storage class specified for a member declaration}} + extern int errz; // expected-error {{error: storage class specified for a member declaration}} + + static void sm() { + sx = 0; + this->x = 0; // expected-error {{error: invalid use of 'this' outside of a nonstatic member function}} + x = 0; // expected-error {{error: invalid use of member 'x' in static member function}} + } + + class NestedC { + void m() { + sx = 0; + x = 0; // expected-error {{error: invalid use of nonstatic data member 'x'}} + } + }; + + int b : 1, w : 2; + int : 1, : 2; + typedef int E : 1; // expected-error {{error: cannot declare 'E' to be a bit-field type}} + static int sb : 1; // expected-error {{error: static member 'sb' cannot be a bit-field}} + static int vs; + + typedef int func(); + func tm; + func btm : 1; // expected-error {{error: bit-field 'btm' with non-integral type}} + NestedC bc : 1; // expected-error {{error: bit-field 'bc' with non-integral type}} + + enum E { en1, en2 }; + + int i = 0; // expected-error {{error: 'i' can only be initialized if it is a static const integral data member}} + static int si = 0; // expected-error {{error: 'si' can only be initialized if it is a static const integral data member}} + static const NestedC ci = 0; // expected-error {{error: 'ci' can only be initialized if it is a static const integral data member}} + static const int nci = vs; // expected-error {{error: initializer element is not constant}} + static const int vi = 0; + static const E evi = 0; + + void m() { + sx = 0; + this->x = 0; + y = 0; + this = 0; // expected-error {{error: expression is not assignable}} + } + + int f1(int p) { + A z = 6; + return p + x + this->y + z; + } + + typedef int A; + +private: + int x,y; + static int sx; +}; + +class C2 { + void f() { + static int lx; + class LC1 { + int m() { return lx; } + }; + class LC2 { + int m() { return lx; } + }; + } +}; diff --git a/test/Sema/cxx-this.cpp b/test/Sema/cxx-this.cpp new file mode 100644 index 0000000000..b8bf4b7d3a --- /dev/null +++ b/test/Sema/cxx-this.cpp @@ -0,0 +1,6 @@ +// RUN: clang -fsyntax-only -verify %s +int x = this; // expected-error {{error: invalid use of 'this' outside of a nonstatic member function}} + +void f() { + int x = this; // expected-error {{error: invalid use of 'this' outside of a nonstatic member function}} +} |