summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Ballman <aaron@aaronballman.com>2018-01-09 13:07:03 +0000
committerAaron Ballman <aaron@aaronballman.com>2018-01-09 13:07:03 +0000
commitde3473e38c50b0bc4f6040187c3345a23819a114 (patch)
treeccce40c1c95e9838fb2b3fc07ea2b0e2c2723da3
parent87bfdf90ba9e94ecc89cc413013597c6fab9b0c8 (diff)
downloadclang-de3473e38c50b0bc4f6040187c3345a23819a114.tar.gz
Track in the AST whether the operand to a UnaryOperator can overflow and then use that logic when evaluating constant expressions and emitting codegen.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@322074 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/AST/Expr.h29
-rw-r--r--lib/AST/ASTDumper.cpp14
-rw-r--r--lib/AST/ASTImporter.cpp11
-rw-r--r--lib/AST/ExprConstant.cpp89
-rw-r--r--lib/Analysis/BodyFarm.cpp9
-rw-r--r--lib/CodeGen/CGExprScalar.cpp48
-rw-r--r--lib/CodeGen/CGObjC.cpp6
-rw-r--r--lib/CodeGen/CGStmtOpenMP.cpp2
-rw-r--r--lib/Frontend/Rewrite/RewriteModernObjC.cpp25
-rw-r--r--lib/Frontend/Rewrite/RewriteObjC.cpp28
-rw-r--r--lib/Sema/SemaDeclCXX.cpp26
-rw-r--r--lib/Sema/SemaExpr.cpp18
-rw-r--r--lib/Sema/SemaExprObjC.cpp6
-rw-r--r--lib/Sema/SemaOverload.cpp6
-rw-r--r--lib/Sema/SemaPseudoObject.cpp44
-rw-r--r--lib/Serialization/ASTReaderStmt.cpp13
-rw-r--r--lib/Serialization/ASTWriterStmt.cpp13
-rw-r--r--test/Misc/ast-dump-stmt.c38
18 files changed, 245 insertions, 180 deletions
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h
index f277094037..9d7a29e909 100644
--- a/include/clang/AST/Expr.h
+++ b/include/clang/AST/Expr.h
@@ -1720,19 +1720,19 @@ public:
private:
unsigned Opc : 5;
+ unsigned CanOverflow : 1;
SourceLocation Loc;
Stmt *Val;
public:
-
- UnaryOperator(Expr *input, Opcode opc, QualType type,
- ExprValueKind VK, ExprObjectKind OK, SourceLocation l)
- : Expr(UnaryOperatorClass, type, VK, OK,
- input->isTypeDependent() || type->isDependentType(),
- input->isValueDependent(),
- (input->isInstantiationDependent() ||
- type->isInstantiationDependentType()),
- input->containsUnexpandedParameterPack()),
- Opc(opc), Loc(l), Val(input) {}
+ UnaryOperator(Expr *input, Opcode opc, QualType type, ExprValueKind VK,
+ ExprObjectKind OK, SourceLocation l, bool CanOverflow)
+ : Expr(UnaryOperatorClass, type, VK, OK,
+ input->isTypeDependent() || type->isDependentType(),
+ input->isValueDependent(),
+ (input->isInstantiationDependent() ||
+ type->isInstantiationDependentType()),
+ input->containsUnexpandedParameterPack()),
+ Opc(opc), CanOverflow(CanOverflow), Loc(l), Val(input) {}
/// \brief Build an empty unary operator.
explicit UnaryOperator(EmptyShell Empty)
@@ -1748,6 +1748,15 @@ public:
SourceLocation getOperatorLoc() const { return Loc; }
void setOperatorLoc(SourceLocation L) { Loc = L; }
+ /// Returns true if the unary operator can cause an overflow. For instance,
+ /// signed int i = INT_MAX; i++;
+ /// signed char c = CHAR_MAX; c++;
+ /// Due to integer promotions, c++ is promoted to an int before the postfix
+ /// increment, and the result is an int that cannot overflow. However, i++
+ /// can overflow.
+ bool canOverflow() const { return CanOverflow; }
+ void setCanOverflow(bool C) { CanOverflow = C; }
+
/// isPostfix - Return true if this is a postfix operation, like x++.
static bool isPostfix(Opcode Op) {
return Op == UO_PostInc || Op == UO_PostDec;
diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp
index 22b71e3cf5..e553ba7e7a 100644
--- a/lib/AST/ASTDumper.cpp
+++ b/lib/AST/ASTDumper.cpp
@@ -2214,12 +2214,14 @@ void ASTDumper::VisitArrayInitIndexExpr(const ArrayInitIndexExpr *E) {
}
void ASTDumper::VisitUnaryOperator(const UnaryOperator *Node) {
- VisitExpr(Node);
- OS << " " << (Node->isPostfix() ? "postfix" : "prefix")
- << " '" << UnaryOperator::getOpcodeStr(Node->getOpcode()) << "'";
-}
-
-void ASTDumper::VisitUnaryExprOrTypeTraitExpr(
+ VisitExpr(Node);
+ OS << " " << (Node->isPostfix() ? "postfix" : "prefix")
+ << " '" << UnaryOperator::getOpcodeStr(Node->getOpcode()) << "'";
+ if (!Node->canOverflow())
+ OS << " cannot overflow";
+}
+
+void ASTDumper::VisitUnaryExprOrTypeTraitExpr(
const UnaryExprOrTypeTraitExpr *Node) {
VisitExpr(Node);
switch(Node->getKind()) {
diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp
index 0d1d980754..cca43eed3e 100644
--- a/lib/AST/ASTImporter.cpp
+++ b/lib/AST/ASTImporter.cpp
@@ -5120,14 +5120,13 @@ Expr *ASTNodeImporter::VisitUnaryOperator(UnaryOperator *E) {
if (!SubExpr)
return nullptr;
- return new (Importer.getToContext()) UnaryOperator(SubExpr, E->getOpcode(),
- T, E->getValueKind(),
- E->getObjectKind(),
- Importer.Import(E->getOperatorLoc()));
+ return new (Importer.getToContext()) UnaryOperator(
+ SubExpr, E->getOpcode(), T, E->getValueKind(), E->getObjectKind(),
+ Importer.Import(E->getOperatorLoc()), E->canOverflow());
}
-Expr *ASTNodeImporter::VisitUnaryExprOrTypeTraitExpr(
- UnaryExprOrTypeTraitExpr *E) {
+Expr *
+ASTNodeImporter::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
QualType ResultType = Importer.Import(E->getType());
if (E->isArgumentType()) {
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index 6cbb498abd..61bd1e5f12 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -3244,17 +3244,12 @@ static bool handleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal,
}
CompleteObject Obj = findCompleteObject(Info, E, AK_Assign, LVal, LValType);
- return Obj && modifySubobject(Info, E, Obj, LVal.Designator, Val);
-}
-
-static bool isOverflowingIntegerType(ASTContext &Ctx, QualType T) {
- return T->isSignedIntegerType() &&
- Ctx.getIntWidth(T) >= Ctx.getIntWidth(Ctx.IntTy);
-}
-
-namespace {
-struct CompoundAssignSubobjectHandler {
- EvalInfo &Info;
+ return Obj && modifySubobject(Info, E, Obj, LVal.Designator, Val);
+}
+
+namespace {
+struct CompoundAssignSubobjectHandler {
+ EvalInfo &Info;
const Expr *E;
QualType PromotedLHSType;
BinaryOperatorKind Opcode;
@@ -3370,13 +3365,13 @@ static bool handleCompoundAssignment(
return Obj && findSubobject(Info, E, Obj, LVal.Designator, Handler);
}
-namespace {
-struct IncDecSubobjectHandler {
- EvalInfo &Info;
- const Expr *E;
- AccessKinds AccessKind;
- APValue *Old;
-
+namespace {
+struct IncDecSubobjectHandler {
+ EvalInfo &Info;
+ const UnaryOperator *E;
+ AccessKinds AccessKind;
+ APValue *Old;
+
typedef bool result_type;
bool checkConst(QualType QT) {
@@ -3442,22 +3437,20 @@ struct IncDecSubobjectHandler {
}
bool WasNegative = Value.isNegative();
- if (AccessKind == AK_Increment) {
- ++Value;
-
- if (!WasNegative && Value.isNegative() &&
- isOverflowingIntegerType(Info.Ctx, SubobjType)) {
- APSInt ActualValue(Value, /*IsUnsigned*/true);
- return HandleOverflow(Info, E, ActualValue, SubobjType);
- }
- } else {
- --Value;
-
- if (WasNegative && !Value.isNegative() &&
- isOverflowingIntegerType(Info.Ctx, SubobjType)) {
- unsigned BitWidth = Value.getBitWidth();
- APSInt ActualValue(Value.sext(BitWidth + 1), /*IsUnsigned*/false);
- ActualValue.setBit(BitWidth);
+ if (AccessKind == AK_Increment) {
+ ++Value;
+
+ if (!WasNegative && Value.isNegative() && E->canOverflow()) {
+ APSInt ActualValue(Value, /*IsUnsigned*/true);
+ return HandleOverflow(Info, E, ActualValue, SubobjType);
+ }
+ } else {
+ --Value;
+
+ if (WasNegative && !Value.isNegative() && E->canOverflow()) {
+ unsigned BitWidth = Value.getBitWidth();
+ APSInt ActualValue(Value.sext(BitWidth + 1), /*IsUnsigned*/false);
+ ActualValue.setBit(BitWidth);
return HandleOverflow(Info, E, ActualValue, SubobjType);
}
}
@@ -3512,13 +3505,13 @@ static bool handleIncDec(EvalInfo &Info, const Expr *E, const LValue &LVal,
Info.FFDiag(E);
return false;
}
-
- AccessKinds AK = IsIncrement ? AK_Increment : AK_Decrement;
- CompleteObject Obj = findCompleteObject(Info, E, AK, LVal, LValType);
- IncDecSubobjectHandler Handler = { Info, E, AK, Old };
- return Obj && findSubobject(Info, E, Obj, LVal.Designator, Handler);
-}
-
+
+ AccessKinds AK = IsIncrement ? AK_Increment : AK_Decrement;
+ CompleteObject Obj = findCompleteObject(Info, E, AK, LVal, LValType);
+ IncDecSubobjectHandler Handler = {Info, cast<UnaryOperator>(E), AK, Old};
+ return Obj && findSubobject(Info, E, Obj, LVal.Designator, Handler);
+}
+
/// Build an lvalue for the object argument of a member function call.
static bool EvaluateObjectArgument(EvalInfo &Info, const Expr *Object,
LValue &This) {
@@ -8875,13 +8868,13 @@ bool IntExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
return Visit(E->getSubExpr());
case UO_Minus: {
if (!Visit(E->getSubExpr()))
- return false;
- if (!Result.isInt()) return Error(E);
- const APSInt &Value = Result.getInt();
- if (Value.isSigned() && Value.isMinSignedValue() &&
- !HandleOverflow(Info, E, -Value.extend(Value.getBitWidth() + 1),
- E->getType()))
- return false;
+ return false;
+ if (!Result.isInt()) return Error(E);
+ const APSInt &Value = Result.getInt();
+ if (Value.isSigned() && Value.isMinSignedValue() && E->canOverflow() &&
+ !HandleOverflow(Info, E, -Value.extend(Value.getBitWidth() + 1),
+ E->getType()))
+ return false;
return Success(-Value, E);
}
case UO_Not: {
diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp
index 89ca848481..492d01f6c9 100644
--- a/lib/Analysis/BodyFarm.cpp
+++ b/lib/Analysis/BodyFarm.cpp
@@ -149,7 +149,8 @@ DeclRefExpr *ASTMaker::makeDeclRefExpr(
UnaryOperator *ASTMaker::makeDereference(const Expr *Arg, QualType Ty) {
return new (C) UnaryOperator(const_cast<Expr*>(Arg), UO_Deref, Ty,
- VK_LValue, OK_Ordinary, SourceLocation());
+ VK_LValue, OK_Ordinary, SourceLocation(),
+ /*CanOverflow*/ false);
}
ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType Ty) {
@@ -441,7 +442,8 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
/* opc=*/ UO_LNot,
/* QualType=*/ C.IntTy,
/* ExprValueKind=*/ VK_RValue,
- /* ExprObjectKind=*/ OK_Ordinary, SourceLocation());
+ /* ExprObjectKind=*/ OK_Ordinary, SourceLocation(),
+ /* CanOverflow*/ false);
// Create assignment.
BinaryOperator *FlagAssignment = M.makeAssignment(
@@ -505,7 +507,8 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
// (2) Create the assignment to the predicate.
Expr *DoneValue =
new (C) UnaryOperator(M.makeIntegerLiteral(0, C.LongTy), UO_Not, C.LongTy,
- VK_RValue, OK_Ordinary, SourceLocation());
+ VK_RValue, OK_Ordinary, SourceLocation(),
+ /*CanOverflow*/false);
BinaryOperator *B =
M.makeAssignment(
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index c46215067a..668c572942 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -162,13 +162,13 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) {
// we can elide the overflow check.
if (!Op.mayHaveIntegerOverflow())
return true;
-
- // If a unary op has a widened operand, the op cannot overflow.
- if (const auto *UO = dyn_cast<UnaryOperator>(Op.E))
- return IsWidenedIntegerOp(Ctx, UO->getSubExpr());
-
- // We usually don't need overflow checks for binops with widened operands.
- // Multiplication with promoted unsigned operands is a special case.
+
+ // If a unary op has a widened operand, the op cannot overflow.
+ if (const auto *UO = dyn_cast<UnaryOperator>(Op.E))
+ return !UO->canOverflow();
+
+ // We usually don't need overflow checks for binops with widened operands.
+ // Multiplication with promoted unsigned operands is a special case.
const auto *BO = cast<BinaryOperator>(Op.E);
auto OptionalLHSTy = getUnwidenedIntegerType(Ctx, BO->getLHS());
if (!OptionalLHSTy)
@@ -1870,13 +1870,13 @@ llvm::Value *ScalarExprEmitter::EmitIncDecConsiderOverflowBehavior(
return Builder.CreateAdd(InVal, Amount, Name);
case LangOptions::SOB_Undefined:
if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow))
- return Builder.CreateNSWAdd(InVal, Amount, Name);
- // Fall through.
- case LangOptions::SOB_Trapping:
- if (IsWidenedIntegerOp(CGF.getContext(), E->getSubExpr()))
- return Builder.CreateNSWAdd(InVal, Amount, Name);
- return EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(E, InVal, IsInc));
- }
+ return Builder.CreateNSWAdd(InVal, Amount, Name);
+ // Fall through.
+ case LangOptions::SOB_Trapping:
+ if (!E->canOverflow())
+ return Builder.CreateNSWAdd(InVal, Amount, Name);
+ return EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(E, InVal, IsInc));
+ }
llvm_unreachable("Unknown SignedOverflowBehaviorTy");
}
@@ -1952,17 +1952,15 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
value = Builder.getTrue();
// Most common case by far: integer increment.
- } else if (type->isIntegerType()) {
- // Note that signed integer inc/dec with width less than int can't
- // overflow because of promotion rules; we're just eliding a few steps here.
- bool CanOverflow = value->getType()->getIntegerBitWidth() >=
- CGF.IntTy->getIntegerBitWidth();
- if (CanOverflow && type->isSignedIntegerOrEnumerationType()) {
- value = EmitIncDecConsiderOverflowBehavior(E, value, isInc);
- } else if (CanOverflow && type->isUnsignedIntegerType() &&
- CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow)) {
- value =
- EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(E, value, isInc));
+ } else if (type->isIntegerType()) {
+ // Note that signed integer inc/dec with width less than int can't
+ // overflow because of promotion rules; we're just eliding a few steps here.
+ if (E->canOverflow() && type->isSignedIntegerOrEnumerationType()) {
+ value = EmitIncDecConsiderOverflowBehavior(E, value, isInc);
+ } else if (E->canOverflow() && type->isUnsignedIntegerType() &&
+ CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow)) {
+ value =
+ EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(E, value, isInc));
} else {
llvm::Value *amt = llvm::ConstantInt::get(value->getType(), amount, true);
value = Builder.CreateAdd(value, amt, isInc ? "inc" : "dec");
diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp
index f26263d947..894d0d5fe2 100644
--- a/lib/CodeGen/CGObjC.cpp
+++ b/lib/CodeGen/CGObjC.cpp
@@ -3268,12 +3268,12 @@ CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction(
DeclRefExpr DstExpr(&DstDecl, false, DestTy,
VK_RValue, SourceLocation());
UnaryOperator DST(&DstExpr, UO_Deref, DestTy->getPointeeType(),
- VK_LValue, OK_Ordinary, SourceLocation());
+ VK_LValue, OK_Ordinary, SourceLocation(), false);
DeclRefExpr SrcExpr(&SrcDecl, false, SrcTy,
VK_RValue, SourceLocation());
UnaryOperator SRC(&SrcExpr, UO_Deref, SrcTy->getPointeeType(),
- VK_LValue, OK_Ordinary, SourceLocation());
+ VK_LValue, OK_Ordinary, SourceLocation(), false);
Expr *Args[2] = { &DST, &SRC };
CallExpr *CalleeExp = cast<CallExpr>(PID->getSetterCXXAssignment());
@@ -3351,7 +3351,7 @@ CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction(
VK_RValue, SourceLocation());
UnaryOperator SRC(&SrcExpr, UO_Deref, SrcTy->getPointeeType(),
- VK_LValue, OK_Ordinary, SourceLocation());
+ VK_LValue, OK_Ordinary, SourceLocation(), false);
CXXConstructExpr *CXXConstExpr =
cast<CXXConstructExpr>(PID->getGetterCXXConstructor());
diff --git a/lib/CodeGen/CGStmtOpenMP.cpp b/lib/CodeGen/CGStmtOpenMP.cpp
index 07c1e3aa51..7221ad95b6 100644
--- a/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/lib/CodeGen/CGStmtOpenMP.cpp
@@ -2483,7 +2483,7 @@ void CodeGenFunction::EmitSections(const OMPExecutableDirective &S) {
OK_Ordinary, S.getLocStart(), FPOptions());
// Increment for loop counter.
UnaryOperator Inc(&IVRefExpr, UO_PreInc, KmpInt32Ty, VK_RValue, OK_Ordinary,
- S.getLocStart());
+ S.getLocStart(), true);
auto BodyGen = [Stmt, CS, &S, &IV](CodeGenFunction &CGF) {
// Iterate through all sections and emit a switch construct:
// switch (IV) {
diff --git a/lib/Frontend/Rewrite/RewriteModernObjC.cpp b/lib/Frontend/Rewrite/RewriteModernObjC.cpp
index 1954b24aed..55f81c2860 100644
--- a/lib/Frontend/Rewrite/RewriteModernObjC.cpp
+++ b/lib/Frontend/Rewrite/RewriteModernObjC.cpp
@@ -2590,7 +2590,7 @@ Stmt *RewriteModernObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) {
Expr *Unop = new (Context) UnaryOperator(DRE, UO_AddrOf,
Context->getPointerType(DRE->getType()),
VK_RValue, OK_Ordinary,
- SourceLocation());
+ SourceLocation(), false);
// cast to NSConstantString *
CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(),
CK_CPointerToObjCPointerCast, Unop);
@@ -3295,7 +3295,7 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf,
Context->getPointerType(SuperRep->getType()),
VK_RValue, OK_Ordinary,
- SourceLocation());
+ SourceLocation(), false);
SuperRep = NoTypeInfoCStyleCastExpr(Context,
Context->getPointerType(superType),
CK_BitCast, SuperRep);
@@ -3313,7 +3313,7 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf,
Context->getPointerType(SuperRep->getType()),
VK_RValue, OK_Ordinary,
- SourceLocation());
+ SourceLocation(), false);
}
MsgExprs.push_back(SuperRep);
break;
@@ -3389,7 +3389,7 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf,
Context->getPointerType(SuperRep->getType()),
VK_RValue, OK_Ordinary,
- SourceLocation());
+ SourceLocation(), false);
SuperRep = NoTypeInfoCStyleCastExpr(Context,
Context->getPointerType(superType),
CK_BitCast, SuperRep);
@@ -4720,7 +4720,7 @@ Stmt *RewriteModernObjC::RewriteLocalVariableExternalStorage(DeclRefExpr *DRE) {
return DRE;
Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(),
VK_LValue, OK_Ordinary,
- DRE->getLocation());
+ DRE->getLocation(), false);
// Need parens to enforce precedence.
ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(),
Exp);
@@ -5314,7 +5314,7 @@ Stmt *RewriteModernObjC::SynthBlockInitExpr(BlockExpr *Exp,
UO_AddrOf,
Context->getPointerType(Context->VoidPtrTy),
VK_RValue, OK_Ordinary,
- SourceLocation());
+ SourceLocation(), false);
InitExprs.push_back(DescRefExpr);
// Add initializers for any closure decl refs.
@@ -5332,7 +5332,8 @@ Stmt *RewriteModernObjC::SynthBlockInitExpr(BlockExpr *Exp,
QualType QT = (*I)->getType();
QT = Context->getPointerType(QT);
Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue,
- OK_Ordinary, SourceLocation());
+ OK_Ordinary, SourceLocation(),
+ false);
}
} else if (isTopLevelBlockPointerType((*I)->getType())) {
FD = SynthBlockInitFunctionDecl((*I)->getName());
@@ -5348,7 +5349,8 @@ Stmt *RewriteModernObjC::SynthBlockInitExpr(BlockExpr *Exp,
QualType QT = (*I)->getType();
QT = Context->getPointerType(QT);
Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue,
- OK_Ordinary, SourceLocation());
+ OK_Ordinary, SourceLocation(),
+ false);
}
}
@@ -5388,7 +5390,8 @@ Stmt *RewriteModernObjC::SynthBlockInitExpr(BlockExpr *Exp,
if (!isNestedCapturedVar)
Exp = new (Context) UnaryOperator(Exp, UO_AddrOf,
Context->getPointerType(Exp->getType()),
- VK_RValue, OK_Ordinary, SourceLocation());
+ VK_RValue, OK_Ordinary, SourceLocation(),
+ false);
Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp);
InitExprs.push_back(Exp);
}
@@ -5414,7 +5417,7 @@ Stmt *RewriteModernObjC::SynthBlockInitExpr(BlockExpr *Exp,
NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf,
Context->getPointerType(NewRep->getType()),
- VK_RValue, OK_Ordinary, SourceLocation());
+ VK_RValue, OK_Ordinary, SourceLocation(), false);
NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast,
NewRep);
// Put Paren around the call.
@@ -7558,7 +7561,7 @@ Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) {
Expr *Exp = new (Context) UnaryOperator(castExpr, UO_Deref, IvarT,
VK_LValue, OK_Ordinary,
- SourceLocation());
+ SourceLocation(), false);
PE = new (Context) ParenExpr(OldRange.getBegin(),
OldRange.getEnd(),
Exp);
diff --git a/lib/Frontend/Rewrite/RewriteObjC.cpp b/lib/Frontend/Rewrite/RewriteObjC.cpp
index 096b81bc3f..37398e5de0 100644
--- a/lib/Frontend/Rewrite/RewriteObjC.cpp
+++ b/lib/Frontend/Rewrite/RewriteObjC.cpp
@@ -2511,7 +2511,7 @@ Stmt *RewriteObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) {
Expr *Unop = new (Context) UnaryOperator(DRE, UO_AddrOf,
Context->getPointerType(DRE->getType()),
VK_RValue, OK_Ordinary,
- SourceLocation());
+ SourceLocation(), false);
// cast to NSConstantString *
CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(),
CK_CPointerToObjCPointerCast, Unop);
@@ -2712,7 +2712,7 @@ Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf,
Context->getPointerType(SuperRep->getType()),
VK_RValue, OK_Ordinary,
- SourceLocation());
+ SourceLocation(), false);
SuperRep = NoTypeInfoCStyleCastExpr(Context,
Context->getPointerType(superType),
CK_BitCast, SuperRep);
@@ -2730,7 +2730,7 @@ Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf,
Context->getPointerType(SuperRep->getType()),
VK_RValue, OK_Ordinary,
- SourceLocation());
+ SourceLocation(), false);
}
MsgExprs.push_back(SuperRep);
break;
@@ -2806,7 +2806,7 @@ Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp,
SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf,
Context->getPointerType(SuperRep->getType()),
VK_RValue, OK_Ordinary,
- SourceLocation());
+ SourceLocation(), false);
SuperRep = NoTypeInfoCStyleCastExpr(Context,
Context->getPointerType(superType),
CK_BitCast, SuperRep);
@@ -3045,7 +3045,7 @@ Stmt *RewriteObjC::RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp) {
VK_LValue, SourceLocation());
Expr *DerefExpr = new (Context) UnaryOperator(DRE, UO_AddrOf,
Context->getPointerType(DRE->getType()),
- VK_RValue, OK_Ordinary, SourceLocation());
+ VK_RValue, OK_Ordinary, SourceLocation(), false);
CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, DerefExpr->getType(),
CK_BitCast,
DerefExpr);
@@ -3875,7 +3875,7 @@ Stmt *RewriteObjC::RewriteLocalVariableExternalStorage(DeclRefExpr *DRE) {
return DRE;
Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(),
VK_LValue, OK_Ordinary,
- DRE->getLocation());
+ DRE->getLocation(), false);
// Need parens to enforce precedence.
ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(),
Exp);
@@ -4438,7 +4438,7 @@ Stmt *RewriteObjC::SynthBlockInitExpr(BlockExpr *Exp,
UO_AddrOf,
Context->getPointerType(Context->VoidPtrTy),
VK_RValue, OK_Ordinary,
- SourceLocation());
+ SourceLocation(), false);
InitExprs.push_back(DescRefExpr);
// Add initializers for any closure decl refs.
@@ -4456,7 +4456,8 @@ Stmt *RewriteObjC::SynthBlockInitExpr(BlockExpr *Exp,
QualType QT = (*I)->getType();
QT = Context->getPointerType(QT);
Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue,
- OK_Ordinary, SourceLocation());
+ OK_Ordinary, SourceLocation(),
+ false);
}
} else if (isTopLevelBlockPointerType((*I)->getType())) {
FD = SynthBlockInitFunctionDecl((*I)->getName());
@@ -4472,7 +4473,8 @@ Stmt *RewriteObjC::SynthBlockInitExpr(BlockExpr *Exp,
QualType QT = (*I)->getType();
QT = Context->getPointerType(QT);
Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue,
- OK_Ordinary, SourceLocation());
+ OK_Ordinary, SourceLocation(),
+ false);
}
}
InitExprs.push_back(Exp);
@@ -4509,9 +4511,9 @@ Stmt *RewriteObjC::SynthBlockInitExpr(BlockExpr *Exp,
// captured nested byref variable has its address passed. Do not take
// its address again.
if (!isNestedCapturedVar)
- Exp = new (Context) UnaryOperator(Exp, UO_AddrOf,
- Context->getPointerType(Exp->getType()),
- VK_RValue, OK_Ordinary, SourceLocation());
+ Exp = new (Context) UnaryOperator(
+ Exp, UO_AddrOf, Context->getPointerType(Exp->getType()), VK_RValue,
+ OK_Ordinary, SourceLocation(), false);
Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp);
InitExprs.push_back(Exp);
}
@@ -4529,7 +4531,7 @@ Stmt *RewriteObjC::SynthBlockInitExpr(BlockExpr *Exp,
FType, VK_LValue, SourceLocation());
NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf,
Context->getPointerType(NewRep->getType()),
- VK_RValue, OK_Ordinary, SourceLocation());
+ VK_RValue, OK_Ordinary, SourceLocation(), false);
NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast,
NewRep);
BlockDeclRefs.clear();
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 5e24649c71..f0a176b14c 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -10990,11 +10990,11 @@ buildMemcpyForAssignmentOp(Sema &S, SourceLocation Loc, QualType T,
Expr *From = FromB.build(S, Loc);
From = new (S.Context) UnaryOperator(From, UO_AddrOf,
S.Context.getPointerType(From->getType()),
- VK_RValue, OK_Ordinary, Loc);
+ VK_RValue, OK_Ordinary, Loc, false);
Expr *To = ToB.build(S, Loc);
To = new (S.Context) UnaryOperator(To, UO_AddrOf,
S.Context.getPointerType(To->getType()),
- VK_RValue, OK_Ordinary, Loc);
+ VK_RValue, OK_Ordinary, Loc, false);
const Type *E = T->getBaseElementTypeUnsafe();
bool NeedsCollectableMemCpy =
@@ -11230,16 +11230,18 @@ buildSingleCopyAssignRecursively(Sema &S, SourceLocation Loc, QualType T,
Expr *Comparison
= new (S.Context) BinaryOperator(IterationVarRefRVal.build(S, Loc),
IntegerLiteral::Create(S.Context, Upper, SizeType, Loc),
- BO_NE, S.Context.BoolTy,
- VK_RValue, OK_Ordinary, Loc, FPOptions());
-
- // Create the pre-increment of the iteration variable.
- Expr *Increment
- = new (S.Context) UnaryOperator(IterationVarRef.build(S, Loc), UO_PreInc,
- SizeType, VK_LValue, OK_Ordinary, Loc);
-
- // Construct the loop that copies all elements of this array.
- return S.ActOnForStmt(
+ BO_NE, S.Context.BoolTy,
+ VK_RValue, OK_Ordinary, Loc, FPOptions());
+
+ // Create the pre-increment of the iteration variable. We can determine
+ // whether the increment will overflow based on the value of the array
+ // bound.
+ Expr *Increment = new (S.Context)
+ UnaryOperator(IterationVarRef.build(S, Loc), UO_PreInc, SizeType,
+ VK_LValue, OK_Ordinary, Loc, Upper.isMaxValue());
+
+ // Construct the loop that copies all elements of this array.
+ return S.ActOnForStmt(
Loc, Loc, InitStmt,
S.ActOnCondition(nullptr, Loc, Comparison, Sema::ConditionKind::Boolean),
S.MakeFullDiscardedValueExpr(Increment), Loc, Copy.get());
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index a6f3ee2c09..1d7a5d2e12 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -12178,6 +12178,16 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc,
return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr);
}
+static bool isOverflowingIntegerType(ASTContext &Ctx, QualType T) {
+ if (T.isNull() || T->isDependentType())
+ return false;
+
+ if (!T->isPromotableIntegerType())
+ return true;
+
+ return Ctx.getIntWidth(T) >= Ctx.getIntWidth(Ctx.IntTy);
+}
+
ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
UnaryOperatorKind Opc,
Expr *InputExpr) {
@@ -12185,6 +12195,8 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
ExprValueKind VK = VK_RValue;
ExprObjectKind OK = OK_Ordinary;
QualType resultType;
+ bool CanOverflow = false;
+
bool ConvertHalfVec = false;
if (getLangOpts().OpenCL) {
QualType Ty = InputExpr->getType();
@@ -12210,6 +12222,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
Opc == UO_PostInc,
Opc == UO_PreInc ||
Opc == UO_PreDec);
+ CanOverflow = isOverflowingIntegerType(Context, resultType);
break;
case UO_AddrOf:
resultType = CheckAddressOfOperand(Input, OpLoc);
@@ -12223,6 +12236,8 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
}
case UO_Plus:
case UO_Minus:
+ CanOverflow = Opc == UO_Minus &&
+ isOverflowingIntegerType(Context, Input.get()->getType());
Input = UsualUnaryConversions(Input.get());
if (Input.isInvalid()) return ExprError();
// Unary plus and minus require promoting an operand of half vector to a
@@ -12259,6 +12274,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
if (Input.isInvalid())
return ExprError();
resultType = Input.get()->getType();
+
if (resultType->isDependentType())
break;
// C99 6.5.3.3p1. We allow complex int and float as a GCC extension.
@@ -12373,7 +12389,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
CheckArrayAccess(Input.get());
auto *UO = new (Context)
- UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc);
+ UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow);
// Convert the result back to a half vector.
if (ConvertHalfVec)
return convertVector(UO, Context.HalfTy, *this);
diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp
index cd0c2c47ae..6a725c485d 100644
--- a/lib/Sema/SemaExprObjC.cpp
+++ b/lib/Sema/SemaExprObjC.cpp
@@ -4276,9 +4276,9 @@ Expr *Sema::stripARCUnbridgedCast(Expr *e) {
} else if (UnaryOperator *uo = dyn_cast<UnaryOperator>(e)) {
assert(uo->getOpcode() == UO_Extension);
Expr *sub = stripARCUnbridgedCast(uo->getSubExpr());
- return new (Context) UnaryOperator(sub, UO_Extension, sub->getType(),
- sub->getValueKind(), sub->getObjectKind(),
- uo->getOperatorLoc());
+ return new (Context)
+ UnaryOperator(sub, UO_Extension, sub->getType(), sub->getValueKind(),
+ sub->getObjectKind(), uo->getOperatorLoc(), false);
} else if (GenericSelectionExpr *gse = dyn_cast<GenericSelectionExpr>(e)) {
assert(!gse->isResultDependent());
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index b760cdcd34..e7eb309c40 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -12022,7 +12022,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
if (Input->isTypeDependent()) {
if (Fns.empty())
return new (Context) UnaryOperator(Input, Opc, Context.DependentTy,
- VK_RValue, OK_Ordinary, OpLoc);
+ VK_RValue, OK_Ordinary, OpLoc, false);
CXXRecordDecl *NamingClass = nullptr; // lookup ignores member operators
UnresolvedLookupExpr *Fn
@@ -13552,7 +13552,7 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
return new (Context) UnaryOperator(SubExpr, UO_AddrOf, MemPtrType,
VK_RValue, OK_Ordinary,
- UnOp->getOperatorLoc());
+ UnOp->getOperatorLoc(), false);
}
}
Expr *SubExpr = FixOverloadedFunctionReference(UnOp->getSubExpr(),
@@ -13563,7 +13563,7 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
return new (Context) UnaryOperator(SubExpr, UO_AddrOf,
Context.getPointerType(SubExpr->getType()),
VK_RValue, OK_Ordinary,
- UnOp->getOperatorLoc());
+ UnOp->getOperatorLoc(), false);
}
// C++ [except.spec]p17:
diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp
index 58980be64a..f09a6f7158 100644
--- a/lib/Sema/SemaPseudoObject.cpp
+++ b/lib/Sema/SemaPseudoObject.cpp
@@ -132,7 +132,8 @@ namespace {
uop->getType(),
uop->getValueKind(),
uop->getObjectKind(),
- uop->getOperatorLoc());
+ uop->getOperatorLoc(),
+ uop->canOverflow());
}
if (GenericSelectionExpr *gse = dyn_cast<GenericSelectionExpr>(e)) {
@@ -524,15 +525,18 @@ PseudoOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc,
addSemanticExpr(result.get());
if (UnaryOperator::isPrefix(opcode) && !captureSetValueAsResult() &&
!result.get()->getType()->isVoidType() &&
- (result.get()->isTypeDependent() || CanCaptureValue(result.get())))
- setResultToLastSemantic();
-
- UnaryOperator *syntactic =
- new (S.Context) UnaryOperator(syntacticOp, opcode, resultType,
- VK_LValue, OK_Ordinary, opcLoc);
- return complete(syntactic);
-}
-
+ (result.get()->isTypeDependent() || CanCaptureValue(result.get())))
+ setResultToLastSemantic();
+
+ UnaryOperator *syntactic = new (S.Context) UnaryOperator(
+ syntacticOp, opcode, resultType, VK_LValue, OK_Ordinary, opcLoc,
+ !resultType->isDependentType()
+ ? S.Context.getTypeSize(resultType) >=
+ S.Context.getTypeSize(S.Context.IntTy)
+ : false);
+ return complete(syntactic);
+}
+
//===----------------------------------------------------------------------===//
// Objective-C @property and implicit property references
@@ -1550,7 +1554,7 @@ ExprResult Sema::checkPseudoObjectIncDec(Scope *Sc, SourceLocation opcLoc,
// Do nothing if the operand is dependent.
if (op->isTypeDependent())
return new (Context) UnaryOperator(op, opcode, Context.DependentTy,
- VK_RValue, OK_Ordinary, opcLoc);
+ VK_RValue, OK_Ordinary, opcLoc, false);
assert(UnaryOperator::isIncrementDecrementOp(opcode));
Expr *opaqueRef = op->IgnoreParens();
@@ -1630,15 +1634,15 @@ static Expr *stripOpaqueValuesFromPseudoObjectRef(Sema &S, Expr *E) {
/// capable of rebuilding a tree without stripping implicit
/// operations.
Expr *Sema::recreateSyntacticForm(PseudoObjectExpr *E) {
- Expr *syntax = E->getSyntacticForm();
- if (UnaryOperator *uop = dyn_cast<UnaryOperator>(syntax)) {
- Expr *op = stripOpaqueValuesFromPseudoObjectRef(*this, uop->getSubExpr());
- return new (Context) UnaryOperator(op, uop->getOpcode(), uop->getType(),
- uop->getValueKind(), uop->getObjectKind(),
- uop->getOperatorLoc());
- } else if (CompoundAssignOperator *cop
- = dyn_cast<CompoundAssignOperator>(syntax)) {
- Expr *lhs = stripOpaqueValuesFromPseudoObjectRef(*this, cop->getLHS());
+ Expr *syntax = E->getSyntacticForm();
+ if (UnaryOperator *uop = dyn_cast<UnaryOperator>(syntax)) {
+ Expr *op = stripOpaqueValuesFromPseudoObjectRef(*this, uop->getSubExpr());
+ return new (Context) UnaryOperator(
+ op, uop->getOpcode(), uop->getType(), uop->getValueKind(),
+ uop->getObjectKind(), uop->getOperatorLoc(), uop->canOverflow());
+ } else if (CompoundAssignOperator *cop
+ = dyn_cast<CompoundAssignOperator>(syntax)) {
+ Expr *lhs = stripOpaqueValuesFromPseudoObjectRef(*this, cop->getLHS());
Expr *rhs = cast<OpaqueValueExpr>(cop->getRHS())->getSourceExpr();
return new (Context) CompoundAssignOperator(lhs, rhs, cop->getOpcode(),
cop->getType(),
diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp
index 6163b811c7..4938b0e902 100644
--- a/lib/Serialization/ASTReaderStmt.cpp
+++ b/lib/Serialization/ASTReaderStmt.cpp
@@ -550,12 +550,13 @@ void ASTStmtReader::VisitParenListExpr(ParenListExpr *E) {
void ASTStmtReader::VisitUnaryOperator(UnaryOperator *E) {
VisitExpr(E);
- E->setSubExpr(Record.readSubExpr());
- E->setOpcode((UnaryOperator::Opcode)Record.readInt());
- E->setOperatorLoc(ReadSourceLocation());
-}
-
-void ASTStmtReader::VisitOffsetOfExpr(OffsetOfExpr *E) {
+ E->setSubExpr(Record.readSubExpr());
+ E->setOpcode((UnaryOperator::Opcode)Record.readInt());
+ E->setOperatorLoc(ReadSourceLocation());
+ E->setCanOverflow(Record.readInt());
+}
+
+void ASTStmtReader::VisitOffsetOfExpr(OffsetOfExpr *E) {
VisitExpr(E);
assert(E->getNumComponents() == Record.peekInt());
Record.skipInts(1);
diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp
index c5f4495d2f..6fd03725d4 100644
--- a/lib/Serialization/ASTWriterStmt.cpp
+++ b/lib/Serialization/ASTWriterStmt.cpp
@@ -506,12 +506,13 @@ void ASTStmtWriter::VisitParenListExpr(ParenListExpr *E) {
void ASTStmtWriter::VisitUnaryOperator(UnaryOperator *E) {
VisitExpr(E);
- Record.AddStmt(E->getSubExpr());
- Record.push_back(E->getOpcode()); // FIXME: stable encoding
- Record.AddSourceLocation(E->getOperatorLoc());
- Code = serialization::EXPR_UNARY_OPERATOR;
-}
-
+ Record.AddStmt(E->getSubExpr());
+ Record.push_back(E->getOpcode()); // FIXME: stable encoding
+ Record.AddSourceLocation(E->getOperatorLoc());
+ Record.push_back(E->canOverflow());
+ Code = serialization::EXPR_UNARY_OPERATOR;
+}
+
void ASTStmtWriter::VisitOffsetOfExpr(OffsetOfExpr *E) {
VisitExpr(E);
Record.push_back(E->getNumComponents());
diff --git a/test/Misc/ast-dump-stmt.c b/test/Misc/ast-dump-stmt.c
index 1f21cf03f6..461893188a 100644
--- a/test/Misc/ast-dump-stmt.c
+++ b/test/Misc/ast-dump-stmt.c
@@ -30,6 +30,38 @@ int TestOpaqueValueExpr = 0 ?: 1;
// CHECK-NEXT: IntegerLiteral
// CHECK-NEXT: OpaqueValueExpr
// CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: OpaqueValueExpr
-// CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: IntegerLiteral
+// CHECK-NEXT: OpaqueValueExpr
+// CHECK-NEXT: IntegerLiteral
+// CHECK-NEXT: IntegerLiteral
+
+void TestUnaryOperatorExpr(void) {
+ char T1 = 1;
+ int T2 = 1;
+
+ T1++;
+ T2++;
+ // CHECK: UnaryOperator{{.*}}postfix '++' cannot overflow
+ // CHECK-NEXT: DeclRefExpr{{.*}}'T1' 'char'
+ // CHECK-NOT: UnaryOperator{{.*}}postfix '++' cannot overflow
+ // CHECK: DeclRefExpr{{.*}}'T2' 'int'
+
+ -T1;
+ -T2;
+ // CHECK: UnaryOperator{{.*}}prefix '-' cannot overflow
+ // CHECK-NEXT: ImplicitCastExpr
+ // CHECK-NEXT: ImplicitCastExpr
+ // CHECK-NEXT: DeclRefExpr{{.*}}'T1' 'char'
+ // CHECK-NOT: UnaryOperator{{.*}}prefix '-' cannot overflow
+ // CHECK: ImplicitCastExpr
+ // CHECK: DeclRefExpr{{.*}}'T2' 'int'
+
+ ~T1;
+ ~T2;
+ // CHECK: UnaryOperator{{.*}}prefix '~' cannot overflow
+ // CHECK-NEXT: ImplicitCastExpr
+ // CHECK-NEXT: ImplicitCastExpr
+ // CHECK-NEXT: DeclRefExpr{{.*}}'T1' 'char'
+ // CHECK: UnaryOperator{{.*}}prefix '~' cannot overflow
+ // CHECK-NEXT: ImplicitCastExpr
+ // CHECK-NEXT: DeclRefExpr{{.*}}'T2' 'int'
+}