From: Douglas Gregor Date: Fri, 16 Jan 2009 18:33:17 +0000 (+0000) Subject: Part one of handling C++ functional casts. This handles semantic X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=506ae418eb171d072f2fb4f6bc46d258b52cbf97;p=clang Part one of handling C++ functional casts. This handles semantic analysis and AST-building for the cases where we have N != 1 arguments. For N == 1 arguments, we need to finish the C++ implementation of explicit type casts (C++ [expr.cast]). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@62329 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index a9ff1546e9..669e790b39 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -399,9 +399,70 @@ public: CreateImpl(llvm::Deserializer& D, ASTContext& C); }; +/// @brief Represents a C++ functional cast expression that builds a +/// temporary object. +/// +/// This expression type represents a C++ "functional" cast +/// (C++[expr.type.conv]) with N != 1 arguments that invokes a +/// constructor to build a temporary object. If N == 0 but no +/// constructor will be called (because the functional cast is +/// performing a value-initialized an object whose class type has no +/// user-declared constructors), CXXZeroInitValueExpr will represent +/// the functional cast. Finally, with N == 1 arguments the functional +/// cast expression will be represented by CXXFunctionalCastExpr. +/// Example: +/// @code +/// struct X { X(int, float); } +/// +/// X create_X() { +/// return X(1, 3.14f); // creates a CXXTemporaryObjectExpr +/// }; +/// @endcode +class CXXTemporaryObjectExpr : public Expr { + SourceLocation TyBeginLoc; + SourceLocation RParenLoc; + CXXConstructorDecl *Constructor; + Stmt **Args; + unsigned NumArgs; + +public: + CXXTemporaryObjectExpr(CXXConstructorDecl *Cons, QualType writtenTy, + SourceLocation tyBeginLoc, Expr **Args, + unsigned NumArgs, SourceLocation rParenLoc); + + ~CXXTemporaryObjectExpr(); + + SourceLocation getTypeBeginLoc() const { return TyBeginLoc; } + SourceLocation getRParenLoc() const { return RParenLoc; } + + typedef ExprIterator arg_iterator; + typedef ConstExprIterator const_arg_iterator; + + arg_iterator arg_begin() { return Args; } + arg_iterator arg_end() { return Args + NumArgs; } + const_arg_iterator arg_begin() const { return Args; } + const_arg_iterator arg_end() const { return Args + NumArgs; } + + virtual SourceRange getSourceRange() const { + return SourceRange(TyBeginLoc, RParenLoc); + } + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXTemporaryObjectExprClass; + } + static bool classof(const CXXTemporaryObjectExpr *) { return true; } + + // Iterators + virtual child_iterator child_begin(); + virtual child_iterator child_end(); + + virtual void EmitImpl(llvm::Serializer& S) const; + static CXXTemporaryObjectExpr *CreateImpl(llvm::Deserializer& D, ASTContext& C); +}; + /// CXXZeroInitValueExpr - [C++ 5.2.3p2] -/// Expression "T()" which creates a value-initialized Rvalue of non-class -/// type T. +/// Expression "T()" which creates a value-initialized rvalue of type +/// T, which is either a non-class type or a class type without any +/// user-defined constructors. /// class CXXZeroInitValueExpr : public Expr { SourceLocation TyBeginLoc; diff --git a/include/clang/AST/StmtNodes.def b/include/clang/AST/StmtNodes.def index 26d1e89637..2f86c93e6d 100644 --- a/include/clang/AST/StmtNodes.def +++ b/include/clang/AST/StmtNodes.def @@ -103,6 +103,7 @@ STMT(CXXDynamicCastExpr , CXXNamedCastExpr) STMT(CXXReinterpretCastExpr , CXXNamedCastExpr) STMT(CXXConstCastExpr , CXXNamedCastExpr) STMT(CXXFunctionalCastExpr , ExplicitCastExpr) +STMT(CXXTemporaryObjectExpr , Expr) STMT(CXXTypeidExpr , Expr) STMT(CXXBoolLiteralExpr , Expr) STMT(CXXThisExpr , Expr) diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 07d794ea43..b21b413689 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -61,6 +61,14 @@ Stmt::child_iterator CXXDefaultArgExpr::child_end() { return child_iterator(); } +// CXXTemporaryObjectExpr +Stmt::child_iterator CXXTemporaryObjectExpr::child_begin() { + return child_iterator(Args); +} +Stmt::child_iterator CXXTemporaryObjectExpr::child_end() { + return child_iterator(Args + NumArgs); +} + // CXXZeroInitValueExpr Stmt::child_iterator CXXZeroInitValueExpr::child_begin() { return child_iterator(); @@ -219,3 +227,23 @@ const char *CXXNamedCastExpr::getCastName() const { default: return ""; } } + +CXXTemporaryObjectExpr::CXXTemporaryObjectExpr(CXXConstructorDecl *Cons, + QualType writtenTy, + SourceLocation tyBeginLoc, + Expr **Args, + unsigned NumArgs, + SourceLocation rParenLoc) + : Expr(CXXTemporaryObjectExprClass, writtenTy), + TyBeginLoc(tyBeginLoc), RParenLoc(rParenLoc), + Constructor(Cons), Args(0), NumArgs(NumArgs) { + if (NumArgs > 0) { + this->Args = new Stmt*[NumArgs]; + for (unsigned i = 0; i < NumArgs; ++i) + this->Args[i] = Args[i]; + } +} + +CXXTemporaryObjectExpr::~CXXTemporaryObjectExpr() { + delete [] Args; +} diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 14d30d8673..ea96411e07 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -989,6 +989,19 @@ void StmtPrinter::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *Node) { OS << ")"; } +void StmtPrinter::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *Node) { + OS << Node->getType().getAsString(); + OS << "("; + for (CXXTemporaryObjectExpr::arg_iterator Arg = Node->arg_begin(), + ArgEnd = Node->arg_end(); + Arg != ArgEnd; ++Arg) { + if (Arg != Node->arg_begin()) + OS << ", "; + PrintExpr(*Arg); + } + OS << ")"; +} + void StmtPrinter::VisitCXXZeroInitValueExpr(CXXZeroInitValueExpr *Node) { OS << Node->getType().getAsString() << "()"; } diff --git a/lib/AST/StmtSerialization.cpp b/lib/AST/StmtSerialization.cpp index 02ccde3da2..39330ce335 100644 --- a/lib/AST/StmtSerialization.cpp +++ b/lib/AST/StmtSerialization.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Basic/TypeTraits.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" @@ -232,6 +233,9 @@ Stmt* Stmt::Create(Deserializer& D, ASTContext& C) { case CXXThisExprClass: return CXXThisExpr::CreateImpl(D, C); + case CXXTemporaryObjectExprClass: + return CXXTemporaryObjectExpr::CreateImpl(D, C); + case CXXZeroInitValueExprClass: return CXXZeroInitValueExpr::CreateImpl(D, C); @@ -1441,6 +1445,40 @@ CXXThisExpr* CXXThisExpr::CreateImpl(llvm::Deserializer& D, ASTContext&) { return new CXXThisExpr(Loc, Ty); } +void CXXTemporaryObjectExpr::EmitImpl(llvm::Serializer& S) const { + S.Emit(getType()); + S.Emit(TyBeginLoc); + S.Emit(RParenLoc); + S.EmitPtr(cast(Constructor)); + S.EmitInt(NumArgs); + if (NumArgs > 0) + S.BatchEmitOwnedPtrs(NumArgs, Args); +} + +CXXTemporaryObjectExpr * +CXXTemporaryObjectExpr::CreateImpl(llvm::Deserializer& D, ASTContext& C) { + QualType writtenTy = QualType::ReadVal(D); + SourceLocation tyBeginLoc = SourceLocation::ReadVal(D); + SourceLocation rParenLoc = SourceLocation::ReadVal(D); + CXXConstructorDecl * Cons = cast_or_null(D.ReadPtr()); + unsigned NumArgs = D.ReadInt(); + Stmt** Args = 0; + if (NumArgs > 0) { + Args = new Stmt*[NumArgs]; + D.BatchReadOwnedPtrs(NumArgs, Args, C); + } + + CXXTemporaryObjectExpr * Result + = new CXXTemporaryObjectExpr(Cons, writtenTy, tyBeginLoc, + (Expr**)Args, NumArgs, rParenLoc); + + if (NumArgs > 0) + delete [] Args; + + return Result; +} + + void CXXZeroInitValueExpr::EmitImpl(Serializer& S) const { S.Emit(getType()); S.Emit(TyBeginLoc); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 0a42de06ca..73ac18eb1a 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -120,21 +120,7 @@ Sema::ActOnCXXTypeConstructExpr(SourceRange TypeRange, TypeTy *TypeRep, SourceLocation TyBeginLoc = TypeRange.getBegin(); SourceRange FullRange = SourceRange(TyBeginLoc, RParenLoc); - if (const RecordType *RT = Ty->getAsRecordType()) { - // C++ 5.2.3p1: - // If the simple-type-specifier specifies a class type, the class type shall - // be complete. - // - if (!RT->getDecl()->isDefinition()) - return Diag(TyBeginLoc, diag::err_invalid_incomplete_type_use) - << Ty << FullRange; - - unsigned DiagID = PP.getDiagnostics().getCustomDiagID(Diagnostic::Error, - "class constructors are not supported yet"); - return Diag(TyBeginLoc, DiagID); - } - - // C++ 5.2.3p1: + // C++ [expr.type.conv]p1: // If the expression list is a single expression, the type conversion // expression is equivalent (in definedness, and if defined in meaning) to the // corresponding cast expression. @@ -146,7 +132,30 @@ Sema::ActOnCXXTypeConstructExpr(SourceRange TypeRange, TypeTy *TypeRep, Exprs[0], RParenLoc); } - // C++ 5.2.3p1: + if (const RecordType *RT = Ty->getAsRecordType()) { + CXXRecordDecl *Record = cast(RT->getDecl()); + + if (NumExprs > 1 || Record->hasUserDeclaredConstructor()) { + CXXConstructorDecl *Constructor + = PerformInitializationByConstructor(Ty, Exprs, NumExprs, + TypeRange.getBegin(), + SourceRange(TypeRange.getBegin(), + RParenLoc), + DeclarationName(), + IK_Direct); + + if (!Constructor) + return true; + + return new CXXTemporaryObjectExpr(Constructor, Ty, TyBeginLoc, + Exprs, NumExprs, RParenLoc); + } + + // Fall through to value-initialize an object of class type that + // doesn't have a user-declared default constructor. + } + + // C++ [expr.type.conv]p1: // If the expression list specifies more than a single value, the type shall // be a class with a suitably declared constructor. // @@ -156,7 +165,7 @@ Sema::ActOnCXXTypeConstructExpr(SourceRange TypeRange, TypeTy *TypeRep, assert(NumExprs == 0 && "Expected 0 expressions"); - // C++ 5.2.3p2: + // C++ [expr.type.conv]p2: // The expression T(), where T is a simple-type-specifier for a non-array // complete object type or the (possibly cv-qualified) void type, creates an // rvalue of the specified type, which is value-initialized. diff --git a/test/SemaCXX/functional-cast.cpp b/test/SemaCXX/functional-cast.cpp new file mode 100644 index 0000000000..3b65031d9c --- /dev/null +++ b/test/SemaCXX/functional-cast.cpp @@ -0,0 +1,27 @@ +// RUN: clang -fsyntax-only -verify %s + +struct SimpleValueInit { + int i; +}; + +struct InitViaConstructor { + InitViaConstructor(int i = 7); +}; + +// FIXME: error messages for implicitly-declared special member +// function candidates are very poor +struct NoValueInit { // expected-note{{candidate function}} + NoValueInit(int i, int j); // expected-note{{candidate function}} +}; + +void test_cxx_functional_value_init() { + (void)SimpleValueInit(); + (void)InitViaConstructor(); + (void)NoValueInit(); // expected-error{{no matching constructor for initialization}} +} + +void test_cxx_function_cast_multi() { + (void)NoValueInit(0, 0); + (void)NoValueInit(0, 0, 0); // expected-error{{no matching constructor for initialization}} + (void)int(1, 2); // expected-error{{function-style cast to a builtin type can only take one argument}} +}