| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,270 @@ | ||
| // Compiler implementation of the D programming language | ||
| // Copyright (c) 1999-2015 by Digital Mars | ||
| // All Rights Reserved | ||
| // written by Walter Bright | ||
| // http://www.digitalmars.com | ||
| // Distributed under the Boost Software License, Version 1.0. | ||
| // http://www.boost.org/LICENSE_1_0.txt | ||
|
|
||
| module ddmd.root.stringtable; | ||
|
|
||
| import core.stdc.stdint, core.stdc.string; | ||
| import ddmd.root.rmem; | ||
|
|
||
| enum POOL_BITS = 12; | ||
| enum POOL_SIZE = (1U << POOL_BITS); | ||
|
|
||
| // TODO: Merge with root.String | ||
| // MurmurHash2 was written by Austin Appleby, and is placed in the public | ||
| // domain. The author hereby disclaims copyright to this source code. | ||
| // https://sites.google.com/site/murmurhash/ | ||
| extern (C++) static uint32_t calcHash(const(char)* key, size_t len) | ||
| { | ||
| // 'm' and 'r' are mixing constants generated offline. | ||
| // They're not really 'magic', they just happen to work well. | ||
| const(uint32_t) m = 0x5bd1e995; | ||
| const(int) r = 24; | ||
| // Initialize the hash to a 'random' value | ||
| uint32_t h = cast(uint32_t)len; | ||
| // Mix 4 bytes at a time into the hash | ||
| const(uint8_t)* data = cast(const(uint8_t)*)key; | ||
| while (len >= 4) | ||
| { | ||
| uint32_t k = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]; | ||
| k *= m; | ||
| k ^= k >> r; | ||
| k *= m; | ||
| h *= m; | ||
| h ^= k; | ||
| data += 4; | ||
| len -= 4; | ||
| } | ||
| // Handle the last few bytes of the input array | ||
| switch (len & 3) | ||
| { | ||
| case 3: | ||
| h ^= data[2] << 16; | ||
| case 2: | ||
| h ^= data[1] << 8; | ||
| case 1: | ||
| h ^= data[0]; | ||
| h *= m; | ||
| default: | ||
| break; | ||
| } | ||
| // Do a few final mixes of the hash to ensure the last few | ||
| // bytes are well-incorporated. | ||
| h ^= h >> 13; | ||
| h *= m; | ||
| h ^= h >> 15; | ||
| return h; | ||
| } | ||
|
|
||
| extern (C++) static size_t nextpow2(size_t val) | ||
| { | ||
| size_t res = 1; | ||
| while (res < val) | ||
| res <<= 1; | ||
| return res; | ||
| } | ||
|
|
||
| extern (C++) __gshared const(double) loadFactor = 0.8; | ||
|
|
||
| struct StringEntry | ||
| { | ||
| uint32_t hash; | ||
| uint32_t vptr; | ||
| } | ||
|
|
||
| // StringValue is a variable-length structure. It has neither proper c'tors nor a | ||
| // factory method because the only thing which should be creating these is StringTable. | ||
| struct StringValue | ||
| { | ||
| void* ptrvalue; | ||
| size_t length; | ||
|
|
||
| extern (C++) char* lstring() | ||
| { | ||
| return cast(char*)(&this + 1); | ||
| } | ||
|
|
||
| extern (C++) const(size_t) len() | ||
| { | ||
| return length; | ||
| } | ||
|
|
||
| extern (C++) const(const(char)*) toDchars() | ||
| { | ||
| return cast(const(char)*)(&this + 1); | ||
| } | ||
| } | ||
|
|
||
| struct StringTable | ||
| { | ||
| private: | ||
| StringEntry* table; | ||
| size_t tabledim; | ||
| uint8_t** pools; | ||
| size_t npools; | ||
| size_t nfill; | ||
| size_t count; | ||
|
|
||
| public: | ||
| extern (C++) void _init(size_t size = 0) | ||
| { | ||
| size = nextpow2(cast(size_t)(size / loadFactor)); | ||
| if (size < 32) | ||
| size = 32; | ||
| table = cast(StringEntry*)mem.xcalloc(size, (table[0]).sizeof); | ||
| tabledim = size; | ||
| pools = null; | ||
| npools = nfill = 0; | ||
| count = 0; | ||
| } | ||
|
|
||
| extern (C++) void reset(size_t size = 0) | ||
| { | ||
| for (size_t i = 0; i < npools; ++i) | ||
| mem.xfree(pools[i]); | ||
| mem.xfree(table); | ||
| mem.xfree(pools); | ||
| table = null; | ||
| pools = null; | ||
| _init(size); | ||
| } | ||
|
|
||
| extern (C++) ~this() | ||
| { | ||
| for (size_t i = 0; i < npools; ++i) | ||
| mem.xfree(pools[i]); | ||
| mem.xfree(table); | ||
| mem.xfree(pools); | ||
| table = null; | ||
| pools = null; | ||
| } | ||
|
|
||
| extern (C++) StringValue* lookup(const(char)* s, size_t length) | ||
| { | ||
| const(hash_t) hash = calcHash(s, length); | ||
| const(size_t) i = findSlot(hash, s, length); | ||
| // printf("lookup %.*s %p\n", (int)length, s, table[i].value ?: NULL); | ||
| return getValue(table[i].vptr); | ||
| } | ||
|
|
||
| extern (C++) StringValue* insert(const(char)* s, size_t length) | ||
| { | ||
| const(hash_t) hash = calcHash(s, length); | ||
| size_t i = findSlot(hash, s, length); | ||
| if (table[i].vptr) | ||
| return null; // already in table | ||
| if (++count > tabledim * loadFactor) | ||
| { | ||
| grow(); | ||
| i = findSlot(hash, s, length); | ||
| } | ||
| table[i].hash = hash; | ||
| table[i].vptr = allocValue(s, length); | ||
| // printf("insert %.*s %p\n", (int)length, s, table[i].value ?: NULL); | ||
| return getValue(table[i].vptr); | ||
| } | ||
|
|
||
| extern (C++) StringValue* update(const(char)* s, size_t length) | ||
| { | ||
| const(hash_t) hash = calcHash(s, length); | ||
| size_t i = findSlot(hash, s, length); | ||
| if (!table[i].vptr) | ||
| { | ||
| if (++count > tabledim * loadFactor) | ||
| { | ||
| grow(); | ||
| i = findSlot(hash, s, length); | ||
| } | ||
| table[i].hash = hash; | ||
| table[i].vptr = allocValue(s, length); | ||
| } | ||
| // printf("update %.*s %p\n", (int)length, s, table[i].value ?: NULL); | ||
| return getValue(table[i].vptr); | ||
| } | ||
|
|
||
| /******************************** | ||
| * Walk the contents of the string table, | ||
| * calling fp for each entry. | ||
| * Params: | ||
| * fp = function to call. Returns !=0 to stop | ||
| * Returns: | ||
| * last return value of fp call | ||
| */ | ||
| extern (C++) int apply(int function(StringValue*) fp) | ||
| { | ||
| for (size_t i = 0; i < tabledim; ++i) | ||
| { | ||
| StringEntry* se = &table[i]; | ||
| if (!se.vptr) | ||
| continue; | ||
| StringValue* sv = getValue(se.vptr); | ||
| int result = (*fp)(sv); | ||
| if (result) | ||
| return result; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| private: | ||
| extern (C++) uint32_t allocValue(const(char)* s, size_t length) | ||
| { | ||
| const(size_t) nbytes = StringValue.sizeof + length + 1; | ||
| if (!npools || nfill + nbytes > POOL_SIZE) | ||
| { | ||
| pools = cast(uint8_t**)mem.xrealloc(pools, ++npools * (pools[0]).sizeof); | ||
| pools[npools - 1] = cast(uint8_t*)mem.xmalloc(nbytes > POOL_SIZE ? nbytes : POOL_SIZE); | ||
| nfill = 0; | ||
| } | ||
| StringValue* sv = cast(StringValue*)&pools[npools - 1][nfill]; | ||
| sv.ptrvalue = null; | ||
| sv.length = length; | ||
| .memcpy(sv.lstring(), s, length); | ||
| sv.lstring()[length] = 0; | ||
| const(uint32_t) vptr = cast(uint32_t)(npools << POOL_BITS | nfill); | ||
| nfill += nbytes + (-nbytes & 7); // align to 8 bytes | ||
| return vptr; | ||
| } | ||
|
|
||
| extern (C++) StringValue* getValue(uint32_t vptr) | ||
| { | ||
| if (!vptr) | ||
| return null; | ||
| const(size_t) idx = (vptr >> POOL_BITS) - 1; | ||
| const(size_t) off = vptr & POOL_SIZE - 1; | ||
| return cast(StringValue*)&pools[idx][off]; | ||
| } | ||
|
|
||
| extern (C++) size_t findSlot(hash_t hash, const(char)* s, size_t length) | ||
| { | ||
| // quadratic probing using triangular numbers | ||
| // http://stackoverflow.com/questions/2348187/moving-from-linear-probing-to-quadratic-probing-hash-collisons/2349774#2349774 | ||
| for (size_t i = hash & (tabledim - 1), j = 1;; ++j) | ||
| { | ||
| StringValue* sv; | ||
| if (!table[i].vptr || table[i].hash == hash && (sv = getValue(table[i].vptr)).length == length && .memcmp(s, sv.lstring(), length) == 0) | ||
| return i; | ||
| i = (i + j) & (tabledim - 1); | ||
| } | ||
| } | ||
|
|
||
| extern (C++) void grow() | ||
| { | ||
| const(size_t) odim = tabledim; | ||
| StringEntry* otab = table; | ||
| tabledim *= 2; | ||
| table = cast(StringEntry*)mem.xcalloc(tabledim, (table[0]).sizeof); | ||
| for (size_t i = 0; i < odim; ++i) | ||
| { | ||
| StringEntry* se = &otab[i]; | ||
| if (!se.vptr) | ||
| continue; | ||
| StringValue* sv = getValue(se.vptr); | ||
| table[findSlot(se.hash, sv.lstring(), sv.length)] = *se; | ||
| } | ||
| mem.xfree(otab); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,175 @@ | ||
| // Compiler implementation of the D programming language | ||
| // Copyright (c) 1999-2015 by Digital Mars | ||
| // All Rights Reserved | ||
| // written by Walter Bright | ||
| // http://www.digitalmars.com | ||
| // Distributed under the Boost Software License, Version 1.0. | ||
| // http://www.boost.org/LICENSE_1_0.txt | ||
|
|
||
| module ddmd.sapply; | ||
|
|
||
| import ddmd.statement, ddmd.visitor; | ||
|
|
||
| /************************************** | ||
| * A Statement tree walker that will visit each Statement s in the tree, | ||
| * in depth-first evaluation order, and call fp(s,param) on it. | ||
| * fp() signals whether the walking continues with its return value: | ||
| * Returns: | ||
| * 0 continue | ||
| * 1 done | ||
| * It's a bit slower than using virtual functions, but more encapsulated and less brittle. | ||
| * Creating an iterator for this would be much more complex. | ||
| */ | ||
| extern (C++) final class PostorderStatementVisitor : StoppableVisitor | ||
| { | ||
| alias visit = super.visit; | ||
| public: | ||
| StoppableVisitor v; | ||
|
|
||
| extern (D) this(StoppableVisitor v) | ||
| { | ||
| this.v = v; | ||
| } | ||
|
|
||
| bool doCond(Statement s) | ||
| { | ||
| if (!stop && s) | ||
| s.accept(this); | ||
| return stop; | ||
| } | ||
|
|
||
| bool applyTo(Statement s) | ||
| { | ||
| s.accept(v); | ||
| stop = v.stop; | ||
| return true; | ||
| } | ||
|
|
||
| void visit(Statement s) | ||
| { | ||
| applyTo(s); | ||
| } | ||
|
|
||
| void visit(PeelStatement s) | ||
| { | ||
| doCond(s.s) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(CompoundStatement s) | ||
| { | ||
| for (size_t i = 0; i < s.statements.dim; i++) | ||
| if (doCond((*s.statements)[i])) | ||
| return; | ||
| applyTo(s); | ||
| } | ||
|
|
||
| void visit(UnrolledLoopStatement s) | ||
| { | ||
| for (size_t i = 0; i < s.statements.dim; i++) | ||
| if (doCond((*s.statements)[i])) | ||
| return; | ||
| applyTo(s); | ||
| } | ||
|
|
||
| void visit(ScopeStatement s) | ||
| { | ||
| doCond(s.statement) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(WhileStatement s) | ||
| { | ||
| doCond(s._body) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(DoStatement s) | ||
| { | ||
| doCond(s._body) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(ForStatement s) | ||
| { | ||
| doCond(s._init) || doCond(s._body) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(ForeachStatement s) | ||
| { | ||
| doCond(s._body) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(ForeachRangeStatement s) | ||
| { | ||
| doCond(s._body) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(IfStatement s) | ||
| { | ||
| doCond(s.ifbody) || doCond(s.elsebody) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(PragmaStatement s) | ||
| { | ||
| doCond(s._body) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(SwitchStatement s) | ||
| { | ||
| doCond(s._body) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(CaseStatement s) | ||
| { | ||
| doCond(s.statement) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(DefaultStatement s) | ||
| { | ||
| doCond(s.statement) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(SynchronizedStatement s) | ||
| { | ||
| doCond(s._body) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(WithStatement s) | ||
| { | ||
| doCond(s._body) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(TryCatchStatement s) | ||
| { | ||
| if (doCond(s._body)) | ||
| return; | ||
| for (size_t i = 0; i < s.catches.dim; i++) | ||
| if (doCond((*s.catches)[i].handler)) | ||
| return; | ||
| applyTo(s); | ||
| } | ||
|
|
||
| void visit(TryFinallyStatement s) | ||
| { | ||
| doCond(s._body) || doCond(s.finalbody) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(OnScopeStatement s) | ||
| { | ||
| doCond(s.statement) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(DebugStatement s) | ||
| { | ||
| doCond(s.statement) || applyTo(s); | ||
| } | ||
|
|
||
| void visit(LabelStatement s) | ||
| { | ||
| doCond(s.statement) || applyTo(s); | ||
| } | ||
| } | ||
|
|
||
| extern (C++) bool walkPostorder(Statement s, StoppableVisitor v) | ||
| { | ||
| scope PostorderStatementVisitor pv = new PostorderStatementVisitor(v); | ||
| s.accept(pv); | ||
| return v.stop; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,342 @@ | ||
| // Compiler implementation of the D programming language | ||
| // Copyright (c) 1999-2015 by Digital Mars | ||
| // All Rights Reserved | ||
| // written by Walter Bright | ||
| // http://www.digitalmars.com | ||
| // Distributed under the Boost Software License, Version 1.0. | ||
| // http://www.boost.org/LICENSE_1_0.txt | ||
|
|
||
| module ddmd.sideeffect; | ||
|
|
||
| import ddmd.apply, ddmd.declaration, ddmd.expression, ddmd.func, ddmd.globals, ddmd.mtype, ddmd.tokens, ddmd.visitor; | ||
|
|
||
| /************************************************** | ||
| * Front-end expression rewriting should create temporary variables for | ||
| * non trivial sub-expressions in order to: | ||
| * 1. save evaluation order | ||
| * 2. prevent sharing of sub-expression in AST | ||
| */ | ||
| extern (C++) bool isTrivialExp(Expression e) | ||
| { | ||
| extern (C++) final class IsTrivialExp : StoppableVisitor | ||
| { | ||
| alias visit = super.visit; | ||
| public: | ||
| extern (D) this() | ||
| { | ||
| } | ||
|
|
||
| void visit(Expression e) | ||
| { | ||
| /* Bugzilla 11201: CallExp is always non trivial expression, | ||
| * especially for inlining. | ||
| */ | ||
| if (e.op == TOKcall) | ||
| { | ||
| stop = true; | ||
| return; | ||
| } | ||
| // stop walking if we determine this expression has side effects | ||
| stop = lambdaHasSideEffect(e); | ||
| } | ||
| } | ||
|
|
||
| scope IsTrivialExp v = new IsTrivialExp(); | ||
| return walkPostorder(e, v) == false; | ||
| } | ||
|
|
||
| /******************************************** | ||
| * Determine if Expression has any side effects. | ||
| */ | ||
| extern (C++) bool hasSideEffect(Expression e) | ||
| { | ||
| extern (C++) final class LambdaHasSideEffect : StoppableVisitor | ||
| { | ||
| alias visit = super.visit; | ||
| public: | ||
| extern (D) this() | ||
| { | ||
| } | ||
|
|
||
| void visit(Expression e) | ||
| { | ||
| // stop walking if we determine this expression has side effects | ||
| stop = lambdaHasSideEffect(e); | ||
| } | ||
| } | ||
|
|
||
| scope LambdaHasSideEffect v = new LambdaHasSideEffect(); | ||
| return walkPostorder(e, v); | ||
| } | ||
|
|
||
| /******************************************** | ||
| * Determine if the call of f, or function type or delegate type t1, has any side effects. | ||
| * Returns: | ||
| * 0 has any side effects | ||
| * 1 nothrow + constant purity | ||
| * 2 nothrow + strong purity | ||
| */ | ||
| extern (C++) int callSideEffectLevel(FuncDeclaration f) | ||
| { | ||
| /* Bugzilla 12760: ctor call always has side effects. | ||
| */ | ||
| if (f.isCtorDeclaration()) | ||
| return 0; | ||
| assert(f.type.ty == Tfunction); | ||
| TypeFunction tf = cast(TypeFunction)f.type; | ||
| if (tf.isnothrow) | ||
| { | ||
| PURE purity = f.isPure(); | ||
| if (purity == PUREstrong) | ||
| return 2; | ||
| if (purity == PUREconst) | ||
| return 1; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| extern (C++) int callSideEffectLevel(Type t) | ||
| { | ||
| t = t.toBasetype(); | ||
| TypeFunction tf; | ||
| if (t.ty == Tdelegate) | ||
| tf = cast(TypeFunction)(cast(TypeDelegate)t).next; | ||
| else | ||
| { | ||
| assert(t.ty == Tfunction); | ||
| tf = cast(TypeFunction)t; | ||
| } | ||
| tf.purityLevel(); | ||
| PURE purity = tf.purity; | ||
| if (t.ty == Tdelegate && purity > PUREweak) | ||
| { | ||
| if (tf.isMutable()) | ||
| purity = PUREweak; | ||
| else if (!tf.isImmutable()) | ||
| purity = PUREconst; | ||
| } | ||
| if (tf.isnothrow) | ||
| { | ||
| if (purity == PUREstrong) | ||
| return 2; | ||
| if (purity == PUREconst) | ||
| return 1; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| extern (C++) bool lambdaHasSideEffect(Expression e) | ||
| { | ||
| switch (e.op) | ||
| { | ||
| // Sort the cases by most frequently used first | ||
| case TOKassign: | ||
| case TOKplusplus: | ||
| case TOKminusminus: | ||
| case TOKdeclaration: | ||
| case TOKconstruct: | ||
| case TOKblit: | ||
| case TOKaddass: | ||
| case TOKminass: | ||
| case TOKcatass: | ||
| case TOKmulass: | ||
| case TOKdivass: | ||
| case TOKmodass: | ||
| case TOKshlass: | ||
| case TOKshrass: | ||
| case TOKushrass: | ||
| case TOKandass: | ||
| case TOKorass: | ||
| case TOKxorass: | ||
| case TOKpowass: | ||
| case TOKin: | ||
| case TOKremove: | ||
| case TOKassert: | ||
| case TOKhalt: | ||
| case TOKdelete: | ||
| case TOKnew: | ||
| case TOKnewanonclass: | ||
| return true; | ||
| case TOKcall: | ||
| { | ||
| CallExp ce = cast(CallExp)e; | ||
| /* Calling a function or delegate that is pure nothrow | ||
| * has no side effects. | ||
| */ | ||
| if (ce.e1.type) | ||
| { | ||
| Type t = ce.e1.type.toBasetype(); | ||
| if (t.ty == Tdelegate) | ||
| t = (cast(TypeDelegate)t).next; | ||
| if (t.ty == Tfunction && (ce.f ? callSideEffectLevel(ce.f) : callSideEffectLevel(ce.e1.type)) > 0) | ||
| { | ||
| } | ||
| else | ||
| return true; | ||
| } | ||
| break; | ||
| } | ||
| case TOKcast: | ||
| { | ||
| CastExp ce = cast(CastExp)e; | ||
| /* if: | ||
| * cast(classtype)func() // because it may throw | ||
| */ | ||
| if (ce.to.ty == Tclass && ce.e1.op == TOKcall && ce.e1.type.ty == Tclass) | ||
| return true; | ||
| break; | ||
| } | ||
| default: | ||
| break; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| /*********************************** | ||
| * The result of this expression will be discarded. | ||
| * Complain if the operation has no side effects (and hence is meaningless). | ||
| */ | ||
| extern (C++) void discardValue(Expression e) | ||
| { | ||
| if (lambdaHasSideEffect(e)) // check side-effect shallowly | ||
| return; | ||
| switch (e.op) | ||
| { | ||
| case TOKcast: | ||
| { | ||
| CastExp ce = cast(CastExp)e; | ||
| if (ce.to.equals(Type.tvoid)) | ||
| { | ||
| /* | ||
| * Don't complain about an expression with no effect if it was cast to void | ||
| */ | ||
| return; | ||
| } | ||
| break; | ||
| // complain | ||
| } | ||
| case TOKerror: | ||
| return; | ||
| case TOKvar: | ||
| { | ||
| VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration(); | ||
| if (v && (v.storage_class & STCtemp)) | ||
| { | ||
| // Bugzilla 5810: Don't complain about an internal generated variable. | ||
| return; | ||
| } | ||
| break; | ||
| } | ||
| case TOKcall: | ||
| /* Issue 3882: */ | ||
| if (global.params.warnings && !global.gag) | ||
| { | ||
| CallExp ce = cast(CallExp)e; | ||
| if (e.type.ty == Tvoid) | ||
| { | ||
| /* Don't complain about calling void-returning functions with no side-effect, | ||
| * because purity and nothrow are inferred, and because some of the | ||
| * runtime library depends on it. Needs more investigation. | ||
| * | ||
| * One possible solution is to restrict this message to only be called in hierarchies that | ||
| * never call assert (and or not called from inside unittest blocks) | ||
| */ | ||
| } | ||
| else if (ce.e1.type) | ||
| { | ||
| Type t = ce.e1.type.toBasetype(); | ||
| if (t.ty == Tdelegate) | ||
| t = (cast(TypeDelegate)t).next; | ||
| if (t.ty == Tfunction && (ce.f ? callSideEffectLevel(ce.f) : callSideEffectLevel(ce.e1.type)) > 0) | ||
| { | ||
| const(char)* s; | ||
| if (ce.f) | ||
| s = ce.f.toPrettyChars(); | ||
| else if (ce.e1.op == TOKstar) | ||
| { | ||
| // print 'fp' if ce->e1 is (*fp) | ||
| s = (cast(PtrExp)ce.e1).e1.toChars(); | ||
| } | ||
| else | ||
| s = ce.e1.toChars(); | ||
| e.warning("calling %s without side effects discards return value of type %s, prepend a cast(void) if intentional", s, e.type.toChars()); | ||
| } | ||
| } | ||
| } | ||
| return; | ||
| case TOKimport: | ||
| e.error("%s has no effect", e.toChars()); | ||
| return; | ||
| case TOKandand: | ||
| { | ||
| AndAndExp aae = cast(AndAndExp)e; | ||
| discardValue(aae.e2); | ||
| return; | ||
| } | ||
| case TOKoror: | ||
| { | ||
| OrOrExp ooe = cast(OrOrExp)e; | ||
| discardValue(ooe.e2); | ||
| return; | ||
| } | ||
| case TOKquestion: | ||
| { | ||
| CondExp ce = cast(CondExp)e; | ||
| /* Bugzilla 6178 & 14089: Either CondExp::e1 or e2 may have | ||
| * redundant expression to make those types common. For example: | ||
| * | ||
| * struct S { this(int n); int v; alias v this; } | ||
| * S[int] aa; | ||
| * aa[1] = 0; | ||
| * | ||
| * The last assignment statement will be rewitten to: | ||
| * | ||
| * 1 in aa ? aa[1].value = 0 : (aa[1] = 0, aa[1].this(0)).value; | ||
| * | ||
| * The last DotVarExp is necessary to take assigned value. | ||
| * | ||
| * int value = (aa[1] = 0); // value = aa[1].value | ||
| * | ||
| * To avoid false error, discardValue() should be called only when | ||
| * the both tops of e1 and e2 have actually no side effects. | ||
| */ | ||
| if (!lambdaHasSideEffect(ce.e1) && !lambdaHasSideEffect(ce.e2)) | ||
| { | ||
| discardValue(ce.e1); | ||
| discardValue(ce.e2); | ||
| } | ||
| return; | ||
| } | ||
| case TOKcomma: | ||
| { | ||
| CommaExp ce = cast(CommaExp)e; | ||
| /* Check for compiler-generated code of the form auto __tmp, e, __tmp; | ||
| * In such cases, only check e for side effect (it's OK for __tmp to have | ||
| * no side effect). | ||
| * See Bugzilla 4231 for discussion | ||
| */ | ||
| CommaExp firstComma = ce; | ||
| while (firstComma.e1.op == TOKcomma) | ||
| firstComma = cast(CommaExp)firstComma.e1; | ||
| if (firstComma.e1.op == TOKdeclaration && ce.e2.op == TOKvar && (cast(DeclarationExp)firstComma.e1).declaration == (cast(VarExp)ce.e2).var) | ||
| { | ||
| return; | ||
| } | ||
| // Don't check e1 until we cast(void) the a,b code generation | ||
| //discardValue(ce->e1); | ||
| discardValue(ce.e2); | ||
| return; | ||
| } | ||
| case TOKtuple: | ||
| /* Pass without complaint if any of the tuple elements have side effects. | ||
| * Ideally any tuple elements with no side effects should raise an error, | ||
| * this needs more investigation as to what is the right thing to do. | ||
| */ | ||
| if (!hasSideEffect(e)) | ||
| break; | ||
| return; | ||
| default: | ||
| break; | ||
| } | ||
| e.error("%s has no effect in expression (%s)", Token.toChars(e.op), e.toChars()); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| // Compiler implementation of the D programming language | ||
| // Copyright (c) 1999-2015 by Digital Mars | ||
| // All Rights Reserved | ||
| // written by Walter Bright | ||
| // http://www.digitalmars.com | ||
| // Distributed under the Boost Software License, Version 1.0. | ||
| // http://www.boost.org/LICENSE_1_0.txt | ||
|
|
||
| module ddmd.staticassert; | ||
|
|
||
| import ddmd.dscope, ddmd.dsymbol, ddmd.errors, ddmd.expression, ddmd.globals, ddmd.globals, ddmd.hdrgen, ddmd.id, ddmd.identifier, ddmd.mtype, ddmd.root.outbuffer, ddmd.visitor; | ||
|
|
||
| extern (C++) final class StaticAssert : Dsymbol | ||
| { | ||
| public: | ||
| Expression exp; | ||
| Expression msg; | ||
|
|
||
| /********************************* AttribDeclaration ****************************/ | ||
| extern (D) this(Loc loc, Expression exp, Expression msg) | ||
| { | ||
| super(Id.empty); | ||
| this.loc = loc; | ||
| this.exp = exp; | ||
| this.msg = msg; | ||
| } | ||
|
|
||
| Dsymbol syntaxCopy(Dsymbol s) | ||
| { | ||
| assert(!s); | ||
| return new StaticAssert(loc, exp.syntaxCopy(), msg ? msg.syntaxCopy() : null); | ||
| } | ||
|
|
||
| void addMember(Scope* sc, ScopeDsymbol sds) | ||
| { | ||
| // we didn't add anything | ||
| } | ||
|
|
||
| void semantic(Scope* sc) | ||
| { | ||
| } | ||
|
|
||
| void semantic2(Scope* sc) | ||
| { | ||
| //printf("StaticAssert::semantic2() %s\n", toChars()); | ||
| auto sds = new ScopeDsymbol(); | ||
| sc = sc.push(sds); | ||
| sc.tinst = null; | ||
| sc.minst = null; | ||
| sc.flags |= SCOPEcondition; | ||
| sc = sc.startCTFE(); | ||
| Expression e = exp.semantic(sc); | ||
| e = resolveProperties(sc, e); | ||
| sc = sc.endCTFE(); | ||
| sc = sc.pop(); | ||
| // Simplify expression, to make error messages nicer if CTFE fails | ||
| e = e.optimize(WANTvalue); | ||
| if (!e.type.isBoolean()) | ||
| { | ||
| if (e.type.toBasetype() != Type.terror) | ||
| exp.error("expression %s of type %s does not have a boolean value", exp.toChars(), e.type.toChars()); | ||
| return; | ||
| } | ||
| uint olderrs = global.errors; | ||
| e = e.ctfeInterpret(); | ||
| if (global.errors != olderrs) | ||
| { | ||
| errorSupplemental(loc, "while evaluating: static assert(%s)", exp.toChars()); | ||
| } | ||
| else if (e.isBool(false)) | ||
| { | ||
| if (msg) | ||
| { | ||
| sc = sc.startCTFE(); | ||
| msg = msg.semantic(sc); | ||
| msg = resolveProperties(sc, msg); | ||
| sc = sc.endCTFE(); | ||
| msg = msg.ctfeInterpret(); | ||
| if (StringExp se = msg.toStringExp()) | ||
| { | ||
| // same with pragma(msg) | ||
| se = se.toUTF8(sc); | ||
| error("\"%.*s\"", cast(int)se.len, cast(char*)se.string); | ||
| } | ||
| else | ||
| error("%s", msg.toChars()); | ||
| } | ||
| else | ||
| error("(%s) is false", exp.toChars()); | ||
| if (sc.tinst) | ||
| sc.tinst.printInstantiationTrace(); | ||
| if (!global.gag) | ||
| fatal(); | ||
| } | ||
| else if (!e.isBool(true)) | ||
| { | ||
| error("(%s) is not evaluatable at compile time", exp.toChars()); | ||
| } | ||
| } | ||
|
|
||
| bool oneMember(Dsymbol* ps, Identifier ident) | ||
| { | ||
| //printf("StaticAssert::oneMember())\n"); | ||
| *ps = null; | ||
| return true; | ||
| } | ||
|
|
||
| const(char)* kind() | ||
| { | ||
| return "static assert"; | ||
| } | ||
|
|
||
| void accept(Visitor v) | ||
| { | ||
| v.visit(this); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,366 @@ | ||
| // Compiler implementation of the D programming language | ||
| // Copyright (c) 1999-2015 by Digital Mars | ||
| // All Rights Reserved | ||
| // written by Walter Bright | ||
| // http://www.digitalmars.com | ||
| // Distributed under the Boost Software License, Version 1.0. | ||
| // http://www.boost.org/LICENSE_1_0.txt | ||
|
|
||
| module ddmd.target; | ||
|
|
||
| import core.stdc.string; | ||
| import ddmd.dmodule, ddmd.expression, ddmd.globals, ddmd.identifier, ddmd.mtype, ddmd.root.longdouble, ddmd.root.outbuffer; | ||
|
|
||
| struct Target | ||
| { | ||
| extern (C++) static __gshared int ptrsize; | ||
| extern (C++) static __gshared int realsize; // size a real consumes in memory | ||
| extern (C++) static __gshared int realpad; // 'padding' added to the CPU real size to bring it up to realsize | ||
| extern (C++) static __gshared int realalignsize; // alignment for reals | ||
| extern (C++) static __gshared bool reverseCppOverloads; // with dmc, overloaded functions are grouped and in reverse order | ||
| extern (C++) static __gshared int c_longsize; // size of a C 'long' or 'unsigned long' type | ||
| extern (C++) static __gshared int c_long_doublesize; // size of a C 'long double' | ||
| extern (C++) static __gshared int classinfosize; // size of 'ClassInfo' | ||
|
|
||
| extern (C++) static void _init() | ||
| { | ||
| // These have default values for 32 bit code, they get | ||
| // adjusted for 64 bit code. | ||
| ptrsize = 4; | ||
| classinfosize = 0x4C; // 76 | ||
| if (global.params.isLP64) | ||
| { | ||
| ptrsize = 8; | ||
| classinfosize = 0x98; // 152 | ||
| } | ||
| if (global.params.isLinux || global.params.isFreeBSD || global.params.isOpenBSD || global.params.isSolaris) | ||
| { | ||
| realsize = 12; | ||
| realpad = 2; | ||
| realalignsize = 4; | ||
| c_longsize = 4; | ||
| } | ||
| else if (global.params.isOSX) | ||
| { | ||
| realsize = 16; | ||
| realpad = 6; | ||
| realalignsize = 16; | ||
| c_longsize = 4; | ||
| } | ||
| else if (global.params.isWindows) | ||
| { | ||
| realsize = 10; | ||
| realpad = 0; | ||
| realalignsize = 2; | ||
| reverseCppOverloads = !global.params.is64bit; | ||
| c_longsize = 4; | ||
| } | ||
| else | ||
| assert(0); | ||
| if (global.params.is64bit) | ||
| { | ||
| if (global.params.isLinux || global.params.isFreeBSD || global.params.isSolaris) | ||
| { | ||
| realsize = 16; | ||
| realpad = 6; | ||
| realalignsize = 16; | ||
| c_longsize = 8; | ||
| } | ||
| else if (global.params.isOSX) | ||
| { | ||
| c_longsize = 8; | ||
| } | ||
| } | ||
| c_long_doublesize = realsize; | ||
| if (global.params.is64bit && global.params.isWindows) | ||
| c_long_doublesize = 8; | ||
| } | ||
|
|
||
| /****************************** | ||
| * Return memory alignment size of type. | ||
| */ | ||
| extern (C++) static uint alignsize(Type type) | ||
| { | ||
| assert(type.isTypeBasic()); | ||
| switch (type.ty) | ||
| { | ||
| case Tfloat80: | ||
| case Timaginary80: | ||
| case Tcomplex80: | ||
| return Target.realalignsize; | ||
| case Tcomplex32: | ||
| if (global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isOpenBSD || global.params.isSolaris) | ||
| return 4; | ||
| break; | ||
| case Tint64: | ||
| case Tuns64: | ||
| case Tfloat64: | ||
| case Timaginary64: | ||
| case Tcomplex64: | ||
| if (global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isOpenBSD || global.params.isSolaris) | ||
| return global.params.is64bit ? 8 : 4; | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| return cast(uint)type.size(Loc()); | ||
| } | ||
|
|
||
| /****************************** | ||
| * Return field alignment size of type. | ||
| */ | ||
| extern (C++) static uint fieldalign(Type type) | ||
| { | ||
| return type.alignsize(); | ||
| } | ||
|
|
||
| /*********************************** | ||
| * Return size of OS critical section. | ||
| * NOTE: can't use the sizeof() calls directly since cross compiling is | ||
| * supported and would end up using the host sizes rather than the target | ||
| * sizes. | ||
| */ | ||
| extern (C++) static uint critsecsize() | ||
| { | ||
| if (global.params.isWindows) | ||
| { | ||
| // sizeof(CRITICAL_SECTION) for Windows. | ||
| return global.params.isLP64 ? 40 : 24; | ||
| } | ||
| else if (global.params.isLinux) | ||
| { | ||
| // sizeof(pthread_mutex_t) for Linux. | ||
| if (global.params.is64bit) | ||
| return global.params.isLP64 ? 40 : 32; | ||
| else | ||
| return global.params.isLP64 ? 40 : 24; | ||
| } | ||
| else if (global.params.isFreeBSD) | ||
| { | ||
| // sizeof(pthread_mutex_t) for FreeBSD. | ||
| return global.params.isLP64 ? 8 : 4; | ||
| } | ||
| else if (global.params.isOpenBSD) | ||
| { | ||
| // sizeof(pthread_mutex_t) for OpenBSD. | ||
| return global.params.isLP64 ? 8 : 4; | ||
| } | ||
| else if (global.params.isOSX) | ||
| { | ||
| // sizeof(pthread_mutex_t) for OSX. | ||
| return global.params.isLP64 ? 64 : 44; | ||
| } | ||
| else if (global.params.isSolaris) | ||
| { | ||
| // sizeof(pthread_mutex_t) for Solaris. | ||
| return 24; | ||
| } | ||
| assert(0); | ||
| return 0; | ||
| } | ||
|
|
||
| /*********************************** | ||
| * Returns a Type for the va_list type of the target. | ||
| * NOTE: For Posix/x86_64 this returns the type which will really | ||
| * be used for passing an argument of type va_list. | ||
| */ | ||
| extern (C++) static Type va_listType() | ||
| { | ||
| if (global.params.isWindows) | ||
| { | ||
| return Type.tchar.pointerTo(); | ||
| } | ||
| else if (global.params.isLinux || global.params.isFreeBSD || global.params.isOpenBSD || global.params.isSolaris || global.params.isOSX) | ||
| { | ||
| if (global.params.is64bit) | ||
| { | ||
| return (new TypeIdentifier(Loc(), Identifier.idPool("__va_list_tag"))).pointerTo(); | ||
| } | ||
| else | ||
| { | ||
| return Type.tchar.pointerTo(); | ||
| } | ||
| } | ||
| else | ||
| { | ||
| assert(0); | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| /* | ||
| * Return true if the given type is supported for this target | ||
| */ | ||
| extern (C++) static int checkVectorType(int sz, Type type) | ||
| { | ||
| if (!global.params.is64bit && !global.params.isOSX) | ||
| return 1; // not supported | ||
| if (sz != 16 && sz != 32) | ||
| return 2; // wrong size | ||
| switch (type.ty) | ||
| { | ||
| case Tvoid: | ||
| case Tint8: | ||
| case Tuns8: | ||
| case Tint16: | ||
| case Tuns16: | ||
| case Tint32: | ||
| case Tuns32: | ||
| case Tfloat32: | ||
| case Tint64: | ||
| case Tuns64: | ||
| case Tfloat64: | ||
| break; | ||
| default: | ||
| return 3; // wrong base type | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| /****************************** | ||
| * Encode the given expression, which is assumed to be an rvalue literal | ||
| * as another type for use in CTFE. | ||
| * This corresponds roughly to the idiom *(Type *)&e. | ||
| */ | ||
| extern (C++) static Expression paintAsType(Expression e, Type type) | ||
| { | ||
| // We support up to 512-bit values. | ||
| ubyte[64] buffer; | ||
| memset(buffer.ptr, 0, buffer.sizeof); | ||
| assert(e.type.size() == type.size()); | ||
| // Write the expression into the buffer. | ||
| switch (e.type.ty) | ||
| { | ||
| case Tint32: | ||
| case Tuns32: | ||
| case Tint64: | ||
| case Tuns64: | ||
| encodeInteger(e, buffer.ptr); | ||
| break; | ||
| case Tfloat32: | ||
| case Tfloat64: | ||
| encodeReal(e, buffer.ptr); | ||
| break; | ||
| default: | ||
| assert(0); | ||
| } | ||
| // Interpret the buffer as a new type. | ||
| switch (type.ty) | ||
| { | ||
| case Tint32: | ||
| case Tuns32: | ||
| case Tint64: | ||
| case Tuns64: | ||
| return decodeInteger(e.loc, type, buffer.ptr); | ||
| case Tfloat32: | ||
| case Tfloat64: | ||
| return decodeReal(e.loc, type, buffer.ptr); | ||
| default: | ||
| assert(0); | ||
| } | ||
| return null; // avoid warning | ||
| } | ||
|
|
||
| /****************************** | ||
| * For the given module, perform any post parsing analysis. | ||
| * Certain compiler backends (ie: GDC) have special placeholder | ||
| * modules whose source are empty, but code gets injected | ||
| * immediately after loading. | ||
| */ | ||
| extern (C++) static void loadModule(Module m) | ||
| { | ||
| } | ||
|
|
||
| /****************************** | ||
| * For the given symbol written to the OutBuffer, apply any | ||
| * target-specific prefixes based on the given linkage. | ||
| */ | ||
| extern (C++) static void prefixName(OutBuffer* buf, LINK linkage) | ||
| { | ||
| switch (linkage) | ||
| { | ||
| case LINKcpp: | ||
| if (global.params.isOSX) | ||
| buf.prependbyte('_'); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /****************************** | ||
| * Private helpers for Target::paintAsType. | ||
| */ | ||
| // Write the integer value of 'e' into a unsigned byte buffer. | ||
| extern (C++) static void encodeInteger(Expression e, ubyte* buffer) | ||
| { | ||
| dinteger_t value = e.toInteger(); | ||
| int size = cast(int)e.type.size(); | ||
| for (int p = 0; p < size; p++) | ||
| { | ||
| int offset = p; // Would be (size - 1) - p; on BigEndian | ||
| buffer[offset] = ((value >> (p * 8)) & 0xFF); | ||
| } | ||
| } | ||
|
|
||
| // Write the bytes encoded in 'buffer' into an integer and returns | ||
| // the value as a new IntegerExp. | ||
| extern (C++) static Expression decodeInteger(Loc loc, Type type, ubyte* buffer) | ||
| { | ||
| dinteger_t value = 0; | ||
| int size = cast(int)type.size(); | ||
| for (int p = 0; p < size; p++) | ||
| { | ||
| int offset = p; // Would be (size - 1) - p; on BigEndian | ||
| value |= (cast(dinteger_t)buffer[offset] << (p * 8)); | ||
| } | ||
| return new IntegerExp(loc, value, type); | ||
| } | ||
|
|
||
| // Write the real value of 'e' into a unsigned byte buffer. | ||
| extern (C++) static void encodeReal(Expression e, ubyte* buffer) | ||
| { | ||
| switch (e.type.ty) | ||
| { | ||
| case Tfloat32: | ||
| { | ||
| float* p = cast(float*)buffer; | ||
| *p = cast(float)e.toReal(); | ||
| break; | ||
| } | ||
| case Tfloat64: | ||
| { | ||
| double* p = cast(double*)buffer; | ||
| *p = cast(double)e.toReal(); | ||
| break; | ||
| } | ||
| default: | ||
| assert(0); | ||
| } | ||
| } | ||
|
|
||
| // Write the bytes encoded in 'buffer' into a longdouble and returns | ||
| // the value as a new RealExp. | ||
| extern (C++) static Expression decodeReal(Loc loc, Type type, ubyte* buffer) | ||
| { | ||
| real value; | ||
| switch (type.ty) | ||
| { | ||
| case Tfloat32: | ||
| { | ||
| float* p = cast(float*)buffer; | ||
| value = ldouble(*p); | ||
| break; | ||
| } | ||
| case Tfloat64: | ||
| { | ||
| double* p = cast(double*)buffer; | ||
| value = ldouble(*p); | ||
| break; | ||
| } | ||
| default: | ||
| assert(0); | ||
| } | ||
| return new RealExp(loc, value, type); | ||
| } |