From: Sebastian Redl Date: Fri, 21 Nov 2008 19:14:01 +0000 (+0000) Subject: Implementation of new and delete parsing and sema. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4c5d320a7581f4b80b151630c91cea5727fa9923;p=clang Implementation of new and delete parsing and sema. This version uses VLAs to represent arrays. I'll try an alternative way next, but I want this safe first. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59835 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Driver/PrintParserCallbacks.cpp b/Driver/PrintParserCallbacks.cpp index 2ee113fcda..02203d356d 100644 --- a/Driver/PrintParserCallbacks.cpp +++ b/Driver/PrintParserCallbacks.cpp @@ -168,7 +168,7 @@ namespace { // Type Parsing Callbacks. //===--------------------------------------------------------------------===// - virtual TypeResult ActOnTypeName(Scope *S, Declarator &D) { + virtual TypeResult ActOnTypeName(Scope *S, Declarator &D, bool CXXNewMode) { llvm::cout << __FUNCTION__ << "\n"; return 0; } diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index c855089d8f..201a9331db 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -19,6 +19,8 @@ namespace clang { + class CXXConstructorDecl; + //===--------------------------------------------------------------------===// // C++ Expressions. //===--------------------------------------------------------------------===// @@ -433,6 +435,184 @@ public: // CreateImpl(llvm::Deserializer& D, ASTContext& C); }; +/// CXXNewExpr - A new expression for memory allocation and constructor calls, +/// e.g: "new CXXNewExpr(foo)". +class CXXNewExpr : public Expr { + // Was the usage ::new, i.e. is the global new to be used? + bool GlobalNew : 1; + // Was the form (type-id) used? Otherwise, it was new-type-id. + bool ParenTypeId : 1; + // Is there an initializer? If not, built-ins are uninitialized, else they're + // value-initialized. + bool Initializer : 1; + // The number of placement new arguments. + unsigned NumPlacementArgs : 14; + // The number of constructor arguments. This may be 1 even for non-class + // types; use the pseudo copy constructor. + unsigned NumConstructorArgs : 15; + // Contains any number of optional placement arguments, and any number of + // optional constructor arguments, in that order. + Stmt **SubExprs; + // Points to the allocation function used. + FunctionDecl *OperatorNew; + // Points to the deallocation function used in case of error. May be null. + FunctionDecl *OperatorDelete; + // Points to the constructor used. Cannot be null if AllocType is a record; + // it would still point at the default constructor (even an implicit one). + // Must be null for all other types. + CXXConstructorDecl *Constructor; + // The type to be allocated. Is either *ty or a VLA of that type. + QualType AllocType; + + SourceLocation StartLoc; + SourceLocation EndLoc; + + // Deserialization constructor + CXXNewExpr(QualType ty, QualType alloc, bool globalNew, bool parenTypeId, + bool initializer, unsigned numPlacementArgs, + unsigned numConstructorArgs, Stmt **subExprs, + FunctionDecl *operatorNew, FunctionDecl *operatorDelete, + CXXConstructorDecl *constructor, SourceLocation startLoc, + SourceLocation endLoc) + : Expr(CXXNewExprClass, ty), GlobalNew(globalNew), ParenTypeId(parenTypeId), + Initializer(initializer), NumPlacementArgs(numPlacementArgs), + NumConstructorArgs(numConstructorArgs), SubExprs(subExprs), + OperatorNew(operatorNew), OperatorDelete(operatorDelete), + Constructor(constructor), AllocType(alloc), + StartLoc(startLoc), EndLoc(endLoc) + { } +public: + CXXNewExpr(bool globalNew, FunctionDecl *operatorNew, Expr **placementArgs, + unsigned numPlaceArgs, bool ParenTypeId, QualType alloc, + CXXConstructorDecl *constructor, bool initializer, + Expr **constructorArgs, unsigned numConsArgs, + FunctionDecl *operatorDelete, QualType ty, + SourceLocation startLoc, SourceLocation endLoc); + ~CXXNewExpr() { + delete[] SubExprs; + } + + QualType getAllocatedType() const { return AllocType; } + + FunctionDecl *getOperatorNew() const { return OperatorNew; } + FunctionDecl *getOperatorDelete() const { return OperatorDelete; } + CXXConstructorDecl *getConstructor() const { return Constructor; } + + unsigned getNumPlacementArgs() const { return NumPlacementArgs; } + Expr *getPlacementArg(unsigned i) { + assert(i < NumPlacementArgs && "Index out of range"); + return cast(SubExprs[i]); + } + const Expr *getPlacementArg(unsigned i) const { + assert(i < NumPlacementArgs && "Index out of range"); + return cast(SubExprs[i]); + } + + bool isGlobalNew() const { return GlobalNew; } + bool isParenTypeId() const { return ParenTypeId; } + bool hasInitializer() const { return Initializer; } + + unsigned getNumConstructorArgs() const { return NumConstructorArgs; } + Expr *getConstructorArg(unsigned i) { + assert(i < NumConstructorArgs && "Index out of range"); + return cast(SubExprs[NumPlacementArgs + i]); + } + const Expr *getConstructorArg(unsigned i) const { + assert(i < NumConstructorArgs && "Index out of range"); + return cast(SubExprs[NumPlacementArgs + i]); + } + + typedef ExprIterator arg_iterator; + typedef ConstExprIterator const_arg_iterator; + + arg_iterator placement_arg_begin() { + return SubExprs; + } + arg_iterator placement_arg_end() { + return SubExprs + getNumPlacementArgs(); + } + const_arg_iterator placement_arg_begin() const { + return SubExprs; + } + const_arg_iterator placement_arg_end() const { + return SubExprs + getNumPlacementArgs(); + } + + arg_iterator constructor_arg_begin() { + return SubExprs + getNumPlacementArgs(); + } + arg_iterator constructor_arg_end() { + return SubExprs + getNumPlacementArgs() + getNumConstructorArgs(); + } + const_arg_iterator constructor_arg_begin() const { + return SubExprs + getNumPlacementArgs(); + } + const_arg_iterator constructor_arg_end() const { + return SubExprs + getNumPlacementArgs() + getNumConstructorArgs(); + } + + virtual SourceRange getSourceRange() const { + return SourceRange(StartLoc, EndLoc); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXNewExprClass; + } + static bool classof(const CXXNewExpr *) { return true; } + + // Iterators + virtual child_iterator child_begin(); + virtual child_iterator child_end(); + + virtual void EmitImpl(llvm::Serializer& S) const; + static CXXNewExpr* CreateImpl(llvm::Deserializer& D, ASTContext& C); +}; + +/// CXXDeleteExpr - A delete expression for memory deallocation and destructor +/// calls, e.g. "delete[] pArray". +class CXXDeleteExpr : public Expr { + // Is this a forced global delete, i.e. "::delete"? + bool GlobalDelete : 1; + // Is this the array form of delete, i.e. "delete[]"? + bool ArrayForm : 1; + // Points to the operator delete overload that is used. Could be a member. + FunctionDecl *OperatorDelete; + // The pointer expression to be deleted. + Stmt *Argument; + // Location of the expression. + SourceLocation Loc; +public: + CXXDeleteExpr(QualType ty, bool globalDelete, bool arrayForm, + FunctionDecl *operatorDelete, Expr *arg, SourceLocation loc) + : Expr(CXXDeleteExprClass, ty), GlobalDelete(globalDelete), + ArrayForm(arrayForm), OperatorDelete(operatorDelete), Argument(arg), + Loc(loc) { } + + bool isGlobalDelete() const { return GlobalDelete; } + bool isArrayForm() const { return ArrayForm; } + + FunctionDecl *getOperatorDelete() const { return OperatorDelete; } + + Expr *getArgument() { return cast(Argument); } + const Expr *getArgument() const { return cast(Argument); } + + virtual SourceRange getSourceRange() const { + return SourceRange(Loc, Argument->getLocEnd()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXDeleteExprClass; + } + static bool classof(const CXXDeleteExpr *) { return true; } + + // Iterators + virtual child_iterator child_begin(); + virtual child_iterator child_end(); + + virtual void EmitImpl(llvm::Serializer& S) const; + static CXXDeleteExpr * CreateImpl(llvm::Deserializer& D, ASTContext& C); +}; + } // end namespace clang #endif diff --git a/include/clang/AST/StmtNodes.def b/include/clang/AST/StmtNodes.def index beb64c51f3..6d3c172c96 100644 --- a/include/clang/AST/StmtNodes.def +++ b/include/clang/AST/StmtNodes.def @@ -104,6 +104,8 @@ STMT(CXXThrowExpr , Expr) STMT(CXXDefaultArgExpr , Expr) STMT(CXXZeroInitValueExpr , Expr) STMT(CXXConditionDeclExpr , DeclRefExpr) +STMT(CXXNewExpr , Expr) +STMT(CXXDeleteExpr , Expr) // Obj-C Expressions. STMT(ObjCStringLiteral , Expr) diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 6a85df0e18..4525355ba3 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -1189,6 +1189,20 @@ DIAG(err_static_downcast_via_virtual, ERROR, // Other C++ expressions DIAG(err_need_header_before_typeid, ERROR, "you need to include before using the 'typeid' operator") +DIAG(err_new_function, ERROR, + "cannot allocate function type '%0' with new") +DIAG(err_new_incomplete, ERROR, + "cannot allocate incomplete type '%0' with new") +DIAG(err_new_reference, ERROR, + "cannot allocate reference type '%0' with new") +DIAG(err_new_array_nonconst, ERROR, + "only the first dimension of an allocated array may be non-const") +DIAG(err_new_uninitialized_const, ERROR, + "must provide an initializer if the allocated object is 'const'") +DIAG(err_delete_operand, ERROR, + "cannot delete expression of type '%0'") +DIAG(warn_delete_incomplete, WARNING, + "deleting pointer to incomplete type '%0' may cause undefined behaviour") DIAG(err_invalid_use_of_function_type, ERROR, "a function type is not allowed here") diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index d00fbd3361..9a7b41b9e5 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -256,8 +256,12 @@ public: //===--------------------------------------------------------------------===// // Type Parsing Callbacks. //===--------------------------------------------------------------------===// - - virtual TypeResult ActOnTypeName(Scope *S, Declarator &D) { + + /// ActOnTypeName - A type-name (type-id in C++) was parsed. + /// CXXNewMode is a flag passed by the parser of C++ new-expressions. It + /// specifies that the outermost array (if any) must be treated as a VLA. + virtual TypeResult ActOnTypeName(Scope *S, Declarator &D, + bool CXXNewMode = false) { return 0; } @@ -746,6 +750,36 @@ public: return 0; } + /// ActOnCXXNew - Parsed a C++ 'new' expression. UseGlobal is true if the + /// new was qualified (::new). In a full new like + /// @code new (p1, p2) type(c1, c2) @endcode + /// the p1 and p2 expressions will be in PlacementArgs and the c1 and c2 + /// expressions in ConstructorArgs. If the type is a dynamic array, Ty will + /// be a variable-length array type, with the outermost dimension to be + /// allocated dynamically. + /// Example: + /// @code new int*[rand()][3] @endcode + /// Here, Ty will be a VLA with size "rand()" and element type "int*[3]". + virtual ExprResult ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, + SourceLocation PlacementLParen, + ExprTy **PlacementArgs, unsigned NumPlaceArgs, + SourceLocation PlacementRParen, + bool ParenTypeId, SourceLocation TyStart, + TypeTy *Ty, SourceLocation TyEnd, + SourceLocation ConstructorLParen, + ExprTy **ConstructorArgs, unsigned NumConsArgs, + SourceLocation ConstructorRParen) { + return 0; + } + + /// ActOnCXXDelete - Parsed a C++ 'delete' expression. UseGlobal is true if + /// the delete was qualified (::delete). ArrayForm is true if the array form + /// was used (delete[]). + virtual ExprResult ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, + bool ArrayForm, ExprTy *Operand) { + return 0; + } + //===---------------------------- C++ Classes ---------------------------===// /// ActOnBaseSpecifier - Parsed a base specifier virtual BaseResult ActOnBaseSpecifier(DeclTy *classdecl, diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 0c061219f8..76edfc1414 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -508,6 +508,14 @@ private: bool ParseCXXTypeSpecifierSeq(DeclSpec &DS); + //===--------------------------------------------------------------------===// + // C++ 5.3.4 and 5.3.5: C++ new and delete + ExprResult ParseCXXNewExpression(); + TypeTy *ParseNewTypeId(); + bool ParseExpressionListOrTypeId(ExprListTy &Exprs, TypeTy *&Ty); + void ParseDirectNewDeclarator(Declarator &D); + ExprResult ParseCXXDeleteExpression(); + //===--------------------------------------------------------------------===// // C++ if/switch/while/for condition expression. ExprResult ParseCXXCondition(); @@ -730,7 +738,7 @@ private: TPResult TryParseBracketDeclarator(); - TypeTy *ParseTypeName(); + TypeTy *ParseTypeName(bool CXXNewMode = false); AttributeList *ParseAttributes(); void ParseTypeofSpecifier(DeclSpec &DS); @@ -756,7 +764,10 @@ private: /// ParseDeclarator - Parse and verify a newly-initialized declarator. void ParseDeclarator(Declarator &D); - void ParseDeclaratorInternal(Declarator &D, bool PtrOperator = false); + /// A function that parses a variant of direct-declarator. + typedef void (Parser::*DirectDeclParseFunction)(Declarator&); + void ParseDeclaratorInternal(Declarator &D, + DirectDeclParseFunction DirectDeclParser); void ParseTypeQualifierListOpt(DeclSpec &DS); void ParseDirectDeclarator(Declarator &D); void ParseParenDeclarator(Declarator &D); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 2e13352a5a..e6a7b4114a 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -329,7 +329,13 @@ bool Expr::hasLocalSideEffect() const { case CXXDefaultArgExprClass: return cast(this)->getExpr()->hasLocalSideEffect(); - } + + case CXXNewExprClass: + // FIXME: In theory, there might be new expressions that don't have side + // effects (e.g. a placement new with an uninitialized POD). + case CXXDeleteExprClass: + return true; + } } /// DeclCanBeLvalue - Determine whether the given declaration can be @@ -481,8 +487,6 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const { case CXXTypeidExprClass: // C++ 5.2.8p1: The result of a typeid expression is an lvalue of ... return LV_Valid; - case CXXThisExprClass: - return LV_InvalidExpression; default: break; } diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 1155a4b9c6..c0f2985f1e 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -77,6 +77,39 @@ Stmt::child_iterator CXXConditionDeclExpr::child_end() { return child_iterator(); } +// CXXNewExpr +CXXNewExpr::CXXNewExpr(bool globalNew, FunctionDecl *operatorNew, + Expr **placementArgs, unsigned numPlaceArgs, + bool parenTypeId, QualType alloc, + CXXConstructorDecl *constructor, bool initializer, + Expr **constructorArgs, unsigned numConsArgs, + FunctionDecl *operatorDelete, QualType ty, + SourceLocation startLoc, SourceLocation endLoc) + : Expr(CXXNewExprClass, ty), GlobalNew(globalNew), ParenTypeId(parenTypeId), + Initializer(initializer), NumPlacementArgs(numPlaceArgs), + NumConstructorArgs(numConsArgs), OperatorNew(operatorNew), + OperatorDelete(operatorDelete), Constructor(constructor), AllocType(alloc), + StartLoc(startLoc), EndLoc(endLoc) +{ + unsigned TotalSize = NumPlacementArgs + NumConstructorArgs; + SubExprs = new Stmt*[TotalSize]; + unsigned i = 0; + for(unsigned j = 0; j < NumPlacementArgs; ++j) + SubExprs[i++] = placementArgs[j]; + for(unsigned j = 0; j < NumConstructorArgs; ++j) + SubExprs[i++] = constructorArgs[j]; + assert(i == TotalSize); +} + +Stmt::child_iterator CXXNewExpr::child_begin() { return &SubExprs[0]; } +Stmt::child_iterator CXXNewExpr::child_end() { + return &SubExprs[0] + getNumPlacementArgs() + getNumConstructorArgs(); +} + +// CXXDeleteExpr +Stmt::child_iterator CXXDeleteExpr::child_begin() { return &Argument; } +Stmt::child_iterator CXXDeleteExpr::child_end() { return &Argument+1; } + OverloadedOperatorKind CXXOperatorCallExpr::getOperator() const { // All simple function calls (e.g. func()) are implicitly cast to pointer to // function. As a result, we try and obtain the DeclRefExpr from the diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index cf6d4c0b90..cb5c44f926 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -925,6 +925,49 @@ StmtPrinter::VisitCXXConditionDeclExpr(CXXConditionDeclExpr *E) { PrintRawDecl(E->getVarDecl()); } +void StmtPrinter::VisitCXXNewExpr(CXXNewExpr *E) { + if (E->isGlobalNew()) + OS << "::"; + OS << "new "; + unsigned NumPlace = E->getNumPlacementArgs(); + if (NumPlace > 0) { + OS << "("; + PrintExpr(E->getPlacementArg(0)); + for (unsigned i = 1; i < NumPlace; ++i) { + OS << ", "; + PrintExpr(E->getPlacementArg(i)); + } + OS << ") "; + } + if (E->isParenTypeId()) + OS << "("; + OS << E->getAllocatedType().getAsString(); + if (E->isParenTypeId()) + OS << ")"; + + if (E->hasInitializer()) { + OS << "("; + unsigned NumCons = E->getNumConstructorArgs(); + if (NumCons > 0) { + PrintExpr(E->getConstructorArg(0)); + for (unsigned i = 1; i < NumCons; ++i) { + OS << ", "; + PrintExpr(E->getConstructorArg(i)); + } + } + OS << ")"; + } +} + +void StmtPrinter::VisitCXXDeleteExpr(CXXDeleteExpr *E) { + if (E->isGlobalDelete()) + OS << "::"; + OS << "delete "; + if (E->isArrayForm()) + OS << "[] "; + PrintExpr(E->getArgument()); +} + // Obj-C void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { diff --git a/lib/AST/StmtSerialization.cpp b/lib/AST/StmtSerialization.cpp index 268922e481..8faef8bb66 100644 --- a/lib/AST/StmtSerialization.cpp +++ b/lib/AST/StmtSerialization.cpp @@ -227,6 +227,12 @@ Stmt* Stmt::Create(Deserializer& D, ASTContext& C) { case CXXZeroInitValueExprClass: return CXXZeroInitValueExpr::CreateImpl(D, C); + + case CXXNewExprClass: + return CXXNewExpr::CreateImpl(D, C); + + case CXXDeleteExprClass: + return CXXDeleteExpr::CreateImpl(D, C); } } @@ -1414,3 +1420,66 @@ CXXZeroInitValueExpr::CreateImpl(Deserializer& D, ASTContext& C) { SourceLocation RParenLoc = SourceLocation::ReadVal(D); return new CXXZeroInitValueExpr(Ty, TyBeginLoc, RParenLoc); } + +void CXXNewExpr::EmitImpl(Serializer& S) const { + S.Emit(getType()); + S.Emit(Initializer); + S.Emit(NumPlacementArgs); + S.Emit(NumConstructorArgs); + S.BatchEmitOwnedPtrs(NumPlacementArgs + NumConstructorArgs, SubExprs); + assert((OperatorNew == 0 || S.isRegistered(OperatorNew)) && + (OperatorDelete == 0 || S.isRegistered(OperatorDelete)) && + (Constructor == 0 || S.isRegistered(Constructor)) && + "CXXNewExpr cannot own declarations"); + S.EmitPtr(OperatorNew); + S.EmitPtr(OperatorDelete); + S.EmitPtr(Constructor); + S.Emit(AllocType); + S.Emit(StartLoc); + S.Emit(EndLoc); +} + +CXXNewExpr * +CXXNewExpr::CreateImpl(Deserializer& D, ASTContext& C) { + QualType T = QualType::ReadVal(D); + bool GlobalNew = D.ReadBool(); + bool ParenTypeId = D.ReadBool(); + bool Initializer = D.ReadBool(); + unsigned NumPlacementArgs = D.ReadInt(); + unsigned NumConstructorArgs = D.ReadInt(); + unsigned TotalExprs = NumPlacementArgs + NumConstructorArgs; + Stmt** SubExprs = new Stmt*[TotalExprs]; + D.BatchReadOwnedPtrs(TotalExprs, SubExprs, C); + FunctionDecl *OperatorNew = D.ReadPtr(); + FunctionDecl *OperatorDelete = D.ReadPtr(); + CXXConstructorDecl *Constructor = D.ReadPtr(); + QualType AllocType = QualType::ReadVal(D); + SourceLocation StartLoc = SourceLocation::ReadVal(D); + SourceLocation EndLoc = SourceLocation::ReadVal(D); + + return new CXXNewExpr(T, AllocType, GlobalNew, ParenTypeId, Initializer, + NumPlacementArgs, NumConstructorArgs, SubExprs, + OperatorNew, OperatorDelete, Constructor, StartLoc, + EndLoc); +} + +void CXXDeleteExpr::EmitImpl(Serializer& S) const { + S.Emit(getType()); + S.Emit(GlobalDelete); + S.Emit(ArrayForm); + S.EmitPtr(OperatorDelete); + S.EmitOwnedPtr(Argument); + S.Emit(Loc); +} + +CXXDeleteExpr * +CXXDeleteExpr::CreateImpl(Deserializer& D, ASTContext& C) { + QualType Ty = QualType::ReadVal(D); + bool GlobalDelete = D.ReadBool(); + bool ArrayForm = D.ReadBool(); + FunctionDecl *OperatorDelete = D.ReadPtr(); + Stmt *Argument = D.ReadOwnedPtr(C); + SourceLocation Loc = SourceLocation::ReadVal(D); + return new CXXDeleteExpr(Ty, GlobalDelete, ArrayForm, OperatorDelete, + cast(Argument), Loc); +} diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 92a5fdb929..3262c0de1c 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -25,7 +25,11 @@ using namespace clang; /// ParseTypeName /// type-name: [C99 6.7.6] /// specifier-qualifier-list abstract-declarator[opt] -Parser::TypeTy *Parser::ParseTypeName() { +/// +/// Called type-id in C++. +/// CXXNewMode is a special flag used by the parser of C++ new-expressions. It +/// is simply passed on to ActOnTypeName. +Parser::TypeTy *Parser::ParseTypeName(bool CXXNewMode) { // Parse the common declaration-specifiers piece. DeclSpec DS; ParseSpecifierQualifierList(DS); @@ -34,7 +38,7 @@ Parser::TypeTy *Parser::ParseTypeName() { Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); ParseDeclarator(DeclaratorInfo); - return Actions.ActOnTypeName(CurScope, DeclaratorInfo).Val; + return Actions.ActOnTypeName(CurScope, DeclaratorInfo, CXXNewMode).Val; } /// ParseAttributes - Parse a non-empty attributes list. @@ -1292,12 +1296,12 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS) { void Parser::ParseDeclarator(Declarator &D) { /// This implements the 'declarator' production in the C grammar, then checks /// for well-formedness and issues diagnostics. - ParseDeclaratorInternal(D); + ParseDeclaratorInternal(D, &Parser::ParseDirectDeclarator); } -/// ParseDeclaratorInternal - Parse a C or C++ declarator. If -/// PtrOperator is true, then this routine won't parse the final -/// direct-declarator; therefore, it effectively parses the C++ +/// ParseDeclaratorInternal - Parse a C or C++ declarator. The direct-declarator +/// is parsed by the function passed to it. Pass null, and the direct-declarator +/// isn't parsed at all, making this function effectively parse the C++ /// ptr-operator production. /// /// declarator: [C99 6.7.5] @@ -1314,14 +1318,15 @@ void Parser::ParseDeclarator(Declarator &D) { /// '&' /// [GNU] '&' restrict[opt] attributes[opt] /// '::'[opt] nested-name-specifier '*' cv-qualifier-seq[opt] [TODO] -void Parser::ParseDeclaratorInternal(Declarator &D, bool PtrOperator) { +void Parser::ParseDeclaratorInternal(Declarator &D, + DirectDeclParseFunction DirectDeclParser) { tok::TokenKind Kind = Tok.getKind(); // Not a pointer, C++ reference, or block. if (Kind != tok::star && (Kind != tok::amp || !getLang().CPlusPlus) && (Kind != tok::caret || !getLang().Blocks)) { - if (!PtrOperator) - ParseDirectDeclarator(D); + if (DirectDeclParser) + (this->*DirectDeclParser)(D); return; } @@ -1335,7 +1340,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D, bool PtrOperator) { ParseTypeQualifierListOpt(DS); // Recursively parse the declarator. - ParseDeclaratorInternal(D, PtrOperator); + ParseDeclaratorInternal(D, DirectDeclParser); if (Kind == tok::star) // Remember that we parsed a pointer type, and remember the type-quals. D.AddTypeInfo(DeclaratorChunk::getPointer(DS.getTypeQualifiers(), Loc, @@ -1366,7 +1371,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D, bool PtrOperator) { } // Recursively parse the declarator. - ParseDeclaratorInternal(D, PtrOperator); + ParseDeclaratorInternal(D, DirectDeclParser); if (D.getNumTypeObjects() > 0) { // C++ [dcl.ref]p4: There shall be no references to references. @@ -1379,7 +1384,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D, bool PtrOperator) { Diag(InnerChunk.Loc, diag::err_illegal_decl_reference_to_reference) << "type name"; - // Once we've complained about the reference-to-referwnce, we + // Once we've complained about the reference-to-reference, we // can go ahead and build the (technically ill-formed) // declarator: reference collapsing will take care of it. } @@ -1581,7 +1586,7 @@ void Parser::ParseParenDeclarator(Declarator &D) { if (AttrList) D.AddAttributes(AttrList); - ParseDeclaratorInternal(D); + ParseDeclaratorInternal(D, &Parser::ParseDirectDeclarator); // Match the ')'. MatchRHSPunctuation(tok::r_paren, StartLoc); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 74b0715de2..9f9b306c45 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -351,6 +351,8 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, unsigned MinPrec) { /// [GNU] '__alignof' '(' type-name ')' /// [C++0x] 'alignof' '(' type-id ')' /// [GNU] '&&' identifier +/// [C++] new-expression +/// [C++] delete-expression /// /// unary-operator: one of /// '&' '*' '+' '-' '~' '!' @@ -405,6 +407,16 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, unsigned MinPrec) { /// '~' class-name [TODO] /// template-id [TODO] /// +/// new-expression: [C++ 5.3.4] +/// '::'[opt] 'new' new-placement[opt] new-type-id +/// new-initializer[opt] +/// '::'[opt] 'new' new-placement[opt] '(' type-id ')' +/// new-initializer[opt] +/// +/// delete-expression: [C++ 5.3.5] +/// '::'[opt] 'delete' cast-expression +/// '::'[opt] 'delete' '[' ']' cast-expression +/// Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { if (getLang().CPlusPlus) { // Annotate typenames and C++ scope specifiers. @@ -614,6 +626,13 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { Res = ParseCXXIdExpression(); return ParsePostfixExpressionSuffix(Res); + case tok::kw_new: // [C++] new-expression + // FIXME: ParseCXXIdExpression currently steals :: tokens. + return ParseCXXNewExpression(); + + case tok::kw_delete: // [C++] delete-expression + return ParseCXXDeleteExpression(); + case tok::at: { SourceLocation AtLoc = ConsumeToken(); return ParseObjCAtExpression(AtLoc); diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 46c7a135e0..9eb2431642 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -614,7 +614,7 @@ Parser::TypeTy *Parser::ParseConversionFunctionId() { // Parse the conversion-declarator, which is merely a sequence of // ptr-operators. Declarator D(DS, Declarator::TypeNameContext); - ParseDeclaratorInternal(D, /*PtrOperator=*/true); + ParseDeclaratorInternal(D, /*DirectDeclParser=*/0); // Finish up the type. Action::TypeResult Result = Actions.ActOnTypeName(CurScope, D); @@ -623,3 +623,229 @@ Parser::TypeTy *Parser::ParseConversionFunctionId() { else return Result.Val; } + +/// ParseCXXNewExpression - Parse a C++ new-expression. New is used to allocate +/// memory in a typesafe manner and call constructors. +/// +/// new-expression: +/// '::'[opt] 'new' new-placement[opt] new-type-id +/// new-initializer[opt] +/// '::'[opt] 'new' new-placement[opt] '(' type-id ')' +/// new-initializer[opt] +/// +/// new-placement: +/// '(' expression-list ')' +/// +/// new-initializer: +/// '(' expression-list[opt] ')' +/// [C++0x] braced-init-list [TODO] +/// +Parser::ExprResult Parser::ParseCXXNewExpression() +{ + assert((Tok.is(tok::coloncolon) || Tok.is(tok::kw_new)) && + "Expected :: or 'new' keyword"); + + SourceLocation Start = Tok.getLocation(); + bool UseGlobal = false; + if (Tok.is(tok::coloncolon)) { + UseGlobal = true; + ConsumeToken(); + } + + assert(Tok.is(tok::kw_new) && "Lookahead should have ensured 'new'"); + // Consume 'new' + ConsumeToken(); + + // A '(' now can be a new-placement or the '(' wrapping the type-id in the + // second form of new-expression. It can't be a new-type-id. + + ExprListTy PlacementArgs; + SourceLocation PlacementLParen, PlacementRParen; + + TypeTy *Ty = 0; + SourceLocation TyStart, TyEnd; + bool ParenTypeId; + if (Tok.is(tok::l_paren)) { + // If it turns out to be a placement, we change the type location. + PlacementLParen = ConsumeParen(); + TyStart = Tok.getLocation(); + if (ParseExpressionListOrTypeId(PlacementArgs, Ty)) + return true; + TyEnd = Tok.getLocation(); + + PlacementRParen = MatchRHSPunctuation(tok::r_paren, PlacementLParen); + if (PlacementRParen.isInvalid()) + return true; + + if (Ty) { + // Reset the placement locations. There was no placement. + PlacementLParen = PlacementRParen = SourceLocation(); + ParenTypeId = true; + } else { + // We still need the type. + if (Tok.is(tok::l_paren)) { + ConsumeParen(); + TyStart = Tok.getLocation(); + Ty = ParseTypeName(/*CXXNewMode=*/true); + ParenTypeId = true; + } else { + TyStart = Tok.getLocation(); + Ty = ParseNewTypeId(); + ParenTypeId = false; + } + if (!Ty) + return true; + TyEnd = Tok.getLocation(); + } + } else { + TyStart = Tok.getLocation(); + Ty = ParseNewTypeId(); + if (!Ty) + return true; + TyEnd = Tok.getLocation(); + ParenTypeId = false; + } + + ExprListTy ConstructorArgs; + SourceLocation ConstructorLParen, ConstructorRParen; + + if (Tok.is(tok::l_paren)) { + ConstructorLParen = ConsumeParen(); + if (Tok.isNot(tok::r_paren)) { + CommaLocsTy CommaLocs; + if (ParseExpressionList(ConstructorArgs, CommaLocs)) + return true; + } + ConstructorRParen = MatchRHSPunctuation(tok::r_paren, ConstructorLParen); + if (ConstructorRParen.isInvalid()) + return true; + } + + return Actions.ActOnCXXNew(Start, UseGlobal, PlacementLParen, + &PlacementArgs[0], PlacementArgs.size(), + PlacementRParen, ParenTypeId, TyStart, Ty, TyEnd, + ConstructorLParen, &ConstructorArgs[0], + ConstructorArgs.size(), ConstructorRParen); +} + +/// ParseNewTypeId - Parses a type ID as it appears in a new expression. +/// The most interesting part of this is the new-declarator, which can be a +/// multi-dimensional array, of which the first has a non-constant expression as +/// the size, e.g. +/// @code new int[runtimeSize()][2][2] @endcode +/// +/// new-type-id: +/// type-specifier-seq new-declarator[opt] +/// +/// new-declarator: +/// ptr-operator new-declarator[opt] +/// direct-new-declarator +/// +Parser::TypeTy * Parser::ParseNewTypeId() +{ + DeclSpec DS; + if (ParseCXXTypeSpecifierSeq(DS)) + return 0; + + // A new-declarator is a simplified version of a declarator. We use + // ParseDeclaratorInternal, but pass our own direct declarator parser, + // one that parses a direct-new-declarator. + Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); + ParseDeclaratorInternal(DeclaratorInfo, &Parser::ParseDirectNewDeclarator); + + TypeTy *Ty = Actions.ActOnTypeName(CurScope, DeclaratorInfo, + /*CXXNewMode=*/true).Val; + return DeclaratorInfo.getInvalidType() ? 0 : Ty; +} + +/// ParseDirectNewDeclarator - Parses a direct-new-declarator. Intended to be +/// passed to ParseDeclaratorInternal. +/// +/// direct-new-declarator: +/// '[' expression ']' +/// direct-new-declarator '[' constant-expression ']' +/// +void Parser::ParseDirectNewDeclarator(Declarator &D) +{ + // Parse the array dimensions. + bool first = true; + while (Tok.is(tok::l_square)) { + SourceLocation LLoc = ConsumeBracket(); + ExprResult Size = first ? ParseExpression() : ParseConstantExpression(); + if (Size.isInvalid) { + // Recover + SkipUntil(tok::r_square); + return; + } + first = false; + + D.AddTypeInfo(DeclaratorChunk::getArray(0, /*static=*/false, /*star=*/false, + Size.Val, LLoc)); + + if (MatchRHSPunctuation(tok::r_square, LLoc).isInvalid()) + return; + } +} + +/// ParseExpressionListOrTypeId - Parse either an expression-list or a type-id. +/// This ambiguity appears in the syntax of the C++ new operator. +/// +/// new-expression: +/// '::'[opt] 'new' new-placement[opt] '(' type-id ')' +/// new-initializer[opt] +/// +/// new-placement: +/// '(' expression-list ')' +/// +bool Parser::ParseExpressionListOrTypeId(ExprListTy &PlacementArgs, TypeTy *&Ty) +{ + // The '(' was already consumed. + if (isTypeIdInParens()) { + Ty = ParseTypeName(/*CXXNewMode=*/true); + return Ty == 0; + } + + // It's not a type, it has to be an expression list. + // Discard the comma locations - ActOnCXXNew has enough parameters. + CommaLocsTy CommaLocs; + return ParseExpressionList(PlacementArgs, CommaLocs); +} + +/// ParseCXXDeleteExpression - Parse a C++ delete-expression. Delete is used +/// to free memory allocated by new. +/// +/// delete-expression: +/// '::'[opt] 'delete' cast-expression +/// '::'[opt] 'delete' '[' ']' cast-expression +Parser::ExprResult Parser::ParseCXXDeleteExpression() +{ + assert((Tok.is(tok::coloncolon) || Tok.is(tok::kw_delete)) && + "Expected :: or 'delete' keyword"); + + SourceLocation Start = Tok.getLocation(); + bool UseGlobal = false; + if (Tok.is(tok::coloncolon)) { + UseGlobal = true; + ConsumeToken(); + } + + assert(Tok.is(tok::kw_delete) && "Lookahead should have ensured 'delete'"); + // Consume 'delete' + ConsumeToken(); + + // Array delete? + bool ArrayDelete = false; + if (Tok.is(tok::l_square)) { + ArrayDelete = true; + SourceLocation LHS = ConsumeBracket(); + SourceLocation RHS = MatchRHSPunctuation(tok::r_square, LHS); + if (RHS.isInvalid()) + return true; + } + + ExprResult Operand = ParseCastExpression(false); + if (Operand.isInvalid) + return Operand; + + return Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.Val); +} diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index ba647cae89..d569994d16 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -238,14 +238,16 @@ public: // QualType ConvertDeclSpecToType(const DeclSpec &DS); void ProcessTypeAttributeList(QualType &Result, const AttributeList *AL); - QualType GetTypeForDeclarator(Declarator &D, Scope *S); + QualType GetTypeForDeclarator(Declarator &D, Scope *S, + bool CXXNewMode = false); DeclarationName GetNameForDeclarator(Declarator &D); QualType ObjCGetTypeForMethodDefinition(DeclTy *D); bool UnwrapSimilarPointerTypes(QualType& T1, QualType& T2); - virtual TypeResult ActOnTypeName(Scope *S, Declarator &D); + virtual TypeResult ActOnTypeName(Scope *S, Declarator &D, + bool CXXNewMode = false); //===--------------------------------------------------------------------===// // Symbol table / Decl tracking callbacks: SemaDecl.cpp. @@ -805,6 +807,23 @@ public: SourceLocation *CommaLocs, SourceLocation RParenLoc); + /// ActOnCXXNew - Parsed a C++ 'new' expression. + virtual ExprResult ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, + SourceLocation PlacementLParen, + ExprTy **PlacementArgs, unsigned NumPlaceArgs, + SourceLocation PlacementRParen, + bool ParenTypeId, SourceLocation TyStart, + TypeTy *Ty, SourceLocation TyEnd, + SourceLocation ConstructorLParen, + ExprTy **ConstructorArgs, unsigned NumConsArgs, + SourceLocation ConstructorRParen); + bool CheckAllocatedType(QualType AllocType, SourceLocation StartLoc, + const SourceRange &TyR); + + /// ActOnCXXDelete - Parsed a C++ 'delete' expression + virtual ExprResult ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, + bool ArrayForm, ExprTy *Operand); + /// ActOnCXXConditionDeclarationExpr - Parsed a condition declaration of a /// C++ if/switch/while/for statement. /// e.g: "if (int x = f()) {...}" diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 55616bad76..80b5f20034 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -170,6 +170,200 @@ Sema::ActOnCXXTypeConstructExpr(SourceRange TypeRange, TypeTy *TypeRep, } +/// ActOnCXXNew - Parsed a C++ 'new' expression (C++ 5.3.4), as in e.g.: +/// @code new (memory) int[size][4] @endcode +/// or +/// @code ::new Foo(23, "hello") @endcode +/// For the interpretation of this heap of arguments, consult the base version. +Action::ExprResult +Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, + SourceLocation PlacementLParen, + ExprTy **PlacementArgs, unsigned NumPlaceArgs, + SourceLocation PlacementRParen, bool ParenTypeId, + SourceLocation TyStart, TypeTy *Ty, SourceLocation TyEnd, + SourceLocation ConstructorLParen, + ExprTy **ConstructorArgs, unsigned NumConsArgs, + SourceLocation ConstructorRParen) +{ + QualType AllocType = QualType::getFromOpaquePtr(Ty); + QualType CheckType = AllocType; + // To leverage the existing parser as much as possible, array types are + // parsed as VLAs. Unwrap for checking. + if (const VariableArrayType *VLA = Context.getAsVariableArrayType(AllocType)){ + CheckType = VLA->getElementType(); + } + + // Validate the type, and unwrap an array if any. + if (CheckAllocatedType(CheckType, StartLoc, SourceRange(TyStart, TyEnd))) + return true; + + QualType ResultType = Context.getPointerType(CheckType); + + // That every array dimension except the first is constant was already + // checked by the type check above. + // C++ 5.3.4p6: "The expression in a direct-new-declarator shall have integral + // or enumeration type with a non-negative value." + // This was checked by ActOnTypeName, since C99 has the same restriction on + // VLA expressions. + + // --- Choosing an allocation function --- + // C++ 5.3.4p8 - 14 & 18 + // 1) If UseGlobal is true, only look in the global scope. Else, also look + // in the scope of the allocated class. + // 2) If an array size is given, look for operator new[], else look for + // operator new. + // 3) The first argument is always size_t. Append the arguments from the + // placement form. + // FIXME: Find the correct overload of operator new. + // FIXME: Also find the corresponding overload of operator delete. + FunctionDecl *OperatorNew = 0; + FunctionDecl *OperatorDelete = 0; + Expr **PlaceArgs = (Expr**)PlacementArgs; + + bool Init = ConstructorLParen.isValid(); + // --- Choosing a constructor --- + // C++ 5.3.4p15 + // 1) If T is a POD and there's no initializer (ConstructorLParen is invalid) + // the object is not initialized. If the object, or any part of it, is + // const-qualified, it's an error. + // 2) If T is a POD and there's an empty initializer, the object is value- + // initialized. + // 3) If T is a POD and there's one initializer argument, the object is copy- + // constructed. + // 4) If T is a POD and there's more initializer arguments, it's an error. + // 5) If T is not a POD, the initializer arguments are used as constructor + // arguments. + // + // Or by the C++0x formulation: + // 1) If there's no initializer, the object is default-initialized according + // to C++0x rules. + // 2) Otherwise, the object is direct-initialized. + CXXConstructorDecl *Constructor = 0; + Expr **ConsArgs = (Expr**)ConstructorArgs; + if (CheckType->isRecordType()) { + // FIXME: This is incorrect for when there is an empty initializer and + // no user-defined constructor. Must zero-initialize, not default-construct. + Constructor = PerformInitializationByConstructor( + CheckType, ConsArgs, NumConsArgs, + TyStart, SourceRange(TyStart, ConstructorRParen), + CheckType.getAsString(), + NumConsArgs != 0 ? IK_Direct : IK_Default); + if (!Constructor) + return true; + } else { + if (!Init) { + // FIXME: Check that no subpart is const. + if (CheckType.isConstQualified()) { + Diag(StartLoc, diag::err_new_uninitialized_const) + << SourceRange(StartLoc, TyEnd); + return true; + } + } else if (NumConsArgs == 0) { + // Object is value-initialized. Do nothing. + } else if (NumConsArgs == 1) { + // Object is direct-initialized. + if (CheckInitializerTypes(ConsArgs[0], CheckType, StartLoc, + CheckType.getAsString())) + return true; + } else { + Diag(StartLoc, diag::err_builtin_direct_init_more_than_one_arg) + << SourceRange(ConstructorLParen, ConstructorRParen); + } + } + + // FIXME: Also check that the destructor is accessible. (C++ 5.3.4p16) + + return new CXXNewExpr(UseGlobal, OperatorNew, PlaceArgs, NumPlaceArgs, + ParenTypeId, AllocType, Constructor, Init, + ConsArgs, NumConsArgs, OperatorDelete, ResultType, + StartLoc, Init ? ConstructorRParen : TyEnd); +} + +/// CheckAllocatedType - Checks that a type is suitable as the allocated type +/// in a new-expression. +/// dimension off and stores the size expression in ArraySize. +bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation StartLoc, + const SourceRange &TyR) +{ + // C++ 5.3.4p1: "[The] type shall be a complete object type, but not an + // abstract class type or array thereof. + // FIXME: We don't have abstract types yet. + // FIXME: Under C++ semantics, an incomplete object type is still an object + // type. This code assumes the C semantics, where it's not. + if (!AllocType->isObjectType()) { + diag::kind msg; + if (AllocType->isFunctionType()) { + msg = diag::err_new_function; + } else if(AllocType->isIncompleteType()) { + msg = diag::err_new_incomplete; + } else if(AllocType->isReferenceType()) { + msg = diag::err_new_reference; + } else { + assert(false && "Unexpected type class"); + return true; + } + Diag(StartLoc, msg) << AllocType.getAsString() << TyR; + return true; + } + + // Every dimension beyond the first shall be of constant size. + while (const ArrayType *Array = Context.getAsArrayType(AllocType)) { + if (!Array->isConstantArrayType()) { + // FIXME: Might be nice to get a better source range from somewhere. + Diag(StartLoc, diag::err_new_array_nonconst) << TyR; + return true; + } + AllocType = Array->getElementType(); + } + + return false; +} + +/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in: +/// @code ::delete ptr; @endcode +/// or +/// @code delete [] ptr; @endcode +Action::ExprResult +Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, + bool ArrayForm, ExprTy *Operand) +{ + // C++ 5.3.5p1: "The operand shall have a pointer type, or a class type + // having a single conversion function to a pointer type. The result has + // type void." + // DR599 amends "pointer type" to "pointer to object type" in both cases. + + Expr *Ex = (Expr *)Operand; + QualType Type = Ex->getType(); + + if (Type->isRecordType()) { + // FIXME: Find that one conversion function and amend the type. + } + + if (!Type->isPointerType()) { + Diag(StartLoc, diag::err_delete_operand) + << Type.getAsString() << Ex->getSourceRange(); + return true; + } + + QualType Pointee = Type->getAsPointerType()->getPointeeType(); + if (Pointee->isIncompleteType() && !Pointee->isVoidType()) + Diag(StartLoc, diag::warn_delete_incomplete) + << Pointee.getAsString() << Ex->getSourceRange(); + else if (!Pointee->isObjectType()) { + Diag(StartLoc, diag::err_delete_operand) + << Type.getAsString() << Ex->getSourceRange(); + return true; + } + + // FIXME: Look up the correct operator delete overload and pass a pointer + // along. + // FIXME: Check access and ambiguity of operator delete and destructor. + + return new CXXDeleteExpr(Context.VoidTy, UseGlobal, ArrayForm, 0, Ex, + StartLoc); +} + + /// ActOnCXXConditionDeclarationExpr - Parsed a condition declaration of a /// C++ if/switch/while/for statement. /// e.g: "if (int x = f()) {...}" diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index a622c8afb8..7000c3f5be 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -250,14 +250,14 @@ QualType Sema::ConvertDeclSpecToType(const DeclSpec &DS) { /// GetTypeForDeclarator - Convert the type for the specified declarator to Type /// instances. -QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S) { +QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, bool CXXNewMode) { // long long is a C99 feature. if (!getLangOptions().C99 && !getLangOptions().CPlusPlus0x && D.getDeclSpec().getTypeSpecWidth() == DeclSpec::TSW_longlong) Diag(D.getDeclSpec().getTypeSpecWidthLoc(), diag::ext_longlong); QualType T = ConvertDeclSpecToType(D.getDeclSpec()); - + // Walk the DeclTypeInfo, building the recursive type as we go. DeclTypeInfos // are ordered from the identifier out, which is opposite of what we want :). for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) { @@ -340,6 +340,8 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S) { break; } case DeclaratorChunk::Array: { + // Only the outermost dimension gets special treatment. + bool UseCXXNewMode = CXXNewMode && i == e-1; DeclaratorChunk::ArrayTypeInfo &ATI = DeclType.Arr; Expr *ArraySize = static_cast(ATI.NumElts); ArrayType::ArraySizeModifier ASM; @@ -394,9 +396,11 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S) { if (!ArraySize) { T = Context.getIncompleteArrayType(T, ASM, ATI.TypeQuals); } else if (!ArraySize->isIntegerConstantExpr(ConstVal, Context) || - !T->isConstantSizeType()) { + !T->isConstantSizeType() || UseCXXNewMode) { // Per C99, a variable array is an array with either a non-constant // size or an element type that has a non-constant-size + // We also force this for parsing C++ new-expressions, since the + // outermost dimension is always treated as variable. T = Context.getVariableArrayType(T, ArraySize, ASM, ATI.TypeQuals); } else { // C99 6.7.5.2p1: If the expression is a constant expression, it shall @@ -416,7 +420,9 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S) { T = Context.getConstantArrayType(T, ConstVal, ASM, ATI.TypeQuals); } // If this is not C99, extwarn about VLA's and C99 array size modifiers. - if (!getLangOptions().C99 && + // Unless we're in C++ new mode. ActOnCXXNew will complain about them + // there, and they're hard errors. + if (!getLangOptions().C99 && !CXXNewMode && (ASM != ArrayType::Normal || (ArraySize && !ArraySize->isIntegerConstantExpr(Context)))) Diag(D.getIdentifierLoc(), diag::ext_vla); @@ -614,12 +620,12 @@ bool Sema::UnwrapSimilarPointerTypes(QualType& T1, QualType& T2) return false; } -Sema::TypeResult Sema::ActOnTypeName(Scope *S, Declarator &D) { +Sema::TypeResult Sema::ActOnTypeName(Scope *S, Declarator &D, bool CXXNewMode) { // C99 6.7.6: Type names have no identifier. This is already validated by // the parser. assert(D.getIdentifier() == 0 && "Type name should have no identifier!"); - QualType T = GetTypeForDeclarator(D, S); + QualType T = GetTypeForDeclarator(D, S, CXXNewMode); assert(!T.isNull() && "GetTypeForDeclarator() returned null type"); diff --git a/test/SemaCXX/new-delete.cpp b/test/SemaCXX/new-delete.cpp new file mode 100644 index 0000000000..449fcee648 --- /dev/null +++ b/test/SemaCXX/new-delete.cpp @@ -0,0 +1,56 @@ +struct S // expected-note {{candidate}} +{ + S(int, int, double); // expected-note {{candidate}} + S(double, int); // expected-note {{candidate}} expected-note {{candidate}} + S(float, int); // expected-note {{candidate}} expected-note {{candidate}} +}; +struct T; + +void good_news() +{ + int *pi = new int; + float *pf = new (pi) float(); + pi = new int(1); + pi = new int('c'); + const int *pci = new const int(); + S *ps = new S(1, 2, 3.4); + ps = new (pf) S(1, 2, 3.4); + S *(*paps)[2] = new S*[*pi][2]; + ps = new (S[3])(1, 2, 3.4); + typedef int ia4[4]; + ia4 *pai = new (int[3][4]); +} + +void bad_news() +{ + int i = 1; + (void)new; // expected-error {{missing type specifier}} + (void)new 4; // expected-error {{missing type specifier}} + (void)new () int; // expected-error {{expected expression}} + (void)new int[1.1]; // expected-error {{size of array has non-integer type}} + (void)new int[1][i]; // expected-error {{only the first dimension}} + (void)new (int[1][i]); // expected-error {{only the first dimension}} + (void)new int(*(S*)0); // expected-error {{incompatible type initializing}} + (void)new int(1, 2); // expected-error {{initializer of a builtin type can only take one argument}} + (void)new S(1); // expected-error {{no matching constructor}} + (void)new S(1, 1); // expected-error {{call to constructor of 'struct S' is ambiguous}} + (void)new const int; // expected-error {{must provide an initializer}} + + // Some lacking cases due to lack of sema support. +} + +void good_deletes() +{ + delete (int*)0; + delete [](int*)0; + delete (S*)0; +} + +void bad_deletes() +{ + delete 0; // expected-error {{cannot delete expression of type 'int'}} + delete [0] (int*)0; // expected-error {{expected ']'}} \ + // expected-error {{to match this '['}} + delete (void*)0; // expected-error {{cannot delete expression}} + delete (T*)0; // expected-warning {{deleting pointer to incomplete type}} +}