1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved 6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(DMDSRC _sideeffect.d) 9 */ 10 11 module ddmd.sideeffect; 12 13 import ddmd.apply; 14 import ddmd.declaration; 15 import ddmd.dscope; 16 import ddmd.expression; 17 import ddmd.func; 18 import ddmd.globals; 19 import ddmd.identifier; 20 import ddmd.init; 21 import ddmd.mtype; 22 import ddmd.tokens; 23 import ddmd.visitor; 24 25 /************************************************** 26 * Front-end expression rewriting should create temporary variables for 27 * non trivial sub-expressions in order to: 28 * 1. save evaluation order 29 * 2. prevent sharing of sub-expression in AST 30 */ 31 extern (C++) bool isTrivialExp(Expression e) 32 { 33 extern (C++) final class IsTrivialExp : StoppableVisitor 34 { 35 alias visit = super.visit; 36 public: 37 extern (D) this() 38 { 39 } 40 41 override void visit(Expression e) 42 { 43 /* Bugzilla 11201: CallExp is always non trivial expression, 44 * especially for inlining. 45 */ 46 if (e.op == TOKcall) 47 { 48 stop = true; 49 return; 50 } 51 // stop walking if we determine this expression has side effects 52 stop = lambdaHasSideEffect(e); 53 } 54 } 55 56 scope IsTrivialExp v = new IsTrivialExp(); 57 return walkPostorder(e, v) == false; 58 } 59 60 /******************************************** 61 * Determine if Expression has any side effects. 62 */ 63 extern (C++) bool hasSideEffect(Expression e) 64 { 65 extern (C++) final class LambdaHasSideEffect : StoppableVisitor 66 { 67 alias visit = super.visit; 68 public: 69 extern (D) this() 70 { 71 } 72 73 override void visit(Expression e) 74 { 75 // stop walking if we determine this expression has side effects 76 stop = lambdaHasSideEffect(e); 77 } 78 } 79 80 scope LambdaHasSideEffect v = new LambdaHasSideEffect(); 81 return walkPostorder(e, v); 82 } 83 84 /******************************************** 85 * Determine if the call of f, or function type or delegate type t1, has any side effects. 86 * Returns: 87 * 0 has any side effects 88 * 1 nothrow + constant purity 89 * 2 nothrow + strong purity 90 */ 91 extern (C++) int callSideEffectLevel(FuncDeclaration f) 92 { 93 /* Bugzilla 12760: ctor call always has side effects. 94 */ 95 if (f.isCtorDeclaration()) 96 return 0; 97 assert(f.type.ty == Tfunction); 98 TypeFunction tf = cast(TypeFunction)f.type; 99 if (tf.isnothrow) 100 { 101 PURE purity = f.isPure(); 102 if (purity == PUREstrong) 103 return 2; 104 if (purity == PUREconst) 105 return 1; 106 } 107 return 0; 108 } 109 110 extern (C++) int callSideEffectLevel(Type t) 111 { 112 t = t.toBasetype(); 113 TypeFunction tf; 114 if (t.ty == Tdelegate) 115 tf = cast(TypeFunction)(cast(TypeDelegate)t).next; 116 else 117 { 118 assert(t.ty == Tfunction); 119 tf = cast(TypeFunction)t; 120 } 121 tf.purityLevel(); 122 PURE purity = tf.purity; 123 if (t.ty == Tdelegate && purity > PUREweak) 124 { 125 if (tf.isMutable()) 126 purity = PUREweak; 127 else if (!tf.isImmutable()) 128 purity = PUREconst; 129 } 130 if (tf.isnothrow) 131 { 132 if (purity == PUREstrong) 133 return 2; 134 if (purity == PUREconst) 135 return 1; 136 } 137 return 0; 138 } 139 140 extern (C++) bool lambdaHasSideEffect(Expression e) 141 { 142 switch (e.op) 143 { 144 // Sort the cases by most frequently used first 145 case TOKassign: 146 case TOKplusplus: 147 case TOKminusminus: 148 case TOKdeclaration: 149 case TOKconstruct: 150 case TOKblit: 151 case TOKaddass: 152 case TOKminass: 153 case TOKcatass: 154 case TOKmulass: 155 case TOKdivass: 156 case TOKmodass: 157 case TOKshlass: 158 case TOKshrass: 159 case TOKushrass: 160 case TOKandass: 161 case TOKorass: 162 case TOKxorass: 163 case TOKpowass: 164 case TOKin: 165 case TOKremove: 166 case TOKassert: 167 case TOKhalt: 168 case TOKdelete: 169 case TOKnew: 170 case TOKnewanonclass: 171 return true; 172 case TOKcall: 173 { 174 CallExp ce = cast(CallExp)e; 175 /* Calling a function or delegate that is pure nothrow 176 * has no side effects. 177 */ 178 if (ce.e1.type) 179 { 180 Type t = ce.e1.type.toBasetype(); 181 if (t.ty == Tdelegate) 182 t = (cast(TypeDelegate)t).next; 183 if (t.ty == Tfunction && (ce.f ? callSideEffectLevel(ce.f) : callSideEffectLevel(ce.e1.type)) > 0) 184 { 185 } 186 else 187 return true; 188 } 189 break; 190 } 191 case TOKcast: 192 { 193 CastExp ce = cast(CastExp)e; 194 /* if: 195 * cast(classtype)func() // because it may throw 196 */ 197 if (ce.to.ty == Tclass && ce.e1.op == TOKcall && ce.e1.type.ty == Tclass) 198 return true; 199 break; 200 } 201 default: 202 break; 203 } 204 return false; 205 } 206 207 /*********************************** 208 * The result of this expression will be discarded. 209 * Complain if the operation has no side effects (and hence is meaningless). 210 */ 211 extern (C++) void discardValue(Expression e) 212 { 213 if (lambdaHasSideEffect(e)) // check side-effect shallowly 214 return; 215 switch (e.op) 216 { 217 case TOKcast: 218 { 219 CastExp ce = cast(CastExp)e; 220 if (ce.to.equals(Type.tvoid)) 221 { 222 /* 223 * Don't complain about an expression with no effect if it was cast to void 224 */ 225 return; 226 } 227 break; // complain 228 } 229 case TOKerror: 230 return; 231 case TOKvar: 232 { 233 VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration(); 234 if (v && (v.storage_class & STCtemp)) 235 { 236 // Bugzilla 5810: Don't complain about an internal generated variable. 237 return; 238 } 239 break; 240 } 241 case TOKcall: 242 /* Issue 3882: */ 243 if (global.params.warnings && !global.gag) 244 { 245 CallExp ce = cast(CallExp)e; 246 if (e.type.ty == Tvoid) 247 { 248 /* Don't complain about calling void-returning functions with no side-effect, 249 * because purity and nothrow are inferred, and because some of the 250 * runtime library depends on it. Needs more investigation. 251 * 252 * One possible solution is to restrict this message to only be called in hierarchies that 253 * never call assert (and or not called from inside unittest blocks) 254 */ 255 } 256 else if (ce.e1.type) 257 { 258 Type t = ce.e1.type.toBasetype(); 259 if (t.ty == Tdelegate) 260 t = (cast(TypeDelegate)t).next; 261 if (t.ty == Tfunction && (ce.f ? callSideEffectLevel(ce.f) : callSideEffectLevel(ce.e1.type)) > 0) 262 { 263 const(char)* s; 264 if (ce.f) 265 s = ce.f.toPrettyChars(); 266 else if (ce.e1.op == TOKstar) 267 { 268 // print 'fp' if ce->e1 is (*fp) 269 s = (cast(PtrExp)ce.e1).e1.toChars(); 270 } 271 else 272 s = ce.e1.toChars(); 273 e.warning("calling %s without side effects discards return value of type %s, prepend a cast(void) if intentional", s, e.type.toChars()); 274 } 275 } 276 } 277 return; 278 case TOKscope: 279 e.error("%s has no effect", e.toChars()); 280 return; 281 case TOKandand: 282 { 283 AndAndExp aae = cast(AndAndExp)e; 284 discardValue(aae.e2); 285 return; 286 } 287 case TOKoror: 288 { 289 OrOrExp ooe = cast(OrOrExp)e; 290 discardValue(ooe.e2); 291 return; 292 } 293 case TOKquestion: 294 { 295 CondExp ce = cast(CondExp)e; 296 /* Bugzilla 6178 & 14089: Either CondExp::e1 or e2 may have 297 * redundant expression to make those types common. For example: 298 * 299 * struct S { this(int n); int v; alias v this; } 300 * S[int] aa; 301 * aa[1] = 0; 302 * 303 * The last assignment statement will be rewitten to: 304 * 305 * 1 in aa ? aa[1].value = 0 : (aa[1] = 0, aa[1].this(0)).value; 306 * 307 * The last DotVarExp is necessary to take assigned value. 308 * 309 * int value = (aa[1] = 0); // value = aa[1].value 310 * 311 * To avoid false error, discardValue() should be called only when 312 * the both tops of e1 and e2 have actually no side effects. 313 */ 314 if (!lambdaHasSideEffect(ce.e1) && !lambdaHasSideEffect(ce.e2)) 315 { 316 discardValue(ce.e1); 317 discardValue(ce.e2); 318 } 319 return; 320 } 321 case TOKcomma: 322 { 323 CommaExp ce = cast(CommaExp)e; 324 /* Check for compiler-generated code of the form auto __tmp, e, __tmp; 325 * In such cases, only check e for side effect (it's OK for __tmp to have 326 * no side effect). 327 * See Bugzilla 4231 for discussion 328 */ 329 CommaExp firstComma = ce; 330 while (firstComma.e1.op == TOKcomma) 331 firstComma = cast(CommaExp)firstComma.e1; 332 if (firstComma.e1.op == TOKdeclaration && ce.e2.op == TOKvar && (cast(DeclarationExp)firstComma.e1).declaration == (cast(VarExp)ce.e2).var) 333 { 334 return; 335 } 336 // Don't check e1 until we cast(void) the a,b code generation 337 //discardValue(ce->e1); 338 discardValue(ce.e2); 339 return; 340 } 341 case TOKtuple: 342 /* Pass without complaint if any of the tuple elements have side effects. 343 * Ideally any tuple elements with no side effects should raise an error, 344 * this needs more investigation as to what is the right thing to do. 345 */ 346 if (!hasSideEffect(e)) 347 break; 348 return; 349 default: 350 break; 351 } 352 e.error("%s has no effect in expression (%s)", Token.toChars(e.op), e.toChars()); 353 } 354 355 /************************************************** 356 * Build a temporary variable to copy the value of e into. 357 * Params: 358 * stc = storage classes will be added to the made temporary variable 359 * name = name for temporary variable 360 * e = original expression 361 * Returns: 362 * Newly created temporary variable. 363 */ 364 VarDeclaration copyToTemp(StorageClass stc, const char* name, Expression e) 365 { 366 assert(name && name[0] == '_' && name[1] == '_'); 367 auto id = Identifier.generateId(name); 368 auto ez = new ExpInitializer(e.loc, e); 369 auto vd = new VarDeclaration(e.loc, e.type, id, ez); 370 vd.storage_class = stc; 371 vd.storage_class |= STCtemp; 372 vd.storage_class |= STCctfe; // temporary is always CTFEable 373 return vd; 374 } 375 376 /************************************************** 377 * Build a temporary variable to extract e's evaluation, if e is not trivial. 378 * Params: 379 * sc = scope 380 * name = name for temporary variable 381 * e0 = a new side effect part will be appended to it. 382 * e = original expression 383 * alwaysCopy = if true, build new temporary variable even if e is trivial. 384 * Returns: 385 * When e is trivial and alwaysCopy == false, e itself is returned. 386 * Otherwise, a new VarExp is returned. 387 * Note: 388 * e's lvalue-ness will be handled well by STCref or STCrvalue. 389 */ 390 Expression extractSideEffect(Scope* sc, const char* name, 391 ref Expression e0, Expression e, bool alwaysCopy = false) 392 { 393 if (!alwaysCopy && isTrivialExp(e)) 394 return e; 395 396 auto vd = copyToTemp(0, name, e); 397 if (e.isLvalue()) 398 vd.storage_class |= STCref; 399 else 400 vd.storage_class |= STCrvalue; 401 402 Expression de = new DeclarationExp(vd.loc, vd); 403 Expression ve = new VarExp(vd.loc, vd); 404 de = de.semantic(sc); 405 ve = ve.semantic(sc); 406 407 e0 = Expression.combine(e0, de); 408 return ve; 409 }