/// HasSideEffects - This routine returns true for all those expressions
/// which have any effect other than producing a value. Example is a function
- /// call, volatile variable read, or throwing an exception.
- bool HasSideEffects(const ASTContext &Ctx) const;
+ /// call, volatile variable read, or throwing an exception. If
+ /// IncludePossibleEffects is false, this call treats certain expressions with
+ /// potential side effects (such as function call-like expressions,
+ /// instantiation-dependent expressions, or invocations from a macro) as not
+ /// having side effects.
+ bool HasSideEffects(const ASTContext &Ctx,
+ bool IncludePossibleEffects = true) const;
/// \brief Determine whether this expression involves a call to any function
/// that is not trivial.
def warn_unused_call : Warning<
"ignoring return value of function declared with %0 attribute">,
InGroup<UnusedValue>;
+def warn_side_effects_unevaluated_context : Warning<
+ "expression with side effects has no effect in an unevaluated context">,
+ InGroup<UnusedValue>;
+def warn_side_effects_typeid : Warning<
+ "expression with side effects will be evaluated despite being used as an "
+ "operand to 'typeid'">, InGroup<UnusedValue>;
def warn_unused_result : Warning<
"ignoring return value of function declared with warn_unused_result "
"attribute">, InGroup<DiagGroup<"unused-result">>;
const CXXScopeSpec &SS, QualType T);
QualType BuildTypeofExprType(Expr *E, SourceLocation Loc);
- QualType BuildDecltypeType(Expr *E, SourceLocation Loc);
+ /// If AsUnevaluated is false, E is treated as though it were an evaluated
+ /// context, such as when building a type for decltype(auto).
+ QualType BuildDecltypeType(Expr *E, SourceLocation Loc,
+ bool AsUnevaluated = true);
QualType BuildUnaryTransformType(QualType BaseType,
UnaryTransformType::UTTKind UKind,
SourceLocation Loc);
return false;
}
-bool Expr::HasSideEffects(const ASTContext &Ctx) const {
+bool Expr::HasSideEffects(const ASTContext &Ctx,
+ bool IncludePossibleEffects) const {
+ // In circumstances where we care about definite side effects instead of
+ // potential side effects, we want to ignore expressions that are part of a
+ // macro expansion as a potential side effect.
+ if (!IncludePossibleEffects && getExprLoc().isMacroID())
+ return false;
+
if (isInstantiationDependent())
- return true;
+ return IncludePossibleEffects;
switch (getStmtClass()) {
case NoStmtClass:
return false;
case CallExprClass:
+ case CXXOperatorCallExprClass:
+ case CXXMemberCallExprClass:
+ case CUDAKernelCallExprClass:
+ case BlockExprClass:
+ case CXXBindTemporaryExprClass:
+ case UserDefinedLiteralClass:
+ // We don't know a call definitely has side effects, but we can check the
+ // call's operands.
+ if (!IncludePossibleEffects)
+ break;
+ return true;
+
case MSPropertyRefExprClass:
case CompoundAssignOperatorClass:
case VAArgExprClass:
case AtomicExprClass:
case StmtExprClass:
- case CXXOperatorCallExprClass:
- case CXXMemberCallExprClass:
- case UserDefinedLiteralClass:
case CXXThrowExprClass:
case CXXNewExprClass:
case CXXDeleteExprClass:
case ExprWithCleanupsClass:
- case CXXBindTemporaryExprClass:
- case BlockExprClass:
- case CUDAKernelCallExprClass:
// These always have a side-effect.
return true;
case InitListExprClass:
// FIXME: The children for an InitListExpr doesn't include the array filler.
if (const Expr *E = cast<InitListExpr>(this)->getArrayFiller())
- if (E->HasSideEffects(Ctx))
+ if (E->HasSideEffects(Ctx, IncludePossibleEffects))
return true;
break;
case GenericSelectionExprClass:
return cast<GenericSelectionExpr>(this)->getResultExpr()->
- HasSideEffects(Ctx);
+ HasSideEffects(Ctx, IncludePossibleEffects);
case ChooseExprClass:
- return cast<ChooseExpr>(this)->getChosenSubExpr()->HasSideEffects(Ctx);
+ return cast<ChooseExpr>(this)->getChosenSubExpr()->HasSideEffects(
+ Ctx, IncludePossibleEffects);
case CXXDefaultArgExprClass:
- return cast<CXXDefaultArgExpr>(this)->getExpr()->HasSideEffects(Ctx);
+ return cast<CXXDefaultArgExpr>(this)->getExpr()->HasSideEffects(
+ Ctx, IncludePossibleEffects);
case CXXDefaultInitExprClass: {
const FieldDecl *FD = cast<CXXDefaultInitExpr>(this)->getField();
if (const Expr *E = FD->getInClassInitializer())
- return E->HasSideEffects(Ctx);
+ return E->HasSideEffects(Ctx, IncludePossibleEffects);
// If we've not yet parsed the initializer, assume it has side-effects.
return true;
}
case CXXConstructExprClass:
case CXXTemporaryObjectExprClass: {
const CXXConstructExpr *CE = cast<CXXConstructExpr>(this);
- if (!CE->getConstructor()->isTrivial())
+ if (!CE->getConstructor()->isTrivial() && IncludePossibleEffects)
return true;
// A trivial constructor does not add any side-effects of its own. Just look
// at its arguments.
const Expr *Subexpr = *I;
if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Subexpr))
Subexpr = OVE->getSourceExpr();
- if (Subexpr->HasSideEffects(Ctx))
+ if (Subexpr->HasSideEffects(Ctx, IncludePossibleEffects))
return true;
}
return false;
case ObjCBoxedExprClass:
case ObjCArrayLiteralClass:
case ObjCDictionaryLiteralClass:
- case ObjCMessageExprClass:
case ObjCSelectorExprClass:
case ObjCProtocolExprClass:
- case ObjCPropertyRefExprClass:
case ObjCIsaExprClass:
case ObjCIndirectCopyRestoreExprClass:
case ObjCSubscriptRefExprClass:
case ObjCBridgedCastExprClass:
- // FIXME: Classify these cases better.
- return true;
+ case ObjCMessageExprClass:
+ case ObjCPropertyRefExprClass:
+ // FIXME: Classify these cases better.
+ if (IncludePossibleEffects)
+ return true;
+ break;
}
// Recurse to children.
for (const_child_range SubStmts = children(); SubStmts; ++SubStmts)
if (const Stmt *S = *SubStmts)
- if (cast<Expr>(S)->HasSideEffects(Ctx))
+ if (cast<Expr>(S)->HasSideEffects(Ctx, IncludePossibleEffects))
return true;
return false;
ControllingExpr = result.get();
}
+ // The controlling expression is an unevaluated operand, so side effects are
+ // likely unintended.
+ if (ActiveTemplateInstantiations.empty() &&
+ ControllingExpr->HasSideEffects(Context, false))
+ Diag(ControllingExpr->getExprLoc(),
+ diag::warn_side_effects_unevaluated_context);
+
bool TypeErrorFound = false,
IsResultDependent = ControllingExpr->isTypeDependent(),
ContainsUnexpandedParameterPack
return true;
}
+ // The operand for sizeof and alignof is in an unevaluated expression context,
+ // so side effects could result in unintended consequences.
+ if ((ExprKind == UETT_SizeOf || ExprKind == UETT_AlignOf) &&
+ ActiveTemplateInstantiations.empty() && E->HasSideEffects(Context, false))
+ Diag(E->getExprLoc(), diag::warn_side_effects_unevaluated_context);
+
if (CheckObjCTraitOperandConstraints(*this, ExprTy, E->getExprLoc(),
E->getSourceRange(), ExprKind))
return true;
SourceLocation TypeidLoc,
Expr *E,
SourceLocation RParenLoc) {
+ bool WasEvaluated = false;
if (E && !E->isTypeDependent()) {
if (E->getType()->isPlaceholderType()) {
ExprResult result = CheckPlaceholderExpr(E);
// We require a vtable to query the type at run time.
MarkVTableUsed(TypeidLoc, RecordD);
+ WasEvaluated = true;
}
}
if (E->getType()->isVariablyModifiedType())
return ExprError(Diag(TypeidLoc, diag::err_variably_modified_typeid)
<< E->getType());
+ else if (ActiveTemplateInstantiations.empty() &&
+ E->HasSideEffects(Context, WasEvaluated)) {
+ // The expression operand for typeid is in an unevaluated expression
+ // context, so side effects could result in unintended consequences.
+ Diag(E->getExprLoc(), WasEvaluated
+ ? diag::warn_side_effects_typeid
+ : diag::warn_side_effects_unevaluated_context);
+ }
return new (Context) CXXTypeidExpr(TypeInfoType.withConst(), E,
SourceRange(TypeidLoc, RParenLoc));
if (CheckArrow(*this, ObjectType, Base, OpKind, OpLoc))
return ExprError();
- QualType T = BuildDecltypeType(DS.getRepAsExpr(), DS.getTypeSpecTypeLoc());
+ QualType T = BuildDecltypeType(DS.getRepAsExpr(), DS.getTypeSpecTypeLoc(),
+ false);
TypeLocBuilder TLB;
DecltypeTypeLoc DecltypeTL = TLB.push<DecltypeTypeLoc>(T);
ExprResult Sema::BuildCXXNoexceptExpr(SourceLocation KeyLoc, Expr *Operand,
SourceLocation RParen) {
+ if (ActiveTemplateInstantiations.empty() &&
+ Operand->HasSideEffects(Context, false)) {
+ // The expression operand for noexcept is in an unevaluated expression
+ // context, so side effects could result in unintended consequences.
+ Diag(Operand->getExprLoc(), diag::warn_side_effects_unevaluated_context);
+ }
+
CanThrowResult CanThrow = canThrow(Operand);
return new (Context)
CXXNoexceptExpr(Context.BoolTy, Operand, CanThrow, KeyLoc, RParen);
return DAR_FailedAlreadyDiagnosed;
}
- QualType Deduced = BuildDecltypeType(Init, Init->getLocStart());
+ QualType Deduced = BuildDecltypeType(Init, Init->getLocStart(), false);
// FIXME: Support a non-canonical deduced type for 'auto'.
Deduced = Context.getCanonicalType(Deduced);
Result = SubstituteAutoTransform(*this, Deduced).Apply(Type);
return T;
}
-QualType Sema::BuildDecltypeType(Expr *E, SourceLocation Loc) {
+QualType Sema::BuildDecltypeType(Expr *E, SourceLocation Loc,
+ bool AsUnevaluated) {
ExprResult ER = CheckPlaceholderExpr(E);
if (ER.isInvalid()) return QualType();
E = ER.get();
+ if (AsUnevaluated && ActiveTemplateInstantiations.empty() &&
+ E->HasSideEffects(Context, false)) {
+ // The expression operand for decltype is in an unevaluated expression
+ // context, so side effects could result in unintended consequences.
+ Diag(E->getExprLoc(), diag::warn_side_effects_unevaluated_context);
+ }
+
return Context.getDecltypeType(E, getDecltypeForExpr(*this, E));
}
// FIXME: this should only emit one error.
int i2 = sizeof([](auto a, auto b)->void{}(3, '4')); // expected-error{{lambda expression in an unevaluated operand}} \
// expected-error{{invalid application of 'sizeof'}}
- const std::type_info &ti1 = typeid([](auto &a) -> P& { static P p; return p; }(i));
+ const std::type_info &ti1 = typeid([](auto &a) -> P& { static P p; return p; }(i)); // expected-warning {{expression with side effects will be evaluated despite being used as an operand to 'typeid'}}
const std::type_info &ti2 = typeid([](auto) -> int { return i; }(i)); // expected-error{{lambda expression in an unevaluated operand}}\
// expected-error{{cannot be implicitly captured}}\
// expected-note{{begins here}}
-// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 -Wno-unused-value %s -verify
// prvalue
void prvalue() {
-// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11 -fms-extensions -Wno-delete-incomplete %s
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11 -fms-extensions -Wno-delete-incomplete -Wno-unused-value %s
// expected-no-diagnostics
#define P(e) static_assert(noexcept(e), "expected nothrow")
-// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c11
+// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c11 -Wno-unused-value
+
enum e0; // expected-note{{forward declaration of 'enum e0'}}
struct a {
// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c99 -Wno-sizeof-array-decay
-// expected-no-diagnostics
// rdar://6095180
struct s { char c[17]; };
// comma does array/function promotion in c99.
int X[sizeof(0, (foo().c)) == sizeof(char*) ? 1 : -1];
int Y[sizeof(0, (a,b).c) == sizeof(char*) ? 1 : -1];
-int Z[sizeof(0, (a=b).c) == sizeof(char*) ? 1 : -1];
+int Z[sizeof(0, (a=b).c) == sizeof(char*) ? 1 : -1]; // expected-warning {{expression with side effects has no effect in an unevaluated context}}
// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c89 -Wno-sizeof-array-decay
-// expected-no-diagnostics
// rdar://6095180
struct s { char c[17]; };
int W[sizeof(0, a.c) == sizeof(char*) ? 1 : -1];
int X[sizeof(0, (foo().c)) == 17 ? 1 : -1];
int Y[sizeof(0, (a,b).c) == 17 ? 1 : -1];
-int Z[sizeof(0, (a=b).c) == 17 ? 1 : -1];
+int Z[sizeof(0, (a=b).c) == 17 ? 1 : -1]; // expected-warning {{expression with side effects has no effect in an unevaluated context}}
-// RUN: %clang_cc1 -fsyntax-only -verify -Wunused-value -Wunused-label %s
-// RUN: %clang_cc1 -fsyntax-only -verify -Wunused %s
-// RUN: %clang_cc1 -fsyntax-only -verify -Wall %s
+// RUN: %clang_cc1 -std=c11 -fsyntax-only -verify -Wunused-value -Wunused-label %s
+// RUN: %clang_cc1 -std=c11 -fsyntax-only -verify -Wunused %s
+// RUN: %clang_cc1 -std=c11 -fsyntax-only -verify -Wall %s
int i = 0;
int j = 0;
// rdar://8139785
f0((int)(a->f0 + 1, 10)); // expected-warning {{expression result unused}}
}
+
+void blah(int a);
+#define GenTest(x) _Generic(x, default : blah)(x)
+
+void unevaluated_operands(void) {
+ int val = 0;
+
+ (void)sizeof(++val); // expected-warning {{expression with side effects has no effect in an unevaluated context}}
+ (void)_Generic(val++, default : 0); // expected-warning {{expression with side effects has no effect in an unevaluated context}}
+ (void)_Alignof(val++); // expected-warning {{expression with side effects has no effect in an unevaluated context}} expected-warning {{'_Alignof' applied to an expression is a GNU extension}}
+
+ // VLAs can have side effects so long as it's part of the type and not
+ // an expression.
+ (void)sizeof(int[++val]); // Ok
+ (void)_Alignof(int[++val]); // Ok
+
+ // Side effects as part of macro expansion are ok.
+ GenTest(val++);
+}
A &g();
constexpr auto &x = typeid(f());
constexpr auto &y = typeid(g()); // expected-error{{constant expression}} \
- // expected-note{{typeid applied to expression of polymorphic type 'TypeId::A' is not allowed in a constant expression}}
+ // expected-note{{typeid applied to expression of polymorphic type 'TypeId::A' is not allowed in a constant expression}} \
+ // expected-warning {{expression with side effects will be evaluated despite being used as an operand to 'typeid'}}
}
namespace PR14203 {
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused-value %s
// Make sure diagnostics that we don't print based on runtime control
// flow are delayed correctly in cases where we can't immediately tell whether
// Check that the checks work with unevaluated contexts
(void)sizeof(p(A::used1));
- (void)typeid(p(A::used1)); // xpected-note {{used here}}
+ (void)typeid(p(A::used1)); // expected-warning {{expression with side effects will be evaluated despite being used as an operand to 'typeid'}} xpected-note {{used here}}
// Misc other testing
a(A::unused, 1 ? A::used2 : A::used2); // xpected-note {{used here}}
int eat_base(...);
void test_typeid(Base &base) {
- (void)typeid(get_base(base)); // expected-warning{{cannot pass object of non-POD type 'Base' through variadic function; call will abort at runtime}}
+ (void)typeid(get_base(base)); // expected-warning{{cannot pass object of non-POD type 'Base' through variadic function; call will abort at runtime}} expected-warning{{expression with side effects will be evaluated despite being used as an operand to 'typeid'}}
(void)typeid(eat_base(base)); // okay
}
D d;
C c;
(void)typeid(f(), c); // Should not warn.
- (void)typeid(f(), d); // expected-warning {{ignoring return value}}
+ (void)typeid(f(), d); // expected-warning {{ignoring return value}} expected-warning {{expression with side effects will be evaluated despite being used as an operand to 'typeid'}}
// The sizeof expression operand is never evaluated.
(void)sizeof(f(), c); // Should not warn.
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -Wunused-value %s
-// expected-no-diagnostics
void f() __attribute__((const));
void g() {
foo(1);
}
+
+void h() {
+ int i = 0;
+ (void)noexcept(++i); // expected-warning {{expression with side effects has no effect in an unevaluated context}}
+ decltype(i++) j = 0; // expected-warning {{expression with side effects has no effect in an unevaluated context}}
}
+
+struct S {
+ S operator++(int);
+ S(int i);
+ S();
+
+ int& f();
+ S g();
+};
+
+void j() {
+ S s;
+ int i = 0;
+ (void)noexcept(s++); // Ok
+ (void)noexcept(i++); // expected-warning {{expression with side effects has no effect in an unevaluated context}}
+ (void)noexcept(i = 5); // expected-warning {{expression with side effects has no effect in an unevaluated context}}
+ (void)noexcept(s = 5); // Ok
+
+ (void)sizeof(s.f()); // Ok
+ (void)sizeof(s.f() = 5); // expected-warning {{expression with side effects has no effect in an unevaluated context}}
+ (void)noexcept(s.g() = 5); // Ok
+}
+
+}
\ No newline at end of file
void method() const {
X* x;
&x[0]; // expected-warning {{expression result unused}}
- }
+ }
};
typedef basic_string<char> string;
- void func(const std::string& str) {
+ void func(const std::string& str) {
str.method(); // expected-note {{in instantiation of member function}}
}
- }
+ }
}
}
Unused(1, 1); // expected-warning {{expression result unused}}
}
}
+
+namespace std {
+ struct type_info {};
+}
+
+namespace test4 {
+struct Good { Good &f(); };
+struct Bad { virtual Bad& f(); };
+
+void f() {
+ int i = 0;
+ (void)typeid(++i); // expected-warning {{expression with side effects has no effect in an unevaluated context}}
+
+ Good g;
+ (void)typeid(g.f()); // Ok; not a polymorphic use of a glvalue.
+
+ // This is a polymorphic use of a glvalue, which results in the typeid being
+ // evaluated instead of unevaluated.
+ Bad b;
+ (void)typeid(b.f()); // expected-warning {{expression with side effects will be evaluated despite being used as an operand to 'typeid'}}
+
+ // A dereference of a volatile pointer is a side effecting operation, despite
+ // it being a reasonable operation.
+ int * volatile x;
+ (void)sizeof(*x); // expected-warning {{expression with side effects has no effect in an unevaluated context}}
+}
+}
class Poly { virtual ~Poly(); };
Poly& P(void*);
-void test2() { (void)typeid(P((void*)(id*)0)); } // expected-error {{pointer to non-const type 'id'}}
+void test2() { (void)typeid(P((void*)(id*)0)); } // expected-error {{pointer to non-const type 'id'}} expected-warning {{expression with side effects will be evaluated despite being used as an operand to 'typeid'}}