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