diff --git a/src/declaration.c b/src/declaration.c index bc89435a83ce..b451b058cda8 100644 --- a/src/declaration.c +++ b/src/declaration.c @@ -1728,7 +1728,7 @@ void VarDeclaration::checkCtorConstInit() * rather than the current one. */ -void VarDeclaration::checkNestedReference(Scope *sc, Loc loc) +bool VarDeclaration::checkNestedReference(Scope *sc, Loc loc) { //printf("VarDeclaration::checkNestedReference() %s\n", toChars()); if (parent && !isDataseg() && parent != sc->parent && @@ -1763,7 +1763,41 @@ void VarDeclaration::checkNestedReference(Scope *sc, Loc loc) //printf("\tfdthis = %s\n", fdthis->toChars()); if (loc.filename) - fdthis->getLevel(loc, sc, fdv); + { + int lv = fdthis->getLevel(loc, sc, fdv); + if (lv == -2) // error + return false; + if (lv > 0 && + fdv->isPureBypassingInference() >= PUREweak && + fdthis->isPureBypassingInference() == PUREfwdref && + fdthis->isInstantiated()) + { + /* Bugzilla 9148 and 14039: + * void foo() pure { + * int x; + * void bar()() { // default is impure + * x = 1; // access to enclosing pure function context + * // means that bar should have weak purity. + * } + * } + */ + fdthis->flags &= ~FUNCFLAGpurityInprocess; + if (fdthis->type->ty == Tfunction) + { + TypeFunction *tf = (TypeFunction *)fdthis->type; + if (tf->deco) + { + tf = (TypeFunction *)tf->copy(); + tf->purity = PUREfwdref; + tf->deco = NULL; + tf->deco = tf->merge()->deco; + } + else + tf->purity = PUREfwdref; + fdthis->type = tf; + } + } + } // Function literals from fdthis to fdv must be delegates for (Dsymbol *s = fdthis; s && s != fdv; s = s->toParent2()) @@ -1772,20 +1806,6 @@ void VarDeclaration::checkNestedReference(Scope *sc, Loc loc) if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration()) { fld->tok = TOKdelegate; -#if 0 - /* This is necessary to avoid breaking tests for 8751 & 8793. - * See: compilable/testInference.d - */ - // if is a mutable variable or - // has any mutable indirections or - // does not belong to pure function - if (type->isMutable() || - !type->implicitConvTo(type->immutableOf()) || - !fdv->isPureBypassingInference()) - { - fld->setImpure(); // Bugzilla 9415 - } -#endif } } @@ -1806,10 +1826,14 @@ void VarDeclaration::checkNestedReference(Scope *sc, Loc loc) //printf("var %s in function %s is nested ref\n", toChars(), fdv->toChars()); // __dollar creates problems because it isn't a real variable Bugzilla 3326 if (ident == Id::dollar) + { ::error(loc, "cannnot use $ inside a function literal"); + return false; + } } } } + return true; } /**************************** diff --git a/src/declaration.h b/src/declaration.h index 2c66b6f8d171..ddfc88b7830d 100644 --- a/src/declaration.h +++ b/src/declaration.h @@ -280,7 +280,7 @@ class VarDeclaration : public Declaration ExpInitializer *getExpInitializer(); Expression *getConstInitializer(bool needFullType = true); void checkCtorConstInit(); - void checkNestedReference(Scope *sc, Loc loc); + bool checkNestedReference(Scope *sc, Loc loc); Dsymbol *toAlias(); // Eliminate need for dynamic_cast VarDeclaration *isVarDeclaration() { return (VarDeclaration *)this; } @@ -626,7 +626,6 @@ class FuncDeclaration : public Declaration bool hasOverloads(); PURE isPure(); PURE isPureBypassingInference(); - bool isPureBypassingInferenceX(); bool setImpure(); bool isSafe(); bool isSafeBypassingInference(); @@ -649,7 +648,7 @@ class FuncDeclaration : public Declaration virtual bool addPostInvariant(); const char *kind(); FuncDeclaration *isUnique(); - void checkNestedReference(Scope *sc, Loc loc); + bool checkNestedReference(Scope *sc, Loc loc); bool needsClosure(); bool hasNestedFrameRefs(); void buildResultVar(Scope *sc, Type *tret); diff --git a/src/delegatize.c b/src/delegatize.c index 8ada6b3fd06b..26ad87feb4e8 100644 --- a/src/delegatize.c +++ b/src/delegatize.c @@ -33,7 +33,7 @@ bool walkPostorder(Expression *e, StoppableVisitor *v); void lambdaSetParent(Expression *e, Scope *sc); -void lambdaCheckForNestedRef(Expression *e, Scope *sc); +bool lambdaCheckForNestedRef(Expression *e, Scope *sc); Expression *toDelegate(Expression *e, Scope *sc) { @@ -50,9 +50,12 @@ Expression *toDelegate(Expression *e, Scope *sc) sc = sc->push(); sc->parent = fld; // set current function to be the delegate lambdaSetParent(e, sc); - lambdaCheckForNestedRef(e, sc); + bool r = lambdaCheckForNestedRef(e, sc); sc = sc->pop(); + if (!r) + return new ErrorExp(); + Statement *s; if (t->ty == Tvoid) s = new ExpStatement(loc, e); @@ -110,14 +113,20 @@ void lambdaSetParent(Expression *e, Scope *sc) /******************************************* * Look for references to variables in a scope enclosing the new function literal. + * Returns false if error occurs. */ -void lambdaCheckForNestedRef(Expression *e, Scope *sc) +bool lambdaCheckForNestedRef(Expression *e, Scope *sc) { class LambdaCheckForNestedRef : public StoppableVisitor { - Scope *sc; public: - LambdaCheckForNestedRef(Scope *sc) : sc(sc) {} + Scope *sc; + bool result; + + LambdaCheckForNestedRef(Scope *sc) + : sc(sc), result(true) + { + } void visit(Expression *) { @@ -127,21 +136,21 @@ void lambdaCheckForNestedRef(Expression *e, Scope *sc) { VarDeclaration *v = e->var->isVarDeclaration(); if (v) - v->checkNestedReference(sc, Loc()); + result = v->checkNestedReference(sc, Loc()); } void visit(VarExp *e) { VarDeclaration *v = e->var->isVarDeclaration(); if (v) - v->checkNestedReference(sc, Loc()); + result = v->checkNestedReference(sc, Loc()); } void visit(ThisExp *e) { VarDeclaration *v = e->var->isVarDeclaration(); if (v) - v->checkNestedReference(sc, Loc()); + result = v->checkNestedReference(sc, Loc()); } void visit(DeclarationExp *e) @@ -149,7 +158,9 @@ void lambdaCheckForNestedRef(Expression *e, Scope *sc) VarDeclaration *v = e->declaration->isVarDeclaration(); if (v) { - v->checkNestedReference(sc, Loc()); + result = v->checkNestedReference(sc, Loc()); + if (!result) + return; /* Some expressions cause the frontend to create a temporary. * For example, structs with cpctors replace the original @@ -163,13 +174,14 @@ void lambdaCheckForNestedRef(Expression *e, Scope *sc) if (v->init && v->init->isExpInitializer()) { Expression *ie = v->init->toExpression(); - lambdaCheckForNestedRef(ie, sc); + result = lambdaCheckForNestedRef(ie, sc); } } } }; - LambdaCheckForNestedRef lcnr(sc); - walkPostorder(e, &lcnr); + LambdaCheckForNestedRef v(sc); + walkPostorder(e, &v); + return v.result; } diff --git a/src/expression.c b/src/expression.c index e17a3b36f9de..f55701b0d67b 100644 --- a/src/expression.c +++ b/src/expression.c @@ -2384,23 +2384,50 @@ void Expression::checkPurity(Scope *sc, FuncDeclaration *f) // Find the closest pure parent of the calling function FuncDeclaration *outerfunc = sc->func; - while ( outerfunc->toParent2() && - !outerfunc->isPureBypassingInference() && - outerfunc->toParent2()->isFuncDeclaration()) + FuncDeclaration *calledparent = f; + + if (outerfunc->isInstantiated()) { - outerfunc = outerfunc->toParent2()->isFuncDeclaration(); - if (outerfunc->type->ty == Terror) - return; + // The attributes of outerfunc should be inferred from the call of f. } - - FuncDeclaration *calledparent = f; - while ( calledparent->toParent2() && - !calledparent->isPureBypassingInference() && - calledparent->toParent2()->isFuncDeclaration()) + else if (f->isFuncLiteralDeclaration()) { - calledparent = calledparent->toParent2()->isFuncDeclaration(); - if (calledparent->type->ty == Terror) - return; + // The attributes of f is always inferred in its declared place. + } + else + { + /* Today, static local functions are impure by default, but they cannot + * violate purity of enclosing functions. + * + * auto foo() pure { // non instantiated funciton + * static auto bar() { // static, without pure attribute + * impureFunc(); // impure call + * // Although impureFunc is called inside bar, f(= impureFunc) + * // is not callable inside pure outerfunc(= foo <- bar). + * } + * + * bar(); + * // Although bar is called inside foo, f(= bar) is callable + * // bacause calledparent(= foo) is same with outerfunc(= foo). + * } + */ + + while (outerfunc->toParent2() && + outerfunc->isPureBypassingInference() == PUREimpure && + outerfunc->toParent2()->isFuncDeclaration()) + { + outerfunc = outerfunc->toParent2()->isFuncDeclaration(); + if (outerfunc->type->ty == Terror) + return; + } + while (calledparent->toParent2() && + calledparent->isPureBypassingInference() == PUREimpure && + calledparent->toParent2()->isFuncDeclaration()) + { + calledparent = calledparent->toParent2()->isFuncDeclaration(); + if (calledparent->type->ty == Terror) + return; + } } // If the caller has a pure parent, then either the called func must be pure, @@ -2408,7 +2435,7 @@ void Expression::checkPurity(Scope *sc, FuncDeclaration *f) if (!f->isPure() && calledparent != outerfunc) { FuncDeclaration *ff = outerfunc; - if (sc->flags & SCOPEcompile ? ff->isPureBypassingInferenceX() : ff->setImpure()) + if (sc->flags & SCOPEcompile ? ff->isPureBypassingInference() >= PUREweak : ff->setImpure()) { error("pure function '%s' cannot call impure function '%s'", ff->toPrettyChars(), f->toPrettyChars()); @@ -2453,7 +2480,7 @@ void Expression::checkPurity(Scope *sc, VarDeclaration *v) * functions must be pure. */ FuncDeclaration *ff = sc->func; - if (sc->flags & SCOPEcompile ? ff->isPureBypassingInferenceX() : ff->setImpure()) + if (sc->flags & SCOPEcompile ? ff->isPureBypassingInference() >= PUREweak : ff->setImpure()) { error("pure function '%s' cannot access mutable static data '%s'", ff->toPrettyChars(), v->toChars()); @@ -2476,6 +2503,14 @@ void Expression::checkPurity(Scope *sc, VarDeclaration *v) */ Dsymbol *vparent = v->toParent2(); + if (FuncDeclaration *fdp = vparent->isFuncDeclaration()) + { + if (!sc->func->isPureBypassingInference() && fdp->setImpure()) + { + error("impure function '%s' cannot access variable '%s' declared in enclosing pure function '%s'", + sc->func->toChars(), v->toChars(), fdp->toPrettyChars()); + } + } for (Dsymbol *s = sc->func; s; s = s->toParent2()) { if (s == vparent) @@ -3439,7 +3474,8 @@ Expression *ThisExp::semantic(Scope *sc) var = fd->vthis; assert(var->parent); type = var->type; - var->isVarDeclaration()->checkNestedReference(sc, loc); + if (!var->isVarDeclaration()->checkNestedReference(sc, loc)) + return new ErrorExp(); if (!sc->intypeof) sc->callSuper |= CSXthis; return this; @@ -3544,7 +3580,8 @@ Expression *SuperExp::semantic(Scope *sc) type = type->castMod(var->type->mod); } - var->isVarDeclaration()->checkNestedReference(sc, loc); + if (!var->isVarDeclaration()->checkNestedReference(sc, loc)) + return new ErrorExp(); if (!sc->intypeof) sc->callSuper |= CSXsuper; @@ -5053,12 +5090,16 @@ Expression *SymOffExp::semantic(Scope *sc) //var->semantic(sc); if (!type) type = var->type->pointerTo(); - VarDeclaration *v = var->isVarDeclaration(); - if (v) - v->checkNestedReference(sc, loc); - FuncDeclaration *f = var->isFuncDeclaration(); - if (f) - f->checkNestedReference(sc, loc); + if (VarDeclaration *v = var->isVarDeclaration()) + { + if (!v->checkNestedReference(sc, loc)) + return new ErrorExp(); + } + else if (FuncDeclaration *f = var->isFuncDeclaration()) + { + if (!f->checkNestedReference(sc, loc)) + return new ErrorExp(); + } return this; } @@ -5125,11 +5166,13 @@ Expression *VarExp::semantic(Scope *sc) if (VarDeclaration *vd = var->isVarDeclaration()) { hasOverloads = 0; - vd->checkNestedReference(sc, loc); + if (!vd->checkNestedReference(sc, loc)) + return new ErrorExp(); } else if (FuncDeclaration *fd = var->isFuncDeclaration()) { - fd->checkNestedReference(sc, loc); + if (!fd->checkNestedReference(sc, loc)) + return new ErrorExp(); } else if (OverDeclaration *od = var->isOverDeclaration()) { @@ -8733,7 +8776,8 @@ Expression *CallExp::semantic(Scope *sc) checkPurity(sc, f); checkSafety(sc, f); checkNogc(sc, f); - f->checkNestedReference(sc, loc); + if (!f->checkNestedReference(sc, loc)) + return new ErrorExp(); } else if (sc->func && sc->intypeof != 1 && !(sc->flags & SCOPEctfe)) { @@ -8820,7 +8864,8 @@ Expression *CallExp::semantic(Scope *sc) checkPurity(sc, f); checkSafety(sc, f); checkNogc(sc, f); - f->checkNestedReference(sc, loc); + if (!f->checkNestedReference(sc, loc)) + return new ErrorExp(); accessCheck(loc, sc, NULL, f); ethis = NULL; diff --git a/src/func.c b/src/func.c index 8b5b57a2d526..385ad1210f50 100644 --- a/src/func.c +++ b/src/func.c @@ -453,7 +453,19 @@ void FuncDeclaration::semantic(Scope *sc) tf->trust = TRUSTsafe; // default to @safe } - /* If the nesting parent is pure, then this function defaults to pure too. + /* If the nesting parent is pure without inference, + * then this function defaults to pure too. + * + * auto foo() pure { + * auto bar() {} // become a weak purity funciton + * class C { // nested class + * auto baz() {} // become a weak purity funciton + * } + * + * static auto boo() {} // typed as impure + * // Even though, boo cannot call any impure functions. + * // See also Expression;;checkPurity(). + * } */ if (tf->purity == PUREimpure && (isNested() || isThis())) { @@ -473,8 +485,11 @@ void FuncDeclaration::semantic(Scope *sc) /* If the parent's purity is inferred, then this function's purity needs * to be inferred first. */ - if (fd && fd->isPureBypassingInferenceX()) + if (fd && fd->isPureBypassingInference() >= PUREweak && + !isInstantiated()) + { tf->purity = PUREfwdref; // default to pure + } } } @@ -1517,7 +1532,11 @@ void FuncDeclaration::semantic3(Scope *sc) if (!fbody) fbody = new CompoundStatement(Loc(), new Statements()); - assert(type == f); + assert(type == f || + (type->ty == Tfunction && + f->purity == PUREimpure && + ((TypeFunction *)type)->purity >= PUREfwdref)); + f = (TypeFunction *)type; if (inferRetType) { @@ -3320,8 +3339,9 @@ AggregateDeclaration *FuncDeclaration::isMember2() * Error if this cannot call fd. * Returns: * 0 same level - * -1 increase nesting by 1 (fd is nested within 'this') * >0 decrease nesting by number + * -1 increase nesting by 1 (fd is nested within 'this') + * -2 error */ int FuncDeclaration::getLevel(Loc loc, Scope *sc, FuncDeclaration *fd) @@ -3381,6 +3401,7 @@ int FuncDeclaration::getLevel(Loc loc, Scope *sc, FuncDeclaration *fd) // better diagnostics for static functions ::error(loc, "%s%s %s cannot access frame of function %s", xstatic, kind(), toPrettyChars(), fd->toPrettyChars()); + return -2; } return 1; } @@ -3551,11 +3572,6 @@ PURE FuncDeclaration::isPureBypassingInference() return isPure(); } -bool FuncDeclaration::isPureBypassingInferenceX() -{ - return !(flags & FUNCFLAGpurityInprocess) && isPure() != PUREimpure; -} - /************************************** * The function is doing something impure, * so mark it as impure. @@ -3963,7 +3979,7 @@ const char *FuncDeclaration::kind() * then mark it as a delegate. */ -void FuncDeclaration::checkNestedReference(Scope *sc, Loc loc) +bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc) { //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars()); if (parent && parent != sc->parent && this->isNested() && @@ -4003,10 +4019,12 @@ void FuncDeclaration::checkNestedReference(Scope *sc, Loc loc) if (fdv && fdthis && fdv != fdthis) { int lv = fdthis->getLevel(loc, sc, fdv); + if (lv == -2) + return false; // error if (lv == -1) - return; // downlevel call + return true; // downlevel call if (lv == 0) - return; // same level call + return true; // same level call // Uplevel call @@ -4017,6 +4035,7 @@ void FuncDeclaration::checkNestedReference(Scope *sc, Loc loc) fld->tok = TOKdelegate; } } + return true; } /* For all functions between outerFunc and f, mark them as needing diff --git a/src/mtype.c b/src/mtype.c index 8c429940181a..b22d37f6d2f7 100644 --- a/src/mtype.c +++ b/src/mtype.c @@ -6039,6 +6039,7 @@ bool TypeFunction::hasLazyParameters() bool TypeFunction::parameterEscapes(Parameter *p) { + purityLevel(); /* Scope parameters do not escape. * Allow 'lazy' to imply 'scope' - @@ -6057,12 +6058,14 @@ bool TypeFunction::parameterEscapes(Parameter *p) return true; if (purity > PUREweak) - { /* With pure functions, we need only be concerned if p escapes + { + /* With pure functions, we need only be concerned if p escapes * via any return statement. */ Type* tret = nextOf()->toBasetype(); if (!isref && !tret->hasPointers()) - { /* The result has no references, so p could not be escaping + { + /* The result has no references, so p could not be escaping * that way. */ return false; diff --git a/src/mtype.h b/src/mtype.h index a6dda47d3ce5..32713e274937 100644 --- a/src/mtype.h +++ b/src/mtype.h @@ -589,10 +589,10 @@ enum TRUSTformat enum PURE { PUREimpure = 0, // not pure at all - PUREweak = 1, // no mutable globals are read or written - PUREconst = 2, // parameters are values or const - PUREstrong = 3, // parameters are values or immutable - PUREfwdref = 4, // it's pure, but not known which level yet + PUREfwdref = 1, // it's pure, but not known which level yet + PUREweak = 2, // no mutable globals are read or written + PUREconst = 3, // parameters are values or const + PUREstrong = 4, // parameters are values or immutable }; RET retStyle(TypeFunction *tf); diff --git a/src/statement.c b/src/statement.c index 3edd7ce67eb8..44a8da9e5a79 100644 --- a/src/statement.c +++ b/src/statement.c @@ -3938,7 +3938,8 @@ Statement *ReturnStatement::semantic(Scope *sc) else { fd->buildResultVar(NULL, exp->type); - fd->vresult->checkNestedReference(sc, Loc()); + bool r = fd->vresult->checkNestedReference(sc, Loc()); + assert(r); // vresult should be always accessible // Send out "case receiver" statement to the foreach. // return vresult; diff --git a/test/compilable/testInference.d b/test/compilable/testInference.d index 19f91dea4b46..91d7e0b981b4 100644 --- a/test/compilable/testInference.d +++ b/test/compilable/testInference.d @@ -548,7 +548,7 @@ class Node10002 /***************************************************/ // 10148 -void fa10148() pure {} // fa is @system +void fa10148() {} // fa is @system auto fb10148(T)() { @@ -561,7 +561,7 @@ auto fb10148(T)() void fc(T2)() { // [5] During semantic3 process, fc is not @safe on default. - static assert(is(typeof(&fc) == void delegate() pure)); + static assert(is(typeof(&fc) == void delegate())); fa10148(); } // [1] this is now inferred to @safe by implementing issue 7511 @@ -578,7 +578,7 @@ void test10148() // [3] instantiate fc // [6] Afer semantic3 done, fc!int is deduced to @system. - static assert(is(typeof(&fb10148!int.fc!int) == void delegate() pure @system)); + static assert(is(typeof(&fb10148!int.fc!int) == void delegate() @system)); } /***************************************************/ diff --git a/test/fail_compilation/fail12378.d b/test/fail_compilation/fail12378.d index 83f3bd1ecde6..e9685841cb48 100644 --- a/test/fail_compilation/fail12378.d +++ b/test/fail_compilation/fail12378.d @@ -53,9 +53,9 @@ fail_compilation/fail12378.d(143): instantiated from here: __lambda1!int fail_compilation/fail12378.d(135): instantiated from here: MapResultI!((y0) => iota(2).mapI!((x0) => ANYTHING - GOES), Result) fail_compilation/fail12378.d(62): instantiated from here: mapI!(Result) fail_compilation/fail12378.d(143): Error: static function fail12378.testI.MapResultI!((y0) => iota(2).mapI!((x0) => ANYTHING - GOES), Result).MapResultI.front cannot access frame of function fail12378.testI -fail_compilation/fail12378.d(143): Error: static function fail12378.testI.MapResultI!((y0) => iota(2).mapI!((x0) => ANYTHING - GOES), Result).MapResultI.front cannot access frame of function fail12378.testI --- */ + void testI() { auto r = diff --git a/test/fail_compilation/fail9148.d b/test/fail_compilation/fail9148.d new file mode 100644 index 000000000000..3881d87d72d7 --- /dev/null +++ b/test/fail_compilation/fail9148.d @@ -0,0 +1,46 @@ +// Test case for issue 9148, found by the regression 14039 + +void impure() {} // impure + +/* +TEST_OUTPUT: +--- +fail_compilation/fail9148.d(22): Error: pure function 'fail9148.fb1!int.fb1.A!int.A.fc!int.fc' cannot call impure function 'fail9148.impure' +fail_compilation/fail9148.d(44): Error: template instance fail9148.fb1!int.fb1.A!int.A.fc!int error instantiating +fail_compilation/fail9148.d(36): Error: impure function 'fc' cannot access variable 'x' declared in enclosing pure function 'fail9148.fb2!int.fb2' +fail_compilation/fail9148.d(45): Error: template instance fail9148.fb2!int.fb2.A!int.A.fc!int error instantiating +--- +*/ +auto fb1(T)() +{ + int x; + struct A(S) + { + void fc(T2)() + { + x = 1; // accessing pure function context makes fc as pure + impure(); // error, impure function call + } + this(S a) {} + } + return A!int(); +} +auto fb2(T)() +{ + int x; + struct A(S) + { + void fc(T2)() + { + impure(); // impure function call makes fc as impure + x = 1; // error, accessing pure context + } + this(S a) {} + } + return A!int(); +} +void test1() +{ + fb1!int().fc!int(); + fb2!int().fc!int(); +} diff --git a/test/fail_compilation/testInference.d b/test/fail_compilation/testInference.d index 6fc29218ff52..1380f3a86e1a 100644 --- a/test/fail_compilation/testInference.d +++ b/test/fail_compilation/testInference.d @@ -133,3 +133,23 @@ immutable(void)* g10063(inout int* p) pure { return f10063(p); } + +/* +TEST_OUTPUT: +--- +fail_compilation/testInference.d(154): Error: pure function 'testInference.bar14049' cannot call impure function 'testInference.foo14049!int.foo14049' +--- +*/ +auto impure14049() { return 1; } + +void foo14049(T)(T val) +{ + auto n = () @trusted { + return impure14049(); + }(); +} + +void bar14049() pure +{ + foo14049(1); +} diff --git a/test/runnable/xtest46.d b/test/runnable/xtest46.d index ecfd6ee715dd..6dc485ed79fe 100644 --- a/test/runnable/xtest46.d +++ b/test/runnable/xtest46.d @@ -5296,7 +5296,7 @@ class C5311 void breaksPure() pure const { static assert(!__traits(compiles, { globalData++; })); // SHOULD BE ERROR - static assert(!__traits(compiles, { X.globalData++; })); // SHOULD BE ERROR + static assert(!__traits(compiles, { C5311.globalData++; }));// SHOULD BE ERROR static assert(!__traits(compiles, { this.globalData++; })); // SHOULD BE ERROR static assert(!__traits(compiles, { int a = this.globalData; })); @@ -5316,7 +5316,7 @@ struct S5311 void breaksPure() pure const { static assert(!__traits(compiles, { globalData++; })); // SHOULD BE ERROR - static assert(!__traits(compiles, { X.globalData++; })); // SHOULD BE ERROR + static assert(!__traits(compiles, { S5311.globalData++; }));// SHOULD BE ERROR static assert(!__traits(compiles, { this.globalData++; })); // SHOULD BE ERROR static assert(!__traits(compiles, { int a = this.globalData; }));