Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -2821,6 +2821,7 @@ (op && op->containsUnexpandedParameterPack()))), Op(op) { CastExprBits.Kind = kind; + CastExprBits.PartOfExplicitCast = false; setBasePathSize(BasePathSize); assert(CastConsistency()); } @@ -2835,6 +2836,13 @@ CastKind getCastKind() const { return (CastKind) CastExprBits.Kind; } void setCastKind(CastKind K) { CastExprBits.Kind = K; } + bool getIsPartOfExplicitCast() const { + return CastExprBits.PartOfExplicitCast; + } + void setIsPartOfExplicitCast(bool PartOfExplicitCast) { + CastExprBits.PartOfExplicitCast = PartOfExplicitCast; + } + static const char *getCastKindName(CastKind CK); const char *getCastKindName() const { return getCastKindName(getCastKind()); } Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -202,7 +202,8 @@ unsigned : NumExprBits; unsigned Kind : 6; - unsigned BasePathSize : 32 - 6 - NumExprBits; + unsigned PartOfExplicitCast : 1; + unsigned BasePathSize : 32 - 6 - 1 - NumExprBits; }; class CallExprBitfields { Index: lib/AST/ASTDumper.cpp =================================================================== --- lib/AST/ASTDumper.cpp +++ lib/AST/ASTDumper.cpp @@ -2117,6 +2117,9 @@ } dumpBasePath(OS, Node); OS << ">"; + + if (Node->getIsPartOfExplicitCast()) + OS << " PartOfExplicitCast"; } void ASTDumper::VisitDeclRefExpr(const DeclRefExpr *Node) { Index: lib/Sema/SemaCast.cpp =================================================================== --- lib/Sema/SemaCast.cpp +++ lib/Sema/SemaCast.cpp @@ -55,7 +55,7 @@ namespace { struct CastOperation { CastOperation(Sema &S, QualType destType, ExprResult src) - : Self(S), SrcExpr(src), DestType(destType), + : Self(S), SrcExpr(src), OrigSrcExpr(SrcExpr), DestType(destType), ResultType(destType.getNonLValueExprType(S.Context)), ValueKind(Expr::getValueKindForType(destType)), Kind(CK_Dependent), IsARCUnbridgedCast(false) { @@ -70,6 +70,8 @@ Sema &Self; ExprResult SrcExpr; + const ExprResult OrigSrcExpr; + // SrcExpr will be modified, but OrigSrcExpr will retain the original value. QualType DestType; QualType ResultType; ExprValueKind ValueKind; @@ -89,6 +91,15 @@ void CheckCXXCStyleCast(bool FunctionalCast, bool ListInitialization); void CheckCStyleCast(); + void updatePartOfExplicitCastFlags(CastExpr *CE) { + // Walk down from the CE to the OrigSrcExpr, and mark all immediate + // ImplicitCastExpr's as being part of ExplicitCastExpr. The original CE + // (which is a ExplicitCastExpr), and the OrigSrcExpr are not touched. + while (OrigSrcExpr.get() != CE->getSubExpr() && + (CE = dyn_cast(CE->getSubExpr()))) + CE->setIsPartOfExplicitCast(true); + } + /// Complete an apparently-successful cast operation that yields /// the given expression. ExprResult complete(CastExpr *castExpr) { @@ -100,6 +111,7 @@ CK_Dependent, castExpr, nullptr, castExpr->getValueKind()); } + updatePartOfExplicitCastFlags(castExpr); return castExpr; } Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -723,6 +723,7 @@ assert(NumBaseSpecs == E->path_size()); E->setSubExpr(Record.readSubExpr()); E->setCastKind((CastKind)Record.readInt()); + E->setIsPartOfExplicitCast(Record.readInt()); CastExpr::path_iterator BaseI = E->path_begin(); while (NumBaseSpecs--) { auto *BaseSpec = new (Record.getContext()) CXXBaseSpecifier; Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -2192,6 +2192,7 @@ // CastExpr Abv->Add(BitCodeAbbrevOp(0)); // PathSize Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 6)); // CastKind + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // PartOfExplicitCast // ImplicitCastExpr ExprImplicitCastAbbrev = Stream.EmitAbbrev(std::move(Abv)); Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -665,6 +665,7 @@ Record.push_back(E->path_size()); Record.AddStmt(E->getSubExpr()); Record.push_back(E->getCastKind()); // FIXME: stable encoding + Record.push_back(E->getIsPartOfExplicitCast()); for (CastExpr::path_iterator PI = E->path_begin(), PE = E->path_end(); PI != PE; ++PI) Index: test/Sema/multistep-explicit-cast.c =================================================================== --- /dev/null +++ test/Sema/multistep-explicit-cast.c @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -ast-dump %s | FileCheck %s + +// We are checking that implicit casts don't get marked with 'PartOfExplicitCast', +// while in explicit casts, the implicitly-inserted implicit casts are marked with 'PartOfExplicitCast' + +unsigned char implicitcast_0(unsigned int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} implicitcast_0 'unsigned char (unsigned int)'{{$}} + // CHECK: ImplicitCastExpr {{.*}} 'unsigned char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' {{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'x' 'unsigned int'{{$}} + return x; +} + +signed char implicitcast_1(unsigned int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} implicitcast_1 'signed char (unsigned int)'{{$}} + // CHECK: ImplicitCastExpr {{.*}} 'signed char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' {{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'x' 'unsigned int'{{$}} + return x; +} + +unsigned char implicitcast_2(signed int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} implicitcast_2 'unsigned char (int)'{{$}} + // CHECK: ImplicitCastExpr {{.*}} 'unsigned char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' {{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int'{{$}} + return x; +} + +signed char implicitcast_3(signed int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} implicitcast_3 'signed char (int)'{{$}} + // CHECK: ImplicitCastExpr {{.*}} 'signed char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' {{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int'{{$}} + return x; +} + +//----------------------------------------------------------------------------// + +unsigned char cstylecast_0(unsigned int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cstylecast_0 'unsigned char (unsigned int)'{{$}} + // CHECK: CStyleCastExpr {{.*}} 'unsigned char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'x' 'unsigned int'{{$}} + return (unsigned char)x; +} + +signed char cstylecast_1(unsigned int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cstylecast_1 'signed char (unsigned int)'{{$}} + // CHECK: CStyleCastExpr {{.*}} 'signed char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'x' 'unsigned int'{{$}} + return (signed char)x; +} + +unsigned char cstylecast_2(signed int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cstylecast_2 'unsigned char (int)'{{$}} + // CHECK: CStyleCastExpr {{.*}} 'unsigned char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int'{{$}} + return (unsigned char)x; +} + +signed char cstylecast_3(signed int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cstylecast_3 'signed char (int)'{{$}} + // CHECK: CStyleCastExpr {{.*}} 'signed char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int'{{$}} + return (signed char)x; +} Index: test/SemaCXX/multistep-explicit-cast.cpp =================================================================== --- /dev/null +++ test/SemaCXX/multistep-explicit-cast.cpp @@ -0,0 +1,155 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -ast-dump %s | FileCheck %s + +// We are checking that implicit casts don't get marked with 'PartOfExplicitCast', +// while in explicit casts, the implicitly-inserted implicit casts are marked with 'PartOfExplicitCast' + +unsigned char implicitcast_0(unsigned int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} implicitcast_0 'unsigned char (unsigned int)'{{$}} + // CHECK: ImplicitCastExpr {{.*}} 'unsigned char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' {{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'x' 'unsigned int'{{$}} + return x; +} + +signed char implicitcast_1(unsigned int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} implicitcast_1 'signed char (unsigned int)'{{$}} + // CHECK: ImplicitCastExpr {{.*}} 'signed char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' {{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'x' 'unsigned int'{{$}} + return x; +} + +unsigned char implicitcast_2(signed int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} implicitcast_2 'unsigned char (int)'{{$}} + // CHECK: ImplicitCastExpr {{.*}} 'unsigned char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' {{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int'{{$}} + return x; +} + +signed char implicitcast_3(signed int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} implicitcast_3 'signed char (int)'{{$}} + // CHECK: ImplicitCastExpr {{.*}} 'signed char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' {{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int'{{$}} + return x; +} + +//----------------------------------------------------------------------------// + +unsigned char cstylecast_0(unsigned int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cstylecast_0 'unsigned char (unsigned int)'{{$}} + // CHECK: CStyleCastExpr {{.*}} 'unsigned char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'x' 'unsigned int'{{$}} + return (unsigned char)x; +} + +signed char cstylecast_1(unsigned int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cstylecast_1 'signed char (unsigned int)'{{$}} + // CHECK: CStyleCastExpr {{.*}} 'signed char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'signed char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'x' 'unsigned int'{{$}} + return (signed char)x; +} + +unsigned char cstylecast_2(signed int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cstylecast_2 'unsigned char (int)'{{$}} + // CHECK: CStyleCastExpr {{.*}} 'unsigned char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int'{{$}} + return (unsigned char)x; +} + +signed char cstylecast_3(signed int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cstylecast_3 'signed char (int)'{{$}} + // CHECK: CStyleCastExpr {{.*}} 'signed char' {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'signed char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int'{{$}} + return (signed char)x; +} + +//----------------------------------------------------------------------------// + +unsigned char cxxstaticcast_0(unsigned int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cxxstaticcast_0 'unsigned char (unsigned int)'{{$}} + // CHECK: CXXStaticCastExpr {{.*}} 'unsigned char' static_cast {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'x' 'unsigned int'{{$}} + return static_cast(x); +} + +signed char cxxstaticcast_1(unsigned int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cxxstaticcast_1 'signed char (unsigned int)'{{$}} + // CHECK: CXXStaticCastExpr {{.*}} 'signed char' static_cast {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'signed char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'x' 'unsigned int'{{$}} + return static_cast(x); +} + +unsigned char cxxstaticcast_2(signed int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cxxstaticcast_2 'unsigned char (int)'{{$}} + // CHECK: CXXStaticCastExpr {{.*}} 'unsigned char' static_cast {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int'{{$}} + return static_cast(x); +} + +signed char cxxstaticcast_3(signed int x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cxxstaticcast_3 'signed char (int)'{{$}} + // CHECK: CXXStaticCastExpr {{.*}} 'signed char' static_cast {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'signed char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int'{{$}} + return static_cast(x); +} + +//----------------------------------------------------------------------------// + +using UnsignedChar = unsigned char; +using SignedChar = signed char; +using UnsignedInt = unsigned int; +using SignedInt = signed int; + +UnsignedChar cxxfunctionalcast_0(UnsignedInt x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cxxfunctionalcast_0 'UnsignedChar (UnsignedInt)'{{$}} + // CHECK: CXXFunctionalCastExpr {{.*}} 'UnsignedChar':'unsigned char' functional cast to UnsignedChar {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'UnsignedChar':'unsigned char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'UnsignedInt':'unsigned int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'UnsignedInt':'unsigned int' lvalue ParmVar {{.*}} 'x' 'UnsignedInt':'unsigned int'{{$}} + return UnsignedChar(x); +} + +SignedChar cxxfunctionalcast_1(UnsignedInt x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cxxfunctionalcast_1 'SignedChar (UnsignedInt)'{{$}} + // CHECK: CXXFunctionalCastExpr {{.*}} 'SignedChar':'signed char' functional cast to SignedChar {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'SignedChar':'signed char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'UnsignedInt':'unsigned int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'UnsignedInt':'unsigned int' lvalue ParmVar {{.*}} 'x' 'UnsignedInt':'unsigned int'{{$}} + return SignedChar(x); +} + +UnsignedChar cxxfunctionalcast_2(SignedInt x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cxxfunctionalcast_2 'UnsignedChar (SignedInt)'{{$}} + // CHECK: CXXFunctionalCastExpr {{.*}} 'UnsignedChar':'unsigned char' functional cast to UnsignedChar {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'UnsignedChar':'unsigned char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'SignedInt':'int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'SignedInt':'int' lvalue ParmVar {{.*}} 'x' 'SignedInt':'int'{{$}} + return UnsignedChar(x); +} + +SignedChar cxxfunctionalcast_3(SignedInt x) { + // CHECK: FunctionDecl {{.*}} <{{.*}}, line:{{.*}}> line:{{.*}} cxxfunctionalcast_3 'SignedChar (SignedInt)'{{$}} + // CHECK: CXXFunctionalCastExpr {{.*}} 'SignedChar':'signed char' functional cast to SignedChar {{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'SignedChar':'signed char' PartOfExplicitCast{{$}} + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'SignedInt':'int' PartOfExplicitCast{{$}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'SignedInt':'int' lvalue ParmVar {{.*}} 'x' 'SignedInt':'int'{{$}} + return SignedChar(x); +}