From 6e60e06e5b44395b8a30567b213c601b8f66286c Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sun, 23 Aug 2015 19:02:14 +0200 Subject: [PATCH 1/6] Fix VC mangling for class reference/pointer --- src/cppmangle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cppmangle.c b/src/cppmangle.c index 73d80a2fd24a..de0dd7bacefe 100644 --- a/src/cppmangle.c +++ b/src/cppmangle.c @@ -1436,7 +1436,7 @@ class VisualCPPMangler : public Visitor t->accept(this); - if ((t->ty == Tpointer || t->ty == Treference) && global.params.is64bit) + if ((t->ty == Tpointer || t->ty == Treference || t->ty == Tclass) && global.params.is64bit) { buf.writeByte('E'); } From bdd7d5bf7b5dd148ab0dd0087b2754b0b6867ce7 Mon Sep 17 00:00:00 2001 From: k-hara Date: Wed, 19 Aug 2015 14:13:05 +0900 Subject: [PATCH 2/6] fix Issue 14923 - ICE: Assertion failed: (tret->ty != Tvoid), function semantic3, file func.c, line 1736. --- src/statement.c | 14 ++++++++++---- test/fail_compilation/ice14923.d | 28 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 test/fail_compilation/ice14923.d diff --git a/src/statement.c b/src/statement.c index 5e9bf2f7bb5e..d02bcca21803 100644 --- a/src/statement.c +++ b/src/statement.c @@ -4034,11 +4034,16 @@ Statement *ReturnStatement::semantic(Scope *sc) { if (tf->next && tf->next->ty != Tvoid) { - error("mismatched function return type inference of void and %s", - tf->next->toChars()); + if (tf->next->ty != Terror) + { + error("mismatched function return type inference of void and %s", + tf->next->toChars()); + } errors = true; + tf->next = Type::terror; } - tf->next = Type::tvoid; + else + tf->next = Type::tvoid; tret = tf->next; tbret = tret->toBasetype(); @@ -4049,7 +4054,8 @@ Statement *ReturnStatement::semantic(Scope *sc) if (tbret->ty != Tvoid) // if non-void return { - error("return expression expected"); + if (tbret->ty != Terror) + error("return expression expected"); errors = true; } else if (fd->isMain()) diff --git a/test/fail_compilation/ice14923.d b/test/fail_compilation/ice14923.d new file mode 100644 index 000000000000..86e3b2a20716 --- /dev/null +++ b/test/fail_compilation/ice14923.d @@ -0,0 +1,28 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice14923.d(21): Error: function ice14923.parse (C a) is not callable using argument types (A) +fail_compilation/ice14923.d(21): instantiated from here: bar!((b) => parse(b)) +--- +*/ + +auto bar(alias fun)() +{ + size_t counter; + scope(exit) counter++; + + Object a2; + if (auto ai = cast(A)a2) return fun(ai); + if (auto ai = cast(B)a2) return fun(ai); +} + +void parse(C a) +{ + bar!(b => parse(b))(); +} + +class A {} + +class C {} + +class B : C {} From 3e3fdfe12e49815f3a78659266b2a5aad737ec79 Mon Sep 17 00:00:00 2001 From: k-hara Date: Wed, 19 Aug 2015 13:55:35 +0900 Subject: [PATCH 3/6] fix Issue 14929 - ICE: Assertion failure: 'ez->exp && ez->exp->op == TOKconstruct' on line 302 in file 'escape.c' If error happens, propagate it without storing. --- src/declaration.c | 19 +++++-- src/expression.c | 1 + src/nogc.c | 7 +-- test/fail_compilation/ice14929.d | 95 ++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 test/fail_compilation/ice14929.d diff --git a/src/declaration.c b/src/declaration.c index 92b3b275667c..b3fbb78f2b1f 100644 --- a/src/declaration.c +++ b/src/declaration.c @@ -1392,17 +1392,26 @@ void VarDeclaration::semantic(Scope *sc) init = ei; } + Expression *exp = ei->exp; Expression *e1 = new VarExp(loc, this); if (isBlit) - ei->exp = new BlitExp(loc, e1, ei->exp); + exp = new BlitExp(loc, e1, exp); else - ei->exp = new ConstructExp(loc, e1, ei->exp); + exp = new ConstructExp(loc, e1, exp); canassign++; - ei->exp = ei->exp->semantic(sc); + exp = exp->semantic(sc); canassign--; - ei->exp->optimize(WANTvalue); + exp = exp->optimize(WANTvalue); - if (isScope()) + if (exp->op == TOKerror) + { + init = new ErrorInitializer(); + ei = NULL; + } + else + ei->exp = exp; + + if (ei && isScope()) { Expression *ex = ei->exp; while (ex->op == TOKcomma) diff --git a/src/expression.c b/src/expression.c index e72f92dff423..b64e5f150668 100644 --- a/src/expression.c +++ b/src/expression.c @@ -6035,6 +6035,7 @@ Expression *DeclarationExp::semantic(Scope *sc) declaration->semantic3(sc); } } + // todo: error in declaration should be propagated. type = Type::tvoid; return this; diff --git a/src/nogc.c b/src/nogc.c index 7d254db58b02..3854bb862e9f 100644 --- a/src/nogc.c +++ b/src/nogc.c @@ -65,13 +65,8 @@ class NOGCVisitor : public StoppableVisitor VarDeclaration *v = e->declaration->isVarDeclaration(); if (v && !(v->storage_class & STCmanifest) && !v->isDataseg() && v->init) { - if (v->init->isVoidInitializer()) + if (ExpInitializer *ei = v->init->isExpInitializer()) { - } - else - { - ExpInitializer *ei = v->init->isExpInitializer(); - assert(ei); doCond(ei->exp); } } diff --git a/test/fail_compilation/ice14929.d b/test/fail_compilation/ice14929.d new file mode 100644 index 000000000000..7bd4fd3aa536 --- /dev/null +++ b/test/fail_compilation/ice14929.d @@ -0,0 +1,95 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice14929.d(44): Error: cast(Node)(*this.current).items[this.index] is not an lvalue +fail_compilation/ice14929.d(87): Error: template instance ice14929.HashMap!(ulong, int).HashMap.opBinaryRight!"in" error instantiating +fail_compilation/ice14929.d(91): instantiated from here: HashmapComponentStorage!int +fail_compilation/ice14929.d(87): Error: rvalue of in expression must be an associative array, not HashMap!(ulong, int) +fail_compilation/ice14929.d(91): Error: template instance ice14929.isComponentStorage!(HashmapComponentStorage!int, int) error instantiating +--- +*/ +struct HashMap(K, V) +{ + V* opBinaryRight(string op)(K key) const if (op == "in") + { + size_t index; + foreach (ref node; buckets[index].range) + { + return &(node.value); + } + return null; + } + + struct Node + { + K key; + V value; + } + + alias Bucket = UnrolledList!(Node); + Bucket[] buckets; +} + +struct UnrolledList(T) +{ + Range range() const pure + { + return Range(_front); + } + + static struct Range + { + ref T front() const @property + { + return cast(T) current.items[index]; + } + void popFront() pure + { + } + bool empty() const pure @property + { + return true; + } + const(Node)* current; + size_t index; + } + + Node* _front; + + static struct Node + { + ContainerStorageType!T[10] items; + } +} + +template ContainerStorageType(T) +{ + alias ContainerStorageType = T; +} + +template isComponentStorage(CS, C) +{ + enum bool isComponentStorage = is(typeof( + (inout int = 0) + { + CS cs = CS.init; + ulong eid; + cs.add(eid, c); + })); +} + +struct HashmapComponentStorage(ComponentType) +{ + private HashMap!(ulong, ComponentType) components; + + void add(ulong eid, ComponentType component) + { + assert(eid !in components); + } +} + +static assert(isComponentStorage!(HashmapComponentStorage!int, int)); + +void main() +{ +} From cb72272c16a607486097eb9b6bdefa985ff65ac5 Mon Sep 17 00:00:00 2001 From: k-hara Date: Wed, 19 Aug 2015 13:55:52 +0900 Subject: [PATCH 4/6] Fix error propagation to improve error diagnostic If an operator overloading member function fails to compile in its `semantic3`, `op_overload` should return `ErrorExp`. --- src/opover.c | 14 +++++++------- test/fail_compilation/ice14929.d | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/opover.c b/src/opover.c index 083bf4ee3301..f67c7d6ba50a 100644 --- a/src/opover.c +++ b/src/opover.c @@ -680,7 +680,7 @@ Expression *op_overload(Expression *e, Scope *sc) if (s) { functionResolve(&m, s, e->loc, sc, tiargs, e->e1->type, &args2); - if (m.lastf && m.lastf->errors) + if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors)) { result = new ErrorExp(); return; @@ -692,7 +692,7 @@ Expression *op_overload(Expression *e, Scope *sc) if (s_r) { functionResolve(&m, s_r, e->loc, sc, tiargs, e->e2->type, &args1); - if (m.lastf && m.lastf->errors) + if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors)) { result = new ErrorExp(); return; @@ -777,7 +777,7 @@ Expression *op_overload(Expression *e, Scope *sc) if (s_r) { functionResolve(&m, s_r, e->loc, sc, tiargs, e->e1->type, &args2); - if (m.lastf && m.lastf->errors) + if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors)) { result = new ErrorExp(); return; @@ -789,7 +789,7 @@ Expression *op_overload(Expression *e, Scope *sc) if (s) { functionResolve(&m, s, e->loc, sc, tiargs, e->e2->type, &args1); - if (m.lastf && m.lastf->errors) + if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors)) { result = new ErrorExp(); return; @@ -1173,7 +1173,7 @@ Expression *op_overload(Expression *e, Scope *sc) if (s) { functionResolve(&m, s, e->loc, sc, tiargs, e->e1->type, &args2); - if (m.lastf && m.lastf->errors) + if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors)) { result = new ErrorExp(); return; @@ -1303,7 +1303,7 @@ Expression *compare_overload(BinExp *e, Scope *sc, Identifier *id) if (s) { functionResolve(&m, s, e->loc, sc, tiargs, e->e1->type, &args2); - if (m.lastf && m.lastf->errors) + if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors)) return new ErrorExp(); } @@ -1313,7 +1313,7 @@ Expression *compare_overload(BinExp *e, Scope *sc, Identifier *id) if (s_r) { functionResolve(&m, s_r, e->loc, sc, tiargs, e->e2->type, &args1); - if (m.lastf && m.lastf->errors) + if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors)) return new ErrorExp(); } diff --git a/test/fail_compilation/ice14929.d b/test/fail_compilation/ice14929.d index 7bd4fd3aa536..b5da262aa5c2 100644 --- a/test/fail_compilation/ice14929.d +++ b/test/fail_compilation/ice14929.d @@ -4,10 +4,10 @@ TEST_OUTPUT: fail_compilation/ice14929.d(44): Error: cast(Node)(*this.current).items[this.index] is not an lvalue fail_compilation/ice14929.d(87): Error: template instance ice14929.HashMap!(ulong, int).HashMap.opBinaryRight!"in" error instantiating fail_compilation/ice14929.d(91): instantiated from here: HashmapComponentStorage!int -fail_compilation/ice14929.d(87): Error: rvalue of in expression must be an associative array, not HashMap!(ulong, int) fail_compilation/ice14929.d(91): Error: template instance ice14929.isComponentStorage!(HashmapComponentStorage!int, int) error instantiating --- */ + struct HashMap(K, V) { V* opBinaryRight(string op)(K key) const if (op == "in") From e891688d80a398be2fc41366184cd26e8229406e Mon Sep 17 00:00:00 2001 From: k-hara Date: Fri, 28 Aug 2015 11:48:50 +0900 Subject: [PATCH 5/6] fix Issue 14973 - compiler inference of contexts for nested map seems broken --- src/declaration.c | 54 +++++++++++++++++++++---------- src/expression.c | 11 +++++++ src/func.c | 52 +++++++++++++++++++++-------- test/compilable/test14973.d | 54 +++++++++++++++++++++++++++++++ test/fail_compilation/diag9831.d | 2 +- test/fail_compilation/fail11545.d | 4 +-- test/fail_compilation/fail120.d | 4 +-- test/fail_compilation/fail39.d | 2 +- 8 files changed, 146 insertions(+), 37 deletions(-) create mode 100644 test/compilable/test14973.d diff --git a/src/declaration.c b/src/declaration.c index b3fbb78f2b1f..4be965ffa900 100644 --- a/src/declaration.c +++ b/src/declaration.c @@ -1806,16 +1806,46 @@ bool lambdaCheckForNestedRef(Expression *e, Scope *sc); bool VarDeclaration::checkNestedReference(Scope *sc, Loc loc) { //printf("VarDeclaration::checkNestedReference() %s\n", toChars()); - if (parent && parent != sc->parent && - !isDataseg() && !(storage_class & STCmanifest) && - sc->intypeof != 1 && !(sc->flags & SCOPEctfe)) + if (sc->intypeof == 1 || (sc->flags & SCOPEctfe)) + return false; + if (!parent || parent == sc->parent) + return false; + if (isDataseg() || (storage_class & STCmanifest)) + return false; + + // The current function + FuncDeclaration *fdthis = sc->parent->isFuncDeclaration(); + if (!fdthis) + return false; // out of function scope + + Dsymbol *p = toParent2(); + + // Function literals from fdthis to p must be delegates + // TODO: here is similar to checkFrameAccess. + for (Dsymbol *s = fdthis; s && s != p; s = s->toParent2()) + { + // function literal has reference to enclosing scope is delegate + if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration()) + fld->tok = TOKdelegate; + + if (FuncDeclaration *fd = s->isFuncDeclaration()) + { + if (!fd->isThis() && !fd->isNested()) + break; + } + if (AggregateDeclaration *ad2 = s->isAggregateDeclaration()) + { + if (ad2->storage_class & STCstatic) + break; + } + } + + if (1) { // The function that this variable is in - FuncDeclaration *fdv = toParent()->isFuncDeclaration(); - // The current function - FuncDeclaration *fdthis = sc->parent->isFuncDeclaration(); + FuncDeclaration *fdv = p->isFuncDeclaration(); - if (fdv && fdthis && fdv != fdthis) + if (fdv && fdv != fdthis) { // Add fdthis to nestedrefs[] if not already there for (size_t i = 0; 1; i++) @@ -1875,16 +1905,6 @@ bool VarDeclaration::checkNestedReference(Scope *sc, Loc loc) } } - // Function literals from fdthis to fdv must be delegates - for (Dsymbol *s = fdthis; s && s != fdv; s = s->toParent2()) - { - // function literal has reference to enclosing scope is delegate - if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration()) - { - fld->tok = TOKdelegate; - } - } - // Add this to fdv->closureVars[] if not already there for (size_t i = 0; 1; i++) { diff --git a/src/expression.c b/src/expression.c index b64e5f150668..9c79ffa62d97 100644 --- a/src/expression.c +++ b/src/expression.c @@ -3359,6 +3359,10 @@ Expression *DsymbolExp::semantic(Scope *sc) v->semantic(v->scope); s = v->toAlias(); // Need this if 'v' is a tuple variable } + + // Change the ancestor lambdas to delegate before hasThis(sc) call. + if (v->checkNestedReference(sc, loc)) + return new ErrorExp(); } if (s->needThis() && hasThis(sc)) { @@ -5310,6 +5314,9 @@ Expression *VarExp::semantic(Scope *sc) } else if (FuncDeclaration *fd = var->isFuncDeclaration()) { + // TODO: If fd isn't yet resolved its overload, the checkNestedReference + // call would cause incorrect validation. + // Maybe here should be moved in CallExp, or AddrExp for functions. if (fd->checkNestedReference(sc, loc)) return new ErrorExp(); } @@ -8966,6 +8973,10 @@ Expression *CallExp::semantic(Scope *sc) if (f->needThis()) { + // Change the ancestor lambdas to delegate before hasThis(sc) call. + if (f->checkNestedReference(sc, loc)) + return new ErrorExp(); + if (hasThis(sc)) { // Supply an implicit 'this', as in diff --git a/src/func.c b/src/func.c index a15d4d9aac71..0795002f292b 100644 --- a/src/func.c +++ b/src/func.c @@ -4120,20 +4120,50 @@ const char *FuncDeclaration::kind() bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc) { //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars()); - if (parent && parent != sc->parent && this->isNested() && - this->ident != Id::require && this->ident != Id::ensure) + if (!parent || parent == sc->parent) + return false; + if (ident == Id::require || ident == Id::ensure) + return false; + if (!isThis() && !isNested()) + return false; + + // The current function + FuncDeclaration *fdthis = sc->parent->isFuncDeclaration(); + if (!fdthis) + return false; // out of function scope + + Dsymbol *p = toParent2(); + + // Function literals from fdthis to p must be delegates + // TODO: here is similar to checkFrameAccess. + for (Dsymbol *s = fdthis; s && s != p; s = s->toParent2()) { - // The function that this function is in - FuncDeclaration *fdv2 = toParent2()->isFuncDeclaration(); + // function literal has reference to enclosing scope is delegate + if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration()) + fld->tok = TOKdelegate; + + if (FuncDeclaration *fd = s->isFuncDeclaration()) + { + if (!fd->isThis() && !fd->isNested()) + break; + } + if (AggregateDeclaration *ad2 = s->isAggregateDeclaration()) + { + if (ad2->storage_class & STCstatic) + break; + } + } - // The current function - FuncDeclaration *fdthis = sc->parent->isFuncDeclaration(); + if (isNested()) + { + // The function that this function is in + FuncDeclaration *fdv2 = p->isFuncDeclaration(); //printf("this = %s in [%s]\n", this->toChars(), this->loc.toChars()); //printf("fdv2 = %s in [%s]\n", fdv2->toChars(), fdv2->loc.toChars()); //printf("fdthis = %s in [%s]\n", fdthis->toChars(), fdthis->loc.toChars()); - if (fdv2 && fdthis && fdv2 != fdthis) + if (fdv2 && fdv2 != fdthis) { // Add this function to the list of those which called us if (fdthis != this) @@ -4153,7 +4183,7 @@ bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc) } } - FuncDeclaration *fdv = toParent2()->isFuncDeclaration(); + FuncDeclaration *fdv = p->isFuncDeclaration(); if (fdv && fdthis && fdv != fdthis) { int lv = fdthis->getLevel(loc, sc, fdv); @@ -4165,12 +4195,6 @@ bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc) return false; // same level call // Uplevel call - - // BUG: may need to walk up outer scopes like Declaration::checkNestedReference() does - - // function literal has reference to enclosing scope is delegate - if (FuncLiteralDeclaration *fld = fdthis->isFuncLiteralDeclaration()) - fld->tok = TOKdelegate; } } return false; diff --git a/test/compilable/test14973.d b/test/compilable/test14973.d new file mode 100644 index 000000000000..4d5575b78afd --- /dev/null +++ b/test/compilable/test14973.d @@ -0,0 +1,54 @@ +template map(fun...) +{ + auto map(R)(R r) + { + return MapResult!(fun, R)(r); + } +} + +struct MapResult(alias fun, R) +{ + R _input; + + @property bool empty() { return _input.length == 0; } + @property auto front() { return fun(_input[0]); } + void popFront() { _input = _input[1..$]; } +} + +class Foo +{ + int baz() { return 1; } + void bar() + { + auto s = [1].map!(i => baz()); // compiles + auto r = [1].map!( + // lambda1 + i => + [1].map!( + // lambda2 + j => + baz() + ) + ); // compiles <- error + } +} + +class Bar +{ + int baz; + void bar() + { + auto s = [1].map!(i => baz); // compiles + auto r = [1].map!( + // lambda1 + i => + [1].map!( + // lambda2 + j => + baz + ) + ); // compiles <- error + } +} + +void main() {} diff --git a/test/fail_compilation/diag9831.d b/test/fail_compilation/diag9831.d index 8882f66efc81..ec6c72d5234e 100644 --- a/test/fail_compilation/diag9831.d +++ b/test/fail_compilation/diag9831.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9831.d(12): Error: function diag9831.main.__lambda1 cannot access frame of function D main +fail_compilation/diag9831.d(12): Error: cannot match delegate literal to function pointer type 'int function(int x)' --- */ diff --git a/test/fail_compilation/fail11545.d b/test/fail_compilation/fail11545.d index 514cb87854ae..0fe4fd03510a 100644 --- a/test/fail_compilation/fail11545.d +++ b/test/fail_compilation/fail11545.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail11545.d(14): Error: need 'this' for 'x' of type 'int' -fail_compilation/fail11545.d(18): Error: need 'this' for 'x' of type 'int' +fail_compilation/fail11545.d(17): Error: cannot implicitly convert expression (__lambda5) of type int delegate() pure nothrow @nogc @safe to int function() +fail_compilation/fail11545.d(17): Error: cannot implicitly convert expression (__lambda5) of type int delegate() pure nothrow @nogc @safe to int function() --- */ diff --git a/test/fail_compilation/fail120.d b/test/fail_compilation/fail120.d index ae0f5b1093b4..443ee7a9ce55 100644 --- a/test/fail_compilation/fail120.d +++ b/test/fail_compilation/fail120.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail120.d(12): Error: need 'this' for 'nodes' of type 'int[2]' -fail_compilation/fail120.d(13): Error: need 'this' for 'nodes' of type 'int[2]' +fail_compilation/fail120.d(12): Error: non-constant nested delegate literal expression __lambda4 +fail_compilation/fail120.d(13): Error: non-constant nested delegate literal expression __lambda5 --- */ diff --git a/test/fail_compilation/fail39.d b/test/fail_compilation/fail39.d index 65472cf614eb..6a952b311d43 100644 --- a/test/fail_compilation/fail39.d +++ b/test/fail_compilation/fail39.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail39.d(11): Error: function fail39.main.__funcliteral2 cannot access frame of function D main +fail_compilation/fail39.d(11): Error: function fail39.main.foo is a nested function and cannot be accessed from fail39.main.__funcliteral2 --- */ From 22d697d84726843e4c3e55d988312dc1d3d0b269 Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Sat, 29 Aug 2015 08:34:21 +0200 Subject: [PATCH 6/6] convert C++ -> D --- src/access.c | 447 - src/access.d | 400 + src/aggregate.d | 500 + src/aliasthis.c | 149 - src/aliasthis.d | 147 + src/apply.c | 145 - src/apply.d | 143 + src/argtypes.c | 491 - src/argtypes.d | 450 + src/arrayop.c | 642 -- src/arrayop.d | 637 ++ src/arraytypes.d | 37 + src/attrib.c | 1408 --- src/attrib.d | 1408 +++ src/{builtin.c => builtin.d} | 240 +- src/canthrow.c | 239 - src/canthrow.d | 221 + src/cast.c | 3746 ------- src/class.c | 1855 ---- src/clone.c | 1131 --- src/clone.d | 1016 ++ src/cond.c | 388 - src/cond.d | 433 + src/constfold.c | 1999 ---- src/constfold.d | 2021 ++++ src/cppmangle.c | 1921 ---- src/cppmangle.d | 1889 ++++ src/ctfeexpr.c | 2383 ----- src/ctfeexpr.d | 2395 +++++ src/dcast.d | 3445 +++++++ src/dclass.d | 1692 ++++ src/declaration.c | 2512 ----- src/declaration.d | 2802 ++++++ src/delegatize.c | 185 - src/delegatize.d | 160 + src/denum.d | 707 ++ src/dimport.d | 432 + src/dinifile.d | 332 + src/dinterpret.d | 6586 +++++++++++++ src/dmacro.d | 408 + src/dmangle.d | 842 ++ src/dmodule.d | 1309 +++ src/doc.c | 2804 ------ src/doc.d | 2465 +++++ src/dscope.d | 601 ++ src/dstruct.d | 858 ++ src/dsymbol.c | 1625 ---- src/dsymbol.d | 1914 ++++ src/dtemplate.d | 8048 ++++++++++++++++ src/dunittest.d | 19 + src/dversion.d | 203 + src/entity.c | 2393 ----- src/enum.c | 720 -- src/errors.c | 281 - src/errors.d | 287 + src/escape.c | 439 - src/escape.d | 428 + src/expression.c | 14251 --------------------------- src/expression.d | 14450 ++++++++++++++++++++++++++++ src/func.c | 5441 ----------- src/func.d | 5362 +++++++++++ src/globals.c | 134 - src/globals.d | 431 + src/hdrgen.c | 3280 ------- src/hdrgen.d | 3168 ++++++ src/identifier.c | 137 - src/identifier.d | 141 + src/imphint.c | 75 - src/imphint.d | 65 + src/import.c | 457 - src/inifile.c | 380 - src/init.c | 1001 -- src/init.d | 1045 ++ src/inline.c | 2101 ---- src/inline.d | 1962 ++++ src/interpret.c | 6925 ------------- src/intrange.c | 1107 --- src/json.c | 890 -- src/json.d | 770 ++ src/lexer.c | 2484 ----- src/lexer.d | 2493 +++++ src/lib.d | 48 + src/link.c | 911 -- src/link.d | 838 ++ src/macro.c | 468 - src/mangle.c | 909 -- src/mars.c | 1904 ---- src/mars.d | 1689 ++++ src/module.c | 1328 --- src/mtype.c | 9207 ------------------ src/mtype.d | 9523 ++++++++++++++++++ src/nogc.c | 214 - src/nogc.d | 182 + src/nspace.c | 214 - src/nspace.d | 226 + src/objc.c | 241 - src/objc.d | 213 + src/objc_stubs.c | 118 - src/opover.c | 1825 ---- src/opover.d | 1929 ++++ src/optimize.c | 1263 --- src/optimize.d | 1155 +++ src/parse.c | 8007 --------------- src/parse.d | 7770 +++++++++++++++ src/root/aav.c | 193 - src/root/aav.d | 171 + src/root/async.c | 348 - src/root/async.h | 48 - src/root/checkedint.c | 559 -- src/root/checkedint.h | 24 - src/root/file.c | 267 - src/root/file.d | 241 + src/root/filename.c | 672 -- src/root/filename.d | 682 ++ src/root/longdouble.c | 654 -- src/root/man.c | 96 - src/root/man.d | 74 + src/root/object.c | 50 - src/root/outbuffer.c | 399 - src/root/outbuffer.d | 419 + src/root/port.c | 1145 --- src/root/response.c | 242 - src/root/response.d | 201 + src/root/rmem.c | 163 - src/root/{speller.c => speller.d} | 209 +- src/root/stringtable.c | 255 - src/root/stringtable.d | 270 + src/sapply.c | 157 - src/sapply.d | 175 + src/scope.c | 581 -- src/sideeffect.c | 383 - src/sideeffect.d | 342 + src/statement.c | 5356 ----------- src/statement.d | 5708 +++++++++++ src/staticassert.c | 121 - src/staticassert.d | 117 + src/struct.c | 1358 --- src/target.c | 407 - src/target.d | 366 + src/template.c | 8457 ---------------- src/tokens.c | 478 - src/tokens.d | 988 ++ src/traits.c | 1256 --- src/traits.d | 1310 +++ src/unittests.c | 27 - src/utf.c | 305 - src/utf.d | 774 ++ src/version.c | 197 - src/visitor.d | 1367 +++ 149 files changed, 112090 insertions(+), 117632 deletions(-) delete mode 100644 src/access.c create mode 100644 src/access.d create mode 100644 src/aggregate.d delete mode 100644 src/aliasthis.c create mode 100644 src/aliasthis.d delete mode 100644 src/apply.c create mode 100644 src/apply.d delete mode 100644 src/argtypes.c create mode 100644 src/argtypes.d delete mode 100644 src/arrayop.c create mode 100644 src/arrayop.d create mode 100644 src/arraytypes.d delete mode 100644 src/attrib.c create mode 100644 src/attrib.d rename src/{builtin.c => builtin.d} (53%) delete mode 100644 src/canthrow.c create mode 100644 src/canthrow.d delete mode 100644 src/cast.c delete mode 100644 src/class.c delete mode 100644 src/clone.c create mode 100644 src/clone.d delete mode 100644 src/cond.c create mode 100644 src/cond.d delete mode 100644 src/constfold.c create mode 100644 src/constfold.d delete mode 100644 src/cppmangle.c create mode 100644 src/cppmangle.d delete mode 100644 src/ctfeexpr.c create mode 100644 src/ctfeexpr.d create mode 100644 src/dcast.d create mode 100644 src/dclass.d delete mode 100644 src/declaration.c create mode 100644 src/declaration.d delete mode 100644 src/delegatize.c create mode 100644 src/delegatize.d create mode 100644 src/denum.d create mode 100644 src/dimport.d create mode 100644 src/dinifile.d create mode 100644 src/dinterpret.d create mode 100644 src/dmacro.d create mode 100644 src/dmangle.d create mode 100644 src/dmodule.d delete mode 100644 src/doc.c create mode 100644 src/doc.d create mode 100644 src/dscope.d create mode 100644 src/dstruct.d delete mode 100644 src/dsymbol.c create mode 100644 src/dsymbol.d create mode 100644 src/dtemplate.d create mode 100644 src/dunittest.d create mode 100644 src/dversion.d delete mode 100644 src/entity.c delete mode 100644 src/enum.c delete mode 100644 src/errors.c create mode 100644 src/errors.d delete mode 100644 src/escape.c create mode 100644 src/escape.d delete mode 100644 src/expression.c create mode 100644 src/expression.d delete mode 100644 src/func.c create mode 100644 src/func.d delete mode 100644 src/globals.c create mode 100644 src/globals.d delete mode 100644 src/hdrgen.c create mode 100644 src/hdrgen.d delete mode 100644 src/identifier.c create mode 100644 src/identifier.d delete mode 100644 src/imphint.c create mode 100644 src/imphint.d delete mode 100644 src/import.c delete mode 100644 src/inifile.c delete mode 100644 src/init.c create mode 100644 src/init.d delete mode 100644 src/inline.c create mode 100644 src/inline.d delete mode 100644 src/interpret.c delete mode 100644 src/intrange.c delete mode 100644 src/json.c create mode 100644 src/json.d delete mode 100644 src/lexer.c create mode 100644 src/lexer.d create mode 100644 src/lib.d delete mode 100644 src/link.c create mode 100644 src/link.d delete mode 100644 src/macro.c delete mode 100644 src/mangle.c delete mode 100644 src/mars.c create mode 100644 src/mars.d delete mode 100644 src/module.c delete mode 100644 src/mtype.c create mode 100644 src/mtype.d delete mode 100644 src/nogc.c create mode 100644 src/nogc.d delete mode 100644 src/nspace.c create mode 100644 src/nspace.d delete mode 100644 src/objc.c create mode 100644 src/objc.d delete mode 100644 src/objc_stubs.c delete mode 100644 src/opover.c create mode 100644 src/opover.d delete mode 100644 src/optimize.c create mode 100644 src/optimize.d delete mode 100644 src/parse.c create mode 100644 src/parse.d delete mode 100644 src/root/aav.c create mode 100644 src/root/aav.d delete mode 100644 src/root/async.c delete mode 100644 src/root/async.h delete mode 100644 src/root/checkedint.c delete mode 100644 src/root/checkedint.h delete mode 100644 src/root/file.c create mode 100644 src/root/file.d delete mode 100644 src/root/filename.c create mode 100644 src/root/filename.d delete mode 100644 src/root/longdouble.c delete mode 100644 src/root/man.c create mode 100644 src/root/man.d delete mode 100644 src/root/object.c delete mode 100644 src/root/outbuffer.c create mode 100644 src/root/outbuffer.d delete mode 100644 src/root/port.c delete mode 100644 src/root/response.c create mode 100644 src/root/response.d delete mode 100644 src/root/rmem.c rename src/root/{speller.c => speller.d} (55%) delete mode 100644 src/root/stringtable.c create mode 100644 src/root/stringtable.d delete mode 100644 src/sapply.c create mode 100644 src/sapply.d delete mode 100644 src/scope.c delete mode 100644 src/sideeffect.c create mode 100644 src/sideeffect.d delete mode 100644 src/statement.c create mode 100644 src/statement.d delete mode 100644 src/staticassert.c create mode 100644 src/staticassert.d delete mode 100644 src/struct.c delete mode 100644 src/target.c create mode 100644 src/target.d delete mode 100644 src/template.c delete mode 100644 src/tokens.c create mode 100644 src/tokens.d delete mode 100644 src/traits.c create mode 100644 src/traits.d delete mode 100644 src/unittests.c delete mode 100644 src/utf.c create mode 100644 src/utf.d delete mode 100644 src/version.c create mode 100644 src/visitor.d diff --git a/src/access.c b/src/access.c deleted file mode 100644 index f2ab12039adf..000000000000 --- a/src/access.c +++ /dev/null @@ -1,447 +0,0 @@ -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/access.c - */ - -#include -#include -#include - -#include "root.h" -#include "rmem.h" - -#include "enum.h" -#include "aggregate.h" -#include "init.h" -#include "attrib.h" -#include "scope.h" -#include "id.h" -#include "mtype.h" -#include "declaration.h" -#include "aggregate.h" -#include "expression.h" -#include "module.h" - -#define LOG 0 - -/* Code to do access checks - */ - -bool hasPackageAccess(Scope *sc, Dsymbol *s); -bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember); -bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd); - -/**************************************** - * Return Prot access for Dsymbol smember in this declaration. - */ -Prot getAccess(AggregateDeclaration *ad, Dsymbol *smember) -{ - Prot access_ret = Prot(PROTnone); - -#if LOG - printf("+AggregateDeclaration::getAccess(this = '%s', smember = '%s')\n", - ad->toChars(), smember->toChars()); -#endif - - assert(ad->isStructDeclaration() || ad->isClassDeclaration()); - if (smember->toParent() == ad) - { - access_ret = smember->prot(); - } - else if (smember->isDeclaration()->isStatic()) - { - access_ret = smember->prot(); - } - if (ClassDeclaration *cd = ad->isClassDeclaration()) - { - for (size_t i = 0; i < cd->baseclasses->dim; i++) - { - BaseClass *b = (*cd->baseclasses)[i]; - - Prot access = getAccess(b->sym, smember); - switch (access.kind) - { - case PROTnone: - break; - - case PROTprivate: - access_ret = Prot(PROTnone); // private members of base class not accessible - break; - - case PROTpackage: - case PROTprotected: - case PROTpublic: - case PROTexport: - // If access is to be tightened - if (b->protection.isMoreRestrictiveThan(access)) - access = b->protection; - - // Pick path with loosest access - if (access_ret.isMoreRestrictiveThan(access)) - access_ret = access; - break; - - default: - assert(0); - } - } - } - -#if LOG - printf("-AggregateDeclaration::getAccess(this = '%s', smember = '%s') = %d\n", - ad->toChars(), smember->toChars(), access_ret); -#endif - return access_ret; -} - -/******************************************************** - * Helper function for checkAccess() - * Returns: - * false is not accessible - * true is accessible - */ -static bool isAccessible( - Dsymbol *smember, - Dsymbol *sfunc, - AggregateDeclaration *dthis, - AggregateDeclaration *cdscope) -{ - assert(dthis); - -#if 0 - printf("isAccessible for %s.%s in function %s() in scope %s\n", - dthis->toChars(), smember->toChars(), - sfunc ? sfunc->toChars() : "NULL", - cdscope ? cdscope->toChars() : "NULL"); -#endif - if (hasPrivateAccess(dthis, sfunc) || - isFriendOf(dthis, cdscope)) - { - if (smember->toParent() == dthis) - return true; - - if (ClassDeclaration *cdthis = dthis->isClassDeclaration()) - { - for (size_t i = 0; i < cdthis->baseclasses->dim; i++) - { - BaseClass *b = (*cdthis->baseclasses)[i]; - Prot access = getAccess(b->sym, smember); - if (access.kind >= PROTprotected || - isAccessible(smember, sfunc, b->sym, cdscope)) - { - return true; - } - } - } - } - else - { - if (smember->toParent() != dthis) - { - if (ClassDeclaration *cdthis = dthis->isClassDeclaration()) - { - for (size_t i = 0; i < cdthis->baseclasses->dim; i++) - { - BaseClass *b = (*cdthis->baseclasses)[i]; - if (isAccessible(smember, sfunc, b->sym, cdscope)) - return true; - } - } - } - } - return false; -} - -/******************************* - * Do access check for member of this class, this class being the - * type of the 'this' pointer used to access smember. - * Returns true if the member is not accessible. - */ -bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember) -{ - FuncDeclaration *f = sc->func; - AggregateDeclaration *cdscope = sc->getStructClassScope(); - -#if LOG - printf("AggregateDeclaration::checkAccess() for %s.%s in function %s() in scope %s\n", - ad->toChars(), smember->toChars(), - f ? f->toChars() : NULL, - cdscope ? cdscope->toChars() : NULL); -#endif - - Dsymbol *smemberparent = smember->toParent(); - if (!smemberparent || !smemberparent->isAggregateDeclaration()) - { -#if LOG - printf("not an aggregate member\n"); -#endif - return false; // then it is accessible - } - - // BUG: should enable this check - //assert(smember->parent->isBaseOf(this, NULL)); - - bool result; - Prot access; - if (smemberparent == ad) - { - access = smember->prot(); - result = access.kind >= PROTpublic || - hasPrivateAccess(ad, f) || - isFriendOf(ad, cdscope) || - (access.kind == PROTpackage && hasPackageAccess(sc, smember)) || - ad->getAccessModule() == sc->module; -#if LOG - printf("result1 = %d\n", result); -#endif - } - else if ((access = getAccess(ad, smember)).kind >= PROTpublic) - { - result = true; -#if LOG - printf("result2 = %d\n", result); -#endif - } - else if (access.kind == PROTpackage && hasPackageAccess(sc, ad)) - { - result = true; -#if LOG - printf("result3 = %d\n", result); -#endif - } - else - { - result = isAccessible(smember, f, ad, cdscope); -#if LOG - printf("result4 = %d\n", result); -#endif - } - if (!result) - { - ad->error(loc, "member %s is not accessible", smember->toChars()); - //printf("smember = %s %s, prot = %d, semanticRun = %d\n", - // smember->kind(), smember->toPrettyChars(), smember->prot(), smember->semanticRun); - return true; - } - return false; -} - -/**************************************** - * Determine if this is the same or friend of cd. - */ -bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd) -{ -#if LOG - printf("AggregateDeclaration::isFriendOf(this = '%s', cd = '%s')\n", ad->toChars(), cd ? cd->toChars() : "null"); -#endif - if (ad == cd) - return true; - - // Friends if both are in the same module - //if (toParent() == cd->toParent()) - if (cd && ad->getAccessModule() == cd->getAccessModule()) - { -#if LOG - printf("\tin same module\n"); -#endif - return true; - } - -#if LOG - printf("\tnot friend\n"); -#endif - return false; -} - -/**************************************** - * Determine if scope sc has package level access to s. - */ -bool hasPackageAccess(Scope *sc, Dsymbol *s) -{ -#if LOG - printf("hasPackageAccess(s = '%s', sc = '%p', s->protection.pkg = '%s')\n", - s->toChars(), sc, - s->prot().pkg ? s->prot().pkg->toChars() : "NULL"); -#endif - - Package *pkg = NULL; - - if (s->prot().pkg) - pkg = s->prot().pkg; - else - { - // no explicit package for protection, inferring most qualified one - for (; s; s = s->parent) - { - if (Module *m = s->isModule()) - { - DsymbolTable *dst = Package::resolve(m->md ? m->md->packages : NULL, NULL, NULL); - assert(dst); - Dsymbol *s2 = dst->lookup(m->ident); - assert(s2); - Package *p = s2->isPackage(); - if (p && p->isPackageMod()) - { - pkg = p; - break; - } - } - else if ((pkg = s->isPackage()) != NULL) - break; - } - } -#if LOG - if (pkg) - printf("\tsymbol access binds to package '%s'\n", pkg->toChars()); -#endif - - if (pkg) - { - if (pkg == sc->module->parent) - { -#if LOG - printf("\tsc is in permitted package for s\n"); -#endif - return true; - } - if (pkg->isPackageMod() == sc->module) - { -#if LOG - printf("\ts is in same package.d module as sc\n"); -#endif - return true; - } - Dsymbol* ancestor = sc->module->parent; - for (; ancestor; ancestor = ancestor->parent) - { - if (ancestor == pkg) - { -#if LOG - printf("\tsc is in permitted ancestor package for s\n"); -#endif - return true; - } - } - } - -#if LOG - printf("\tno package access\n"); -#endif - return false; -} - -/********************************** - * Determine if smember has access to private members of this declaration. - */ -bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember) -{ - if (smember) - { - AggregateDeclaration *cd = NULL; - Dsymbol *smemberparent = smember->toParent(); - if (smemberparent) - cd = smemberparent->isAggregateDeclaration(); - -#if LOG - printf("AggregateDeclaration::hasPrivateAccess(class %s, member %s)\n", - ad->toChars(), smember->toChars()); -#endif - - if (ad == cd) // smember is a member of this class - { -#if LOG - printf("\tyes 1\n"); -#endif - return true; // so we get private access - } - - // If both are members of the same module, grant access - while (1) - { - Dsymbol *sp = smember->toParent(); - if (sp->isFuncDeclaration() && smember->isFuncDeclaration()) - smember = sp; - else - break; - } - if (!cd && ad->toParent() == smember->toParent()) - { -#if LOG - printf("\tyes 2\n"); -#endif - return true; - } - if (!cd && ad->getAccessModule() == smember->getAccessModule()) - { -#if LOG - printf("\tyes 3\n"); -#endif - return true; - } - } -#if LOG - printf("\tno\n"); -#endif - return false; -} - -/**************************************** - * Check access to d for expression e.d - * Returns true if the declaration is not accessible. - */ -bool checkAccess(Loc loc, Scope *sc, Expression *e, Declaration *d) -{ - if (sc->flags & SCOPEnoaccesscheck) - return false; - -#if LOG - if (e) - { - printf("checkAccess(%s . %s)\n", e->toChars(), d->toChars()); - printf("\te->type = %s\n", e->type->toChars()); - } - else - { - printf("checkAccess(%s)\n", d->toPrettyChars()); - } -#endif - if (d->isUnitTestDeclaration()) - { - // Unittests are always accessible. - return false; - } - if (!e) - { - if (d->prot().kind == PROTprivate && d->getAccessModule() != sc->module || - d->prot().kind == PROTpackage && !hasPackageAccess(sc, d)) - { - error(loc, "%s %s is not accessible from module %s", - d->kind(), d->toPrettyChars(), sc->module->toChars()); - return true; - } - } - else if (e->type->ty == Tclass) - { - // Do access check - ClassDeclaration *cd = (ClassDeclaration *)(((TypeClass *)e->type)->sym); - if (e->op == TOKsuper) - { - ClassDeclaration *cd2 = sc->func->toParent()->isClassDeclaration(); - if (cd2) - cd = cd2; - } - return checkAccess(cd, loc, sc, d); - } - else if (e->type->ty == Tstruct) - { - // Do access check - StructDeclaration *cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym); - return checkAccess(cd, loc, sc, d); - } - return false; -} diff --git a/src/access.d b/src/access.d new file mode 100644 index 000000000000..32e94554fb49 --- /dev/null +++ b/src/access.d @@ -0,0 +1,400 @@ +// 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.access; + +import ddmd.aggregate, ddmd.dclass, ddmd.declaration, ddmd.dmodule, ddmd.dscope, ddmd.dstruct, ddmd.dsymbol, ddmd.errors, ddmd.expression, ddmd.func, ddmd.globals, ddmd.mtype, ddmd.tokens; + +private enum LOG = false; + +/**************************************** + * Return Prot access for Dsymbol smember in this declaration. + */ +extern (C++) Prot getAccess(AggregateDeclaration ad, Dsymbol smember) +{ + Prot access_ret = Prot(PROTnone); + static if (LOG) + { + printf("+AggregateDeclaration::getAccess(this = '%s', smember = '%s')\n", ad.toChars(), smember.toChars()); + } + assert(ad.isStructDeclaration() || ad.isClassDeclaration()); + if (smember.toParent() == ad) + { + access_ret = smember.prot(); + } + else if (smember.isDeclaration().isStatic()) + { + access_ret = smember.prot(); + } + if (ClassDeclaration cd = ad.isClassDeclaration()) + { + for (size_t i = 0; i < cd.baseclasses.dim; i++) + { + BaseClass* b = (*cd.baseclasses)[i]; + Prot access = getAccess(b.sym, smember); + switch (access.kind) + { + case PROTnone: + break; + case PROTprivate: + access_ret = Prot(PROTnone); // private members of base class not accessible + break; + case PROTpackage: + case PROTprotected: + case PROTpublic: + case PROTexport: + // If access is to be tightened + if (b.protection.isMoreRestrictiveThan(access)) + access = b.protection; + // Pick path with loosest access + if (access_ret.isMoreRestrictiveThan(access)) + access_ret = access; + break; + default: + assert(0); + } + } + } + static if (LOG) + { + printf("-AggregateDeclaration::getAccess(this = '%s', smember = '%s') = %d\n", ad.toChars(), smember.toChars(), access_ret); + } + return access_ret; +} + +/******************************************************** + * Helper function for checkAccess() + * Returns: + * false is not accessible + * true is accessible + */ +extern (C++) static bool isAccessible(Dsymbol smember, Dsymbol sfunc, AggregateDeclaration dthis, AggregateDeclaration cdscope) +{ + assert(dthis); + version (none) + { + printf("isAccessible for %s.%s in function %s() in scope %s\n", dthis.toChars(), smember.toChars(), sfunc ? sfunc.toChars() : "NULL", cdscope ? cdscope.toChars() : "NULL"); + } + if (hasPrivateAccess(dthis, sfunc) || isFriendOf(dthis, cdscope)) + { + if (smember.toParent() == dthis) + return true; + if (ClassDeclaration cdthis = dthis.isClassDeclaration()) + { + for (size_t i = 0; i < cdthis.baseclasses.dim; i++) + { + BaseClass* b = (*cdthis.baseclasses)[i]; + Prot access = getAccess(b.sym, smember); + if (access.kind >= PROTprotected || isAccessible(smember, sfunc, b.sym, cdscope)) + { + return true; + } + } + } + } + else + { + if (smember.toParent() != dthis) + { + if (ClassDeclaration cdthis = dthis.isClassDeclaration()) + { + for (size_t i = 0; i < cdthis.baseclasses.dim; i++) + { + BaseClass* b = (*cdthis.baseclasses)[i]; + if (isAccessible(smember, sfunc, b.sym, cdscope)) + return true; + } + } + } + } + return false; +} + +/******************************* + * Do access check for member of this class, this class being the + * type of the 'this' pointer used to access smember. + * Returns true if the member is not accessible. + */ +extern (C++) bool checkAccess(AggregateDeclaration ad, Loc loc, Scope* sc, Dsymbol smember) +{ + FuncDeclaration f = sc.func; + AggregateDeclaration cdscope = sc.getStructClassScope(); + static if (LOG) + { + printf("AggregateDeclaration::checkAccess() for %s.%s in function %s() in scope %s\n", ad.toChars(), smember.toChars(), f ? f.toChars() : null, cdscope ? cdscope.toChars() : null); + } + Dsymbol smemberparent = smember.toParent(); + if (!smemberparent || !smemberparent.isAggregateDeclaration()) + { + static if (LOG) + { + printf("not an aggregate member\n"); + } + return false; // then it is accessible + } + // BUG: should enable this check + //assert(smember->parent->isBaseOf(this, NULL)); + bool result; + Prot access; + if (smemberparent == ad) + { + access = smember.prot(); + result = access.kind >= PROTpublic || hasPrivateAccess(ad, f) || isFriendOf(ad, cdscope) || (access.kind == PROTpackage && hasPackageAccess(sc, smember)) || ad.getAccessModule() == sc._module; + static if (LOG) + { + printf("result1 = %d\n", result); + } + } + else if ((access = getAccess(ad, smember)).kind >= PROTpublic) + { + result = true; + static if (LOG) + { + printf("result2 = %d\n", result); + } + } + else if (access.kind == PROTpackage && hasPackageAccess(sc, ad)) + { + result = true; + static if (LOG) + { + printf("result3 = %d\n", result); + } + } + else + { + result = isAccessible(smember, f, ad, cdscope); + static if (LOG) + { + printf("result4 = %d\n", result); + } + } + if (!result) + { + ad.error(loc, "member %s is not accessible", smember.toChars()); + //printf("smember = %s %s, prot = %d, semanticRun = %d\n", + // smember->kind(), smember->toPrettyChars(), smember->prot(), smember->semanticRun); + return true; + } + return false; +} + +/**************************************** + * Determine if this is the same or friend of cd. + */ +extern (C++) bool isFriendOf(AggregateDeclaration ad, AggregateDeclaration cd) +{ + static if (LOG) + { + printf("AggregateDeclaration::isFriendOf(this = '%s', cd = '%s')\n", ad.toChars(), cd ? cd.toChars() : "null"); + } + if (ad == cd) + return true; + // Friends if both are in the same module + //if (toParent() == cd->toParent()) + if (cd && ad.getAccessModule() == cd.getAccessModule()) + { + static if (LOG) + { + printf("\tin same module\n"); + } + return true; + } + static if (LOG) + { + printf("\tnot friend\n"); + } + return false; +} + +/**************************************** + * Determine if scope sc has package level access to s. + */ +extern (C++) bool hasPackageAccess(Scope* sc, Dsymbol s) +{ + static if (LOG) + { + printf("hasPackageAccess(s = '%s', sc = '%p', s->protection.pkg = '%s')\n", s.toChars(), sc, s.prot().pkg ? s.prot().pkg.toChars() : "NULL"); + } + Package pkg = null; + if (s.prot().pkg) + pkg = s.prot().pkg; + else + { + // no explicit package for protection, inferring most qualified one + for (; s; s = s.parent) + { + if (Module m = s.isModule()) + { + DsymbolTable dst = Package.resolve(m.md ? m.md.packages : null, null, null); + assert(dst); + Dsymbol s2 = dst.lookup(m.ident); + assert(s2); + Package p = s2.isPackage(); + if (p && p.isPackageMod()) + { + pkg = p; + break; + } + } + else if ((pkg = s.isPackage()) !is null) + break; + } + } + static if (LOG) + { + if (pkg) + printf("\tsymbol access binds to package '%s'\n", pkg.toChars()); + } + if (pkg) + { + if (pkg == sc._module.parent) + { + static if (LOG) + { + printf("\tsc is in permitted package for s\n"); + } + return true; + } + if (pkg.isPackageMod() == sc._module) + { + static if (LOG) + { + printf("\ts is in same package.d module as sc\n"); + } + return true; + } + Dsymbol ancestor = sc._module.parent; + for (; ancestor; ancestor = ancestor.parent) + { + if (ancestor == pkg) + { + static if (LOG) + { + printf("\tsc is in permitted ancestor package for s\n"); + } + return true; + } + } + } + static if (LOG) + { + printf("\tno package access\n"); + } + return false; +} + +/********************************** + * Determine if smember has access to private members of this declaration. + */ +extern (C++) bool hasPrivateAccess(AggregateDeclaration ad, Dsymbol smember) +{ + if (smember) + { + AggregateDeclaration cd = null; + Dsymbol smemberparent = smember.toParent(); + if (smemberparent) + cd = smemberparent.isAggregateDeclaration(); + static if (LOG) + { + printf("AggregateDeclaration::hasPrivateAccess(class %s, member %s)\n", ad.toChars(), smember.toChars()); + } + if (ad == cd) // smember is a member of this class + { + static if (LOG) + { + printf("\tyes 1\n"); + } + return true; // so we get private access + } + // If both are members of the same module, grant access + while (1) + { + Dsymbol sp = smember.toParent(); + if (sp.isFuncDeclaration() && smember.isFuncDeclaration()) + smember = sp; + else + break; + } + if (!cd && ad.toParent() == smember.toParent()) + { + static if (LOG) + { + printf("\tyes 2\n"); + } + return true; + } + if (!cd && ad.getAccessModule() == smember.getAccessModule()) + { + static if (LOG) + { + printf("\tyes 3\n"); + } + return true; + } + } + static if (LOG) + { + printf("\tno\n"); + } + return false; +} + +/**************************************** + * Check access to d for expression e.d + * Returns true if the declaration is not accessible. + */ +extern (C++) bool checkAccess(Loc loc, Scope* sc, Expression e, Declaration d) +{ + if (sc.flags & SCOPEnoaccesscheck) + return false; + static if (LOG) + { + if (e) + { + printf("checkAccess(%s . %s)\n", e.toChars(), d.toChars()); + printf("\te->type = %s\n", e.type.toChars()); + } + else + { + printf("checkAccess(%s)\n", d.toPrettyChars()); + } + } + if (d.isUnitTestDeclaration()) + { + // Unittests are always accessible. + return false; + } + if (!e) + { + if (d.prot().kind == PROTprivate && d.getAccessModule() != sc._module || d.prot().kind == PROTpackage && !hasPackageAccess(sc, d)) + { + error(loc, "%s %s is not accessible from module %s", d.kind(), d.toPrettyChars(), sc._module.toChars()); + return true; + } + } + else if (e.type.ty == Tclass) + { + // Do access check + ClassDeclaration cd = cast(ClassDeclaration)(cast(TypeClass)e.type).sym; + if (e.op == TOKsuper) + { + ClassDeclaration cd2 = sc.func.toParent().isClassDeclaration(); + if (cd2) + cd = cd2; + } + return checkAccess(cd, loc, sc, d); + } + else if (e.type.ty == Tstruct) + { + // Do access check + StructDeclaration cd = cast(StructDeclaration)(cast(TypeStruct)e.type).sym; + return checkAccess(cd, loc, sc, d); + } + return false; +} diff --git a/src/aggregate.d b/src/aggregate.d new file mode 100644 index 000000000000..a6bc907612c5 --- /dev/null +++ b/src/aggregate.d @@ -0,0 +1,500 @@ +// 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.aggregate; + +import ddmd.access, ddmd.arraytypes, ddmd.backend, ddmd.clone, ddmd.dclass, ddmd.declaration, ddmd.doc, ddmd.dscope, ddmd.dstruct, ddmd.dsymbol, ddmd.dtemplate, ddmd.expression, ddmd.func, ddmd.globals, ddmd.hdrgen, ddmd.id, ddmd.identifier, ddmd.mtype, ddmd.opover, ddmd.root.outbuffer, ddmd.statement, ddmd.tokens, ddmd.visitor; + +enum Sizeok : int +{ + SIZEOKnone, // size of aggregate is not computed yet + SIZEOKdone, // size of aggregate is set correctly + SIZEOKfwd, // error in computing size of aggregate +} + +alias SIZEOKnone = Sizeok.SIZEOKnone; +alias SIZEOKdone = Sizeok.SIZEOKdone; +alias SIZEOKfwd = Sizeok.SIZEOKfwd; + +enum Baseok : int +{ + BASEOKnone, // base classes not computed yet + BASEOKin, // in process of resolving base classes + BASEOKdone, // all base classes are resolved + BASEOKsemanticdone, // all base classes semantic done +} + +alias BASEOKnone = Baseok.BASEOKnone; +alias BASEOKin = Baseok.BASEOKin; +alias BASEOKdone = Baseok.BASEOKdone; +alias BASEOKsemanticdone = Baseok.BASEOKsemanticdone; + +extern (C++) class AggregateDeclaration : ScopeDsymbol +{ +public: + Type type; + StorageClass storage_class; + Prot protection; + uint structsize; // size of struct + uint alignsize; // size of struct for alignment purposes + VarDeclarations fields; // VarDeclaration fields + Sizeok sizeok; // set when structsize contains valid data + Dsymbol deferred; // any deferred semantic2() or semantic3() symbol + bool isdeprecated; // true if deprecated + /* !=NULL if is nested + * pointing to the dsymbol that directly enclosing it. + * 1. The function that enclosing it (nested struct and class) + * 2. The class that enclosing it (nested class only) + * 3. If enclosing aggregate is template, its enclosing dsymbol. + * See AggregateDeclaraton::makeNested for the details. + */ + Dsymbol enclosing; + VarDeclaration vthis; // 'this' parameter if this aggregate is nested + // Special member functions + FuncDeclarations invs; // Array of invariants + FuncDeclaration inv; // invariant + NewDeclaration aggNew; // allocator + DeleteDeclaration aggDelete; // deallocator + Dsymbol ctor; // CtorDeclaration or TemplateDeclaration + // default constructor - should have no arguments, because + // it would be stored in TypeInfo_Class.defaultConstructor + CtorDeclaration defaultCtor; + Dsymbol aliasthis; // forward unresolved lookups to aliasthis + bool noDefaultCtor; // no default construction + FuncDeclarations dtors; // Array of destructors + FuncDeclaration dtor; // aggregate destructor + Expression getRTInfo; // pointer to GC info generated by object.RTInfo(this) + + /********************************* AggregateDeclaration ****************************/ + final extern (D) this(Loc loc, Identifier id) + { + super(id); + this.loc = loc; + storage_class = 0; + protection = Prot(PROTpublic); + type = null; + structsize = 0; // size of struct + alignsize = 0; // size of struct for alignment purposes + sizeok = SIZEOKnone; // size not determined yet + deferred = null; + isdeprecated = false; + inv = null; + aggNew = null; + aggDelete = null; + stag = null; + sinit = null; + enclosing = null; + vthis = null; + ctor = null; + defaultCtor = null; + aliasthis = null; + noDefaultCtor = false; + dtor = null; + getRTInfo = null; + } + + final void setScope(Scope* sc) + { + if (sizeok == SIZEOKdone) + return; + ScopeDsymbol.setScope(sc); + } + + final void semantic2(Scope* sc) + { + //printf("AggregateDeclaration::semantic2(%s) type = %s, errors = %d\n", toChars(), type->toChars(), errors); + if (!members) + return; + if (_scope && sizeok == SIZEOKfwd) // Bugzilla 12531 + semantic(null); + if (_scope) + { + error("has forward references"); + return; + } + Scope* sc2 = sc.push(this); + sc2.stc &= STCsafe | STCtrusted | STCsystem; + sc2.parent = this; + //if (isUnionDeclaration()) // TODO + // sc2->inunion = 1; + sc2.protection = Prot(PROTpublic); + sc2.explicitProtection = 0; + sc2.structalign = STRUCTALIGN_DEFAULT; + sc2.userAttribDecl = null; + for (size_t i = 0; i < members.dim; i++) + { + Dsymbol s = (*members)[i]; + //printf("\t[%d] %s\n", i, s->toChars()); + s.semantic2(sc2); + } + sc2.pop(); + } + + final void semantic3(Scope* sc) + { + //printf("AggregateDeclaration::semantic3(%s) type = %s, errors = %d\n", toChars(), type->toChars(), errors); + if (!members) + return; + StructDeclaration sd = isStructDeclaration(); + if (!sc) // from runDeferredSemantic3 for TypeInfo generation + { + assert(sd); + sd.semanticTypeInfoMembers(); + return; + } + Scope* sc2 = sc.push(this); + sc2.stc &= STCsafe | STCtrusted | STCsystem; + sc2.parent = this; + if (isUnionDeclaration()) + sc2.inunion = 1; + sc2.protection = Prot(PROTpublic); + sc2.explicitProtection = 0; + sc2.structalign = STRUCTALIGN_DEFAULT; + sc2.userAttribDecl = null; + for (size_t i = 0; i < members.dim; i++) + { + Dsymbol s = (*members)[i]; + s.semantic3(sc2); + } + sc2.pop(); + // don't do it for unused deprecated types + // or error types + if (!getRTInfo && Type.rtinfo && (!isDeprecated() || global.params.useDeprecated) && (type && type.ty != Terror)) + { + // Evaluate: RTinfo!type + auto tiargs = new Objects(); + tiargs.push(type); + auto ti = new TemplateInstance(loc, Type.rtinfo, tiargs); + Scope* sc3 = ti.tempdecl._scope.startCTFE(); + sc3.tinst = sc.tinst; + sc3.minst = sc.minst; + if (isDeprecated()) + sc3.stc |= STCdeprecated; + ti.semantic(sc3); + ti.semantic2(sc3); + ti.semantic3(sc3); + Expression e = new DsymbolExp(Loc(), ti.toAlias(), 0); + e = e.semantic(sc3); + sc3.endCTFE(); + e = e.ctfeInterpret(); + getRTInfo = e; + } + if (sd) + sd.semanticTypeInfoMembers(); + } + + final uint size(Loc loc) + { + //printf("AggregateDeclaration::size() %s, scope = %p\n", toChars(), scope); + if (loc.linnum == 0) + loc = this.loc; + if (sizeok != SIZEOKdone && _scope) + { + semantic(null); + // Determine the instance size of base class first. + if (ClassDeclaration cd = isClassDeclaration()) + cd.baseClass.size(loc); + } + if (sizeok != SIZEOKdone && members) + { + /* See if enough is done to determine the size, + * meaning all the fields are done. + */ + struct SV + { + /* Returns: + * 0 this member doesn't need further processing to determine struct size + * 1 this member does + */ + extern (C++) static int func(Dsymbol s, void* param) + { + VarDeclaration v = s.isVarDeclaration(); + if (v) + { + /* Bugzilla 12799: enum a = ...; is a VarDeclaration and + * STCmanifest is already set in parssing stage. So we can + * check this before the semantic() call. + */ + if (v.storage_class & STCmanifest) + return 0; + if (v._scope) + v.semantic(null); + if (v.storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCctfe | STCtemplateparameter)) + return 0; + if (v.isField() && v.sem >= SemanticDone) + return 0; + return 1; + } + return 0; + } + } + + SV sv; + for (size_t i = 0; i < members.dim; i++) + { + Dsymbol s = (*members)[i]; + if (s.apply(&SV.func, &sv)) + goto L1; + } + finalizeSize(null); + L1: + } + if (!members) + { + error(loc, "unknown size"); + } + else if (sizeok != SIZEOKdone) + { + error(loc, "no size yet for forward reference"); + //*(char*)0=0; + } + return structsize; + } + + abstract void finalizeSize(Scope* sc); + + /**************************** + * Do byte or word alignment as necessary. + * Align sizes of 0, as we may not know array sizes yet. + * + * alignment: struct alignment that is in effect + * size: alignment requirement of field + */ + final static void alignmember(structalign_t alignment, uint size, uint* poffset) + { + //printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset); + switch (alignment) + { + case cast(structalign_t)1: + // No alignment + break; + case cast(structalign_t)STRUCTALIGN_DEFAULT: + // Alignment in Target::fieldalignsize must match what the + // corresponding C compiler's default alignment behavior is. + assert(size > 0 && !(size & (size - 1))); + *poffset = (*poffset + size - 1) & ~(size - 1); + break; + default: + // Align on alignment boundary, which must be a positive power of 2 + assert(alignment > 0 && !(alignment & (alignment - 1))); + *poffset = (*poffset + alignment - 1) & ~(alignment - 1); + break; + } + } + + /**************************************** + * Place a member (mem) into an aggregate (agg), which can be a struct, union or class + * Returns: + * offset to place field at + * + * nextoffset: next location in aggregate + * memsize: size of member + * memalignsize: size of member for alignment purposes + * alignment: alignment in effect for this member + * paggsize: size of aggregate (updated) + * paggalignsize: size of aggregate for alignment purposes (updated) + * isunion: the aggregate is a union + */ + final static uint placeField(uint* nextoffset, uint memsize, uint memalignsize, structalign_t alignment, uint* paggsize, uint* paggalignsize, bool isunion) + { + uint ofs = *nextoffset; + alignmember(alignment, memalignsize, &ofs); + uint memoffset = ofs; + ofs += memsize; + if (ofs > *paggsize) + *paggsize = ofs; + if (!isunion) + *nextoffset = ofs; + if (alignment == STRUCTALIGN_DEFAULT) + { + if (global.params.is64bit && memalignsize == 16) + { + } + else if (8 < memalignsize) + memalignsize = 8; + } + else + { + if (memalignsize < alignment) + memalignsize = alignment; + } + if (*paggalignsize < memalignsize) + *paggalignsize = memalignsize; + return memoffset; + } + + final Type getType() + { + return type; + } + + /**************************************** + * If field[indx] is not part of a union, return indx. + * Otherwise, return the lowest field index of the union. + */ + final int firstFieldInUnion(int indx) + { + if (isUnionDeclaration()) + return 0; + VarDeclaration vd = fields[indx]; + int firstNonZero = indx; // first index in the union with non-zero size + for (;;) + { + if (indx == 0) + return firstNonZero; + VarDeclaration v = fields[indx - 1]; + if (v.offset != vd.offset) + return firstNonZero; + --indx; + /* If it is a zero-length field, it's ambiguous: we don't know if it is + * in the union unless we find an earlier non-zero sized field with the + * same offset. + */ + if (v.size(loc) != 0) + firstNonZero = indx; + } + } + + /**************************************** + * Count the number of fields starting at firstIndex which are part of the + * same union as field[firstIndex]. If not a union, return 1. + */ + final int numFieldsInUnion(int firstIndex) + { + VarDeclaration vd = fields[firstIndex]; + /* If it is a zero-length field, AND we can't find an earlier non-zero + * sized field with the same offset, we assume it's not part of a union. + */ + if (vd.size(loc) == 0 && !isUnionDeclaration() && firstFieldInUnion(firstIndex) == firstIndex) + return 1; + int count = 1; + for (size_t i = firstIndex + 1; i < fields.dim; ++i) + { + VarDeclaration v = fields[i]; + // If offsets are different, they are not in the same union + if (v.offset != vd.offset) + break; + ++count; + } + return count; + } + + // is aggregate deprecated? + final bool isDeprecated() + { + return isdeprecated; + } + + /**************************************** + * Returns true if there's an extra member which is the 'this' + * pointer to the enclosing context (enclosing aggregate or function) + */ + final bool isNested() + { + return enclosing !is null; + } + + final void makeNested() + { + if (enclosing) // if already nested + return; + if (sizeok == SIZEOKdone) + return; + if (isUnionDeclaration() || isInterfaceDeclaration()) + return; + if (storage_class & STCstatic) + return; + // If nested struct, add in hidden 'this' pointer to outer scope + Dsymbol s = toParent2(); + if (!s) + return; + AggregateDeclaration ad = s.isAggregateDeclaration(); + FuncDeclaration fd = s.isFuncDeclaration(); + Type t = null; + if (fd) + { + enclosing = fd; + AggregateDeclaration agg = fd.isMember2(); + t = agg ? agg.handleType() : Type.tvoidptr; + } + else if (ad) + { + if (isClassDeclaration() && ad.isClassDeclaration()) + { + enclosing = ad; + } + else if (isStructDeclaration()) + { + if (TemplateInstance ti = ad.parent.isTemplateInstance()) + { + enclosing = ti.enclosing; + } + } + t = ad.handleType(); + } + if (enclosing) + { + //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing->toChars()); + assert(t); + if (t.ty == Tstruct) + t = Type.tvoidptr; // t should not be a ref type + assert(!vthis); + vthis = new ThisDeclaration(loc, t); + //vthis->storage_class |= STCref; + members.push(vthis); + } + } + + final bool isExport() + { + return protection.kind == PROTexport; + } + + /******************************************* + * Look for constructor declaration. + */ + final Dsymbol searchCtor() + { + Dsymbol s = search(Loc(), Id.ctor); + if (s) + { + if (!(s.isCtorDeclaration() || s.isTemplateDeclaration() || s.isOverloadSet())) + { + s.error("is not a constructor; identifiers starting with __ are reserved for the implementation"); + errors = true; + s = null; + } + } + return s; + } + + final Prot prot() + { + return protection; + } + + // 'this' type + final Type handleType() + { + return type; + } + + // Back end + Symbol* stag; // tag symbol for debug data + Symbol* sinit; + + final AggregateDeclaration isAggregateDeclaration() + { + return this; + } + + void accept(Visitor v) + { + v.visit(this); + } +} diff --git a/src/aliasthis.c b/src/aliasthis.c deleted file mode 100644 index 9627e63bd536..000000000000 --- a/src/aliasthis.c +++ /dev/null @@ -1,149 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 2009-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/aliasthis.c - */ - -#include -#include - -#include "mars.h" -#include "identifier.h" -#include "aliasthis.h" -#include "scope.h" -#include "aggregate.h" -#include "dsymbol.h" -#include "mtype.h" -#include "declaration.h" -#include "tokens.h" - -Expression *resolveAliasThis(Scope *sc, Expression *e) -{ - AggregateDeclaration *ad = isAggregate(e->type); - - if (ad && ad->aliasthis) - { - Loc loc = e->loc; - Type *tthis = (e->op == TOKtype ? e->type : NULL); - e = new DotIdExp(loc, e, ad->aliasthis->ident); - e = e->semantic(sc); - if (tthis && ad->aliasthis->needThis()) - { - if (e->op == TOKvar) - { - if (FuncDeclaration *f = ((VarExp *)e)->var->isFuncDeclaration()) - { - // Bugzilla 13009: Support better match for the overloaded alias this. - Type *t; - f = f->overloadModMatch(loc, tthis, t); - if (f && t) - { - e = new VarExp(loc, f, 0); // use better match - e = new CallExp(loc, e); - goto L1; - } - } - } - /* non-@property function is not called inside typeof(), - * so resolve it ahead. - */ - { - int save = sc->intypeof; - sc->intypeof = 1; // bypass "need this" error check - e = resolveProperties(sc, e); - sc->intypeof = save; - } - - L1: - e = new TypeExp(loc, new TypeTypeof(loc, e)); - e = e->semantic(sc); - } - e = resolveProperties(sc, e); - } - - return e; -} - -AliasThis::AliasThis(Loc loc, Identifier *ident) - : Dsymbol(NULL) // it's anonymous (no identifier) -{ - this->loc = loc; - this->ident = ident; -} - -Dsymbol *AliasThis::syntaxCopy(Dsymbol *s) -{ - assert(!s); - /* Since there is no semantic information stored here, - * we don't need to copy it. - */ - return this; -} - -void AliasThis::semantic(Scope *sc) -{ - Dsymbol *p = sc->parent->pastMixin(); - AggregateDeclaration *ad = p->isAggregateDeclaration(); - if (!ad) - { - ::error(loc, "alias this can only be a member of aggregate, not %s %s", - p->kind(), p->toChars()); - return; - } - - assert(ad->members); - Dsymbol *s = ad->search(loc, ident); - if (!s) - { - s = sc->search(loc, ident, NULL); - if (s) - ::error(loc, "%s is not a member of %s", s->toChars(), ad->toChars()); - else - ::error(loc, "undefined identifier %s", ident->toChars()); - return; - } - else if (ad->aliasthis && s != ad->aliasthis) - { - ::error(loc, "there can be only one alias this"); - return; - } - - if (ad->type->ty == Tstruct && ((TypeStruct *)ad->type)->sym != ad) - { - AggregateDeclaration *ad2 = ((TypeStruct *)ad->type)->sym; - assert(ad2->type == Type::terror); - ad->aliasthis = ad2->aliasthis; - return; - } - - /* disable the alias this conversion so the implicit conversion check - * doesn't use it. - */ - ad->aliasthis = NULL; - - Dsymbol *sx = s; - if (sx->isAliasDeclaration()) - sx = sx->toAlias(); - Declaration *d = sx->isDeclaration(); - if (d && !d->isTupleDeclaration()) - { - Type *t = d->type; - assert(t); - if (ad->type->implicitConvTo(t) > MATCHnomatch) - { - ::error(loc, "alias this is not reachable as %s already converts to %s", ad->toChars(), t->toChars()); - } - } - - ad->aliasthis = s; -} - -const char *AliasThis::kind() -{ - return "alias this"; -} diff --git a/src/aliasthis.d b/src/aliasthis.d new file mode 100644 index 000000000000..c0a57bbaa14b --- /dev/null +++ b/src/aliasthis.d @@ -0,0 +1,147 @@ +// 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.aliasthis; + +import ddmd.aggregate, ddmd.declaration, ddmd.dscope, ddmd.dsymbol, ddmd.errors, ddmd.expression, ddmd.func, ddmd.globals, ddmd.hdrgen, ddmd.identifier, ddmd.mtype, ddmd.opover, ddmd.root.outbuffer, ddmd.tokens, ddmd.visitor; + +/**************************************************************/ +extern (C++) final class AliasThis : Dsymbol +{ +public: + // alias Identifier this; + Identifier ident; + + // it's anonymous (no identifier) + extern (D) this(Loc loc, Identifier ident) + { + super(null); + this.loc = loc; + this.ident = ident; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + assert(!s); + /* Since there is no semantic information stored here, + * we don't need to copy it. + */ + return this; + } + + void semantic(Scope* sc) + { + Dsymbol p = sc.parent.pastMixin(); + AggregateDeclaration ad = p.isAggregateDeclaration(); + if (!ad) + { + .error(loc, "alias this can only be a member of aggregate, not %s %s", p.kind(), p.toChars()); + return; + } + assert(ad.members); + Dsymbol s = ad.search(loc, ident); + if (!s) + { + s = sc.search(loc, ident, null); + if (s) + .error(loc, "%s is not a member of %s", s.toChars(), ad.toChars()); + else + .error(loc, "undefined identifier %s", ident.toChars()); + return; + } + else if (ad.aliasthis && s != ad.aliasthis) + { + .error(loc, "there can be only one alias this"); + return; + } + if (ad.type.ty == Tstruct && (cast(TypeStruct)ad.type).sym != ad) + { + AggregateDeclaration ad2 = (cast(TypeStruct)ad.type).sym; + assert(ad2.type == Type.terror); + ad.aliasthis = ad2.aliasthis; + return; + } + /* disable the alias this conversion so the implicit conversion check + * doesn't use it. + */ + ad.aliasthis = null; + Dsymbol sx = s; + if (sx.isAliasDeclaration()) + sx = sx.toAlias(); + Declaration d = sx.isDeclaration(); + if (d && !d.isTupleDeclaration()) + { + Type t = d.type; + assert(t); + if (ad.type.implicitConvTo(t) > MATCHnomatch) + { + .error(loc, "alias this is not reachable as %s already converts to %s", ad.toChars(), t.toChars()); + } + } + ad.aliasthis = s; + } + + const(char)* kind() + { + return "alias this"; + } + + AliasThis isAliasThis() + { + return this; + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) Expression resolveAliasThis(Scope* sc, Expression e) +{ + AggregateDeclaration ad = isAggregate(e.type); + if (ad && ad.aliasthis) + { + Loc loc = e.loc; + Type tthis = (e.op == TOKtype ? e.type : null); + e = new DotIdExp(loc, e, ad.aliasthis.ident); + e = e.semantic(sc); + if (tthis && ad.aliasthis.needThis()) + { + if (e.op == TOKvar) + { + if (FuncDeclaration f = (cast(VarExp)e).var.isFuncDeclaration()) + { + // Bugzilla 13009: Support better match for the overloaded alias this. + Type t; + f = f.overloadModMatch(loc, tthis, t); + if (f && t) + { + e = new VarExp(loc, f, 0); // use better match + e = new CallExp(loc, e); + goto L1; + } + } + } + /* non-@property function is not called inside typeof(), + * so resolve it ahead. + */ + { + int save = sc.intypeof; + sc.intypeof = 1; // bypass "need this" error check + e = resolveProperties(sc, e); + sc.intypeof = save; + } + L1: + e = new TypeExp(loc, new TypeTypeof(loc, e)); + e = e.semantic(sc); + } + e = resolveProperties(sc, e); + } + return e; +} diff --git a/src/apply.c b/src/apply.c deleted file mode 100644 index 0eaf61ed3c94..000000000000 --- a/src/apply.c +++ /dev/null @@ -1,145 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/apply.c - */ - -#include -#include - -#include "mars.h" -#include "expression.h" -#include "visitor.h" - - -/************************************** - * An Expression tree walker that will visit each Expression e in the tree, - * in depth-first evaluation order, and call fp(e,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. - */ - -class PostorderExpressionVisitor : public StoppableVisitor -{ -public: - StoppableVisitor *v; - PostorderExpressionVisitor(StoppableVisitor *v) : v(v) {} - - bool doCond(Expression *e) - { - if (!stop && e) - e->accept(this); - return stop; - } - bool doCond(Expressions *e) - { - if (!e) - return false; - for (size_t i = 0; i < e->dim && !stop; i++) - doCond((*e)[i]); - return stop; - } - bool applyTo(Expression *e) - { - e->accept(v); - stop = v->stop; - return true; - } - - void visit(Expression *e) - { - applyTo(e); - } - - void visit(NewExp *e) - { - //printf("NewExp::apply(): %s\n", toChars()); - - doCond(e->thisexp) || doCond(e->newargs) || doCond(e->arguments) || applyTo(e); - } - - void visit(NewAnonClassExp *e) - { - //printf("NewAnonClassExp::apply(): %s\n", toChars()); - - doCond(e->thisexp) || doCond(e->newargs) || doCond(e->arguments) || applyTo(e); - } - - void visit(UnaExp *e) - { - doCond(e->e1) || applyTo(e); - } - - void visit(BinExp *e) - { - doCond(e->e1) || doCond(e->e2) || applyTo(e); - } - - void visit(AssertExp *e) - { - //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e->e1) || doCond(e->msg) || applyTo(e); - } - - void visit(CallExp *e) - { - //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e->e1) || doCond(e->arguments) || applyTo(e); - } - - void visit(ArrayExp *e) - { - //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e->e1) || doCond(e->arguments) || applyTo(e); - } - - void visit(SliceExp *e) - { - doCond(e->e1) || doCond(e->lwr) || doCond(e->upr) || applyTo(e); - } - - void visit(ArrayLiteralExp *e) - { - doCond(e->elements) || applyTo(e); - } - - void visit(AssocArrayLiteralExp *e) - { - doCond(e->keys) || doCond(e->values) || applyTo(e); - } - - void visit(StructLiteralExp *e) - { - if (e->stageflags & stageApply) return; - int old = e->stageflags; - e->stageflags |= stageApply; - doCond(e->elements) || applyTo(e); - e->stageflags = old; - } - - void visit(TupleExp *e) - { - doCond(e->e0) || doCond(e->exps) || applyTo(e); - } - - void visit(CondExp *e) - { - doCond(e->econd) || doCond(e->e1) || doCond(e->e2) || applyTo(e); - } -}; - -bool walkPostorder(Expression *e, StoppableVisitor *v) -{ - PostorderExpressionVisitor pv(v); - e->accept(&pv); - return v->stop; -} diff --git a/src/apply.d b/src/apply.d new file mode 100644 index 000000000000..bb8ab370398e --- /dev/null +++ b/src/apply.d @@ -0,0 +1,143 @@ +// 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.apply; + +import ddmd.arraytypes, ddmd.expression, ddmd.visitor; + +/************************************** + * An Expression tree walker that will visit each Expression e in the tree, + * in depth-first evaluation order, and call fp(e,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 PostorderExpressionVisitor : StoppableVisitor +{ + alias visit = super.visit; +public: + StoppableVisitor v; + + extern (D) this(StoppableVisitor v) + { + this.v = v; + } + + bool doCond(Expression e) + { + if (!stop && e) + e.accept(this); + return stop; + } + + bool doCond(Expressions* e) + { + if (!e) + return false; + for (size_t i = 0; i < e.dim && !stop; i++) + doCond((*e)[i]); + return stop; + } + + bool applyTo(Expression e) + { + e.accept(v); + stop = v.stop; + return true; + } + + void visit(Expression e) + { + applyTo(e); + } + + void visit(NewExp e) + { + //printf("NewExp::apply(): %s\n", toChars()); + doCond(e.thisexp) || doCond(e.newargs) || doCond(e.arguments) || applyTo(e); + } + + void visit(NewAnonClassExp e) + { + //printf("NewAnonClassExp::apply(): %s\n", toChars()); + doCond(e.thisexp) || doCond(e.newargs) || doCond(e.arguments) || applyTo(e); + } + + void visit(UnaExp e) + { + doCond(e.e1) || applyTo(e); + } + + void visit(BinExp e) + { + doCond(e.e1) || doCond(e.e2) || applyTo(e); + } + + void visit(AssertExp e) + { + //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.msg) || applyTo(e); + } + + void visit(CallExp e) + { + //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.arguments) || applyTo(e); + } + + void visit(ArrayExp e) + { + //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.arguments) || applyTo(e); + } + + void visit(SliceExp e) + { + doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e); + } + + void visit(ArrayLiteralExp e) + { + doCond(e.elements) || applyTo(e); + } + + void visit(AssocArrayLiteralExp e) + { + doCond(e.keys) || doCond(e.values) || applyTo(e); + } + + void visit(StructLiteralExp e) + { + if (e.stageflags & stageApply) + return; + int old = e.stageflags; + e.stageflags |= stageApply; + doCond(e.elements) || applyTo(e); + e.stageflags = old; + } + + void visit(TupleExp e) + { + doCond(e.e0) || doCond(e.exps) || applyTo(e); + } + + void visit(CondExp e) + { + doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e); + } +} + +extern (C++) bool walkPostorder(Expression e, StoppableVisitor v) +{ + scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v); + e.accept(pv); + return v.stop; +} diff --git a/src/argtypes.c b/src/argtypes.c deleted file mode 100644 index 33c80b8dcb42..000000000000 --- a/src/argtypes.c +++ /dev/null @@ -1,491 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 2010-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/argtypes.c - */ - -#include -#include - -#include "mars.h" -#include "dsymbol.h" -#include "mtype.h" -#include "scope.h" -#include "init.h" -#include "expression.h" -#include "attrib.h" -#include "declaration.h" -#include "template.h" -#include "id.h" -#include "enum.h" -#include "import.h" -#include "aggregate.h" -#include "hdrgen.h" - -/**************************************************** - * This breaks a type down into 'simpler' types that can be passed to a function - * in registers, and returned in registers. - * It's highly platform dependent. - * Returning a tuple of zero length means the type cannot be passed/returned in registers. - */ - -TypeTuple *toArgTypes(Type *t) -{ - class ToArgTypes : public Visitor - { - public: - TypeTuple *result; - - ToArgTypes() - { - result = NULL; - } - - void visit(Type *) - { - // not valid for a parameter - } - - void visit(TypeError *) - { - result = new TypeTuple(Type::terror); - } - - void visit(TypeBasic *t) - { - Type *t1 = NULL; - Type *t2 = NULL; - switch (t->ty) - { - case Tvoid: - return; - - case Tbool: - case Tint8: - case Tuns8: - case Tint16: - case Tuns16: - case Tint32: - case Tuns32: - case Tfloat32: - case Tint64: - case Tuns64: - case Tint128: - case Tuns128: - case Tfloat64: - case Tfloat80: - t1 = t; - break; - - case Timaginary32: - t1 = Type::tfloat32; - break; - - case Timaginary64: - t1 = Type::tfloat64; - break; - - case Timaginary80: - t1 = Type::tfloat80; - break; - - case Tcomplex32: - if (global.params.is64bit) - t1 = Type::tfloat64; - else - { - t1 = Type::tfloat64; - t2 = Type::tfloat64; - } - break; - - case Tcomplex64: - t1 = Type::tfloat64; - t2 = Type::tfloat64; - break; - - case Tcomplex80: - t1 = Type::tfloat80; - t2 = Type::tfloat80; - break; - - case Tchar: - t1 = Type::tuns8; - break; - - case Twchar: - t1 = Type::tuns16; - break; - - case Tdchar: - t1 = Type::tuns32; - break; - - default: - assert(0); - } - - if (t1) - { - if (t2) - result = new TypeTuple(t1, t2); - else - result = new TypeTuple(t1); - } - else - result = new TypeTuple(); - } - - void visit(TypeVector *t) - { - result = new TypeTuple(t); - } - - void visit(TypeSArray *t) - { - if (t->dim) - { - /* Should really be done as if it were a struct with dim members - * of the array's elements. - * I.e. int[2] should be done like struct S { int a; int b; } - */ - dinteger_t sz = t->dim->toInteger(); - // T[1] should be passed like T - if (sz == 1) - { - t->next->accept(this); - return; - } - } - result = new TypeTuple(); // pass on the stack for efficiency - } - - void visit(TypeAArray *) - { - result = new TypeTuple(Type::tvoidptr); - } - - void visit(TypePointer *) - { - result = new TypeTuple(Type::tvoidptr); - } - - /************************************* - * Convert a floating point type into the equivalent integral type. - */ - - static Type *mergeFloatToInt(Type *t) - { - switch (t->ty) - { - case Tfloat32: - case Timaginary32: - t = Type::tint32; - break; - case Tfloat64: - case Timaginary64: - case Tcomplex32: - t = Type::tint64; - break; - default: - #ifdef DEBUG - printf("mergeFloatToInt() %s\n", t->toChars()); - #endif - assert(0); - } - return t; - } - - /************************************* - * This merges two types into an 8byte type. - */ - - static Type *argtypemerge(Type *t1, Type *t2, unsigned offset2) - { - //printf("argtypemerge(%s, %s, %d)\n", t1 ? t1->toChars() : "", t2 ? t2->toChars() : "", offset2); - if (!t1) - { assert(!t2 || offset2 == 0); - return t2; - } - if (!t2) - return t1; - - unsigned sz1 = (unsigned)t1->size(Loc()); - unsigned sz2 = (unsigned)t2->size(Loc()); - - if (t1->ty != t2->ty && - (t1->ty == Tfloat80 || t2->ty == Tfloat80)) - return NULL; - - // [float,float] => [cfloat] - if (t1->ty == Tfloat32 && t2->ty == Tfloat32 && offset2 == 4) - return Type::tfloat64; - - // Merging floating and non-floating types produces the non-floating type - if (t1->isfloating()) - { - if (!t2->isfloating()) - t1 = mergeFloatToInt(t1); - } - else if (t2->isfloating()) - t2 = mergeFloatToInt(t2); - - Type *t; - - // Pick type with larger size - if (sz1 < sz2) - t = t2; - else - t = t1; - - // If t2 does not lie within t1, need to increase the size of t to enclose both - if (offset2 && sz1 < offset2 + sz2) - { - switch (offset2 + sz2) - { - case 2: - t = Type::tint16; - break; - case 3: - case 4: - t = Type::tint32; - break; - case 5: - case 6: - case 7: - case 8: - t = Type::tint64; - break; - default: - assert(0); - } - } - return t; - } - - void visit(TypeDArray *) - { - /* Should be done as if it were: - * struct S { size_t length; void* ptr; } - */ - if (global.params.is64bit && !global.params.isLP64) - { - // For AMD64 ILP32 ABI, D arrays fit into a single integer register. - unsigned offset = (unsigned)Type::tsize_t->size(Loc()); - Type *t = argtypemerge(Type::tsize_t, Type::tvoidptr, offset); - if (t) - { - result = new TypeTuple(t); - return; - } - } - result = new TypeTuple(Type::tsize_t, Type::tvoidptr); - } - - void visit(TypeDelegate *) - { - /* Should be done as if it were: - * struct S { size_t length; void* ptr; } - */ - if (global.params.is64bit && !global.params.isLP64) - { - // For AMD64 ILP32 ABI, delegates fit into a single integer register. - unsigned offset = (unsigned)Type::tsize_t->size(Loc()); - Type *t = argtypemerge(Type::tsize_t, Type::tvoidptr, offset); - if (t) - { - result = new TypeTuple(t); - return; - } - } - result = new TypeTuple(Type::tvoidptr, Type::tvoidptr); - } - - void visit(TypeStruct *t) - { - //printf("TypeStruct::toArgTypes() %s\n", t->toChars()); - if (!t->sym->isPOD() || t->sym->fields.dim == 0) - { - Lmemory: - //printf("\ttoArgTypes() %s => [ ]\n", t->toChars()); - result = new TypeTuple(); // pass on the stack - return; - } - Type *t1 = NULL; - Type *t2 = NULL; - d_uns64 sz = t->size(Loc()); - assert(sz < 0xFFFFFFFF); - switch ((unsigned)sz) - { - case 1: - t1 = Type::tint8; - break; - case 2: - t1 = Type::tint16; - break; - case 3: - if (!global.params.is64bit) - goto Lmemory; - case 4: - t1 = Type::tint32; - break; - case 5: - case 6: - case 7: - if (!global.params.is64bit) - goto Lmemory; - case 8: - t1 = Type::tint64; - break; - case 16: - t1 = NULL; // could be a TypeVector - break; - case 9: - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - if (!global.params.is64bit) - goto Lmemory; - t1 = NULL; - break; - default: - goto Lmemory; - } - if (global.params.is64bit && t->sym->fields.dim) - { - #if 1 - t1 = NULL; - for (size_t i = 0; i < t->sym->fields.dim; i++) - { - VarDeclaration *f = t->sym->fields[i]; - //printf("f->type = %s\n", f->type->toChars()); - - TypeTuple *tup = toArgTypes(f->type); - if (!tup) - goto Lmemory; - size_t dim = tup->arguments->dim; - Type *ft1 = NULL; - Type *ft2 = NULL; - switch (dim) - { - case 2: - ft1 = (*tup->arguments)[0]->type; - ft2 = (*tup->arguments)[1]->type; - break; - case 1: - if (f->offset < 8) - ft1 = (*tup->arguments)[0]->type; - else - ft2 = (*tup->arguments)[0]->type; - break; - default: - goto Lmemory; - } - - if (f->offset & 7) - { - // Misaligned fields goto Lmemory - unsigned alignsz = f->type->alignsize(); - if (f->offset & (alignsz - 1)) - goto Lmemory; - - // Fields that overlap the 8byte boundary goto Lmemory - d_uns64 fieldsz = f->type->size(Loc()); - if (f->offset < 8 && (f->offset + fieldsz) > 8) - goto Lmemory; - } - - // First field in 8byte must be at start of 8byte - assert(t1 || f->offset == 0); - - if (ft1) - { - t1 = argtypemerge(t1, ft1, f->offset); - if (!t1) - goto Lmemory; - } - - if (ft2) - { - unsigned off2 = f->offset; - if (ft1) - off2 = 8; - if (!t2 && off2 != 8) - goto Lmemory; - assert(t2 || off2 == 8); - t2 = argtypemerge(t2, ft2, off2 - 8); - if (!t2) - goto Lmemory; - } - } - - if (t2) - { - if (t1->isfloating() && t2->isfloating()) - { - if ((t1->ty == Tfloat32 || t1->ty == Tfloat64) && - (t2->ty == Tfloat32 || t2->ty == Tfloat64)) - ; - else - goto Lmemory; - } - else if (t1->isfloating()) - goto Lmemory; - else if (t2->isfloating()) - goto Lmemory; - else - ; - } - #else - if (t->sym->fields.dim == 1) - { - VarDeclaration *f = t->sym->fields[0]; - //printf("f->type = %s\n", f->type->toChars()); - TypeTuple *tup = toArgTypes(f->type); - if (tup) - { - size_t dim = tup->arguments->dim; - if (dim == 1) - t1 = (*tup->arguments)[0]->type; - } - } - #endif - } - - //printf("\ttoArgTypes() %s => [%s,%s]\n", t->toChars(), t1 ? t1->toChars() : "", t2 ? t2->toChars() : ""); - - if (t1) - { - //if (t1) printf("test1: %s => %s\n", toChars(), t1->toChars()); - if (t2) - result = new TypeTuple(t1, t2); - else - result = new TypeTuple(t1); - } - else - goto Lmemory; - } - - void visit(TypeEnum *t) - { - t->toBasetype()->accept(this); - } - - void visit(TypeClass *) - { - result = new TypeTuple(Type::tvoidptr); - } - }; - - ToArgTypes v; - t->accept(&v); - return v.result; -} diff --git a/src/argtypes.d b/src/argtypes.d new file mode 100644 index 000000000000..510ccbbba789 --- /dev/null +++ b/src/argtypes.d @@ -0,0 +1,450 @@ +// 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.argtypes; + +import core.stdc.stdio; +import ddmd.declaration, ddmd.globals, ddmd.mtype, ddmd.visitor; + +/**************************************************** + * This breaks a type down into 'simpler' types that can be passed to a function + * in registers, and returned in registers. + * It's highly platform dependent. + * Returning a tuple of zero length means the type cannot be passed/returned in registers. + */ +extern (C++) TypeTuple toArgTypes(Type t) +{ + extern (C++) final class ToArgTypes : Visitor + { + alias visit = super.visit; + public: + TypeTuple result; + + extern (D) this() + { + result = null; + } + + void visit(Type) + { + // not valid for a parameter + } + + void visit(TypeError) + { + result = new TypeTuple(Type.terror); + } + + void visit(TypeBasic t) + { + Type t1 = null; + Type t2 = null; + switch (t.ty) + { + case Tvoid: + return; + case Tbool: + case Tint8: + case Tuns8: + case Tint16: + case Tuns16: + case Tint32: + case Tuns32: + case Tfloat32: + case Tint64: + case Tuns64: + case Tint128: + case Tuns128: + case Tfloat64: + case Tfloat80: + t1 = t; + break; + case Timaginary32: + t1 = Type.tfloat32; + break; + case Timaginary64: + t1 = Type.tfloat64; + break; + case Timaginary80: + t1 = Type.tfloat80; + break; + case Tcomplex32: + if (global.params.is64bit) + t1 = Type.tfloat64; + else + { + t1 = Type.tfloat64; + t2 = Type.tfloat64; + } + break; + case Tcomplex64: + t1 = Type.tfloat64; + t2 = Type.tfloat64; + break; + case Tcomplex80: + t1 = Type.tfloat80; + t2 = Type.tfloat80; + break; + case Tchar: + t1 = Type.tuns8; + break; + case Twchar: + t1 = Type.tuns16; + break; + case Tdchar: + t1 = Type.tuns32; + break; + default: + assert(0); + } + if (t1) + { + if (t2) + result = new TypeTuple(t1, t2); + else + result = new TypeTuple(t1); + } + else + result = new TypeTuple(); + } + + void visit(TypeVector t) + { + result = new TypeTuple(t); + } + + void visit(TypeSArray t) + { + if (t.dim) + { + /* Should really be done as if it were a struct with dim members + * of the array's elements. + * I.e. int[2] should be done like struct S { int a; int b; } + */ + dinteger_t sz = t.dim.toInteger(); + // T[1] should be passed like T + if (sz == 1) + { + t.next.accept(this); + return; + } + } + result = new TypeTuple(); // pass on the stack for efficiency + } + + void visit(TypeAArray) + { + result = new TypeTuple(Type.tvoidptr); + } + + void visit(TypePointer) + { + result = new TypeTuple(Type.tvoidptr); + } + + /************************************* + * Convert a floating point type into the equivalent integral type. + */ + static Type mergeFloatToInt(Type t) + { + switch (t.ty) + { + case Tfloat32: + case Timaginary32: + t = Type.tint32; + break; + case Tfloat64: + case Timaginary64: + case Tcomplex32: + t = Type.tint64; + break; + default: + debug + { + printf("mergeFloatToInt() %s\n", t.toChars()); + } + assert(0); + } + return t; + } + + /************************************* + * This merges two types into an 8byte type. + */ + static Type argtypemerge(Type t1, Type t2, uint offset2) + { + //printf("argtypemerge(%s, %s, %d)\n", t1 ? t1->toChars() : "", t2 ? t2->toChars() : "", offset2); + if (!t1) + { + assert(!t2 || offset2 == 0); + return t2; + } + if (!t2) + return t1; + uint sz1 = cast(uint)t1.size(Loc()); + uint sz2 = cast(uint)t2.size(Loc()); + if (t1.ty != t2.ty && (t1.ty == Tfloat80 || t2.ty == Tfloat80)) + return null; + // [float,float] => [cfloat] + if (t1.ty == Tfloat32 && t2.ty == Tfloat32 && offset2 == 4) + return Type.tfloat64; + // Merging floating and non-floating types produces the non-floating type + if (t1.isfloating()) + { + if (!t2.isfloating()) + t1 = mergeFloatToInt(t1); + } + else if (t2.isfloating()) + t2 = mergeFloatToInt(t2); + Type t; + // Pick type with larger size + if (sz1 < sz2) + t = t2; + else + t = t1; + // If t2 does not lie within t1, need to increase the size of t to enclose both + if (offset2 && sz1 < offset2 + sz2) + { + switch (offset2 + sz2) + { + case 2: + t = Type.tint16; + break; + case 3: + case 4: + t = Type.tint32; + break; + case 5: + case 6: + case 7: + case 8: + t = Type.tint64; + break; + default: + assert(0); + } + } + return t; + } + + void visit(TypeDArray) + { + /* Should be done as if it were: + * struct S { size_t length; void* ptr; } + */ + if (global.params.is64bit && !global.params.isLP64) + { + // For AMD64 ILP32 ABI, D arrays fit into a single integer register. + uint offset = cast(uint)Type.tsize_t.size(Loc()); + Type t = argtypemerge(Type.tsize_t, Type.tvoidptr, offset); + if (t) + { + result = new TypeTuple(t); + return; + } + } + result = new TypeTuple(Type.tsize_t, Type.tvoidptr); + } + + void visit(TypeDelegate) + { + /* Should be done as if it were: + * struct S { size_t length; void* ptr; } + */ + if (global.params.is64bit && !global.params.isLP64) + { + // For AMD64 ILP32 ABI, delegates fit into a single integer register. + uint offset = cast(uint)Type.tsize_t.size(Loc()); + Type t = argtypemerge(Type.tsize_t, Type.tvoidptr, offset); + if (t) + { + result = new TypeTuple(t); + return; + } + } + result = new TypeTuple(Type.tvoidptr, Type.tvoidptr); + } + + void visit(TypeStruct t) + { + //printf("TypeStruct::toArgTypes() %s\n", t->toChars()); + if (!t.sym.isPOD() || t.sym.fields.dim == 0) + { + Lmemory: + //printf("\ttoArgTypes() %s => [ ]\n", t->toChars()); + result = new TypeTuple(); // pass on the stack + return; + } + Type t1 = null; + Type t2 = null; + d_uns64 sz = t.size(Loc()); + assert(sz < 0xFFFFFFFF); + switch (cast(uint)sz) + { + case 1: + t1 = Type.tint8; + break; + case 2: + t1 = Type.tint16; + break; + case 3: + if (!global.params.is64bit) + goto Lmemory; + case 4: + t1 = Type.tint32; + break; + case 5: + case 6: + case 7: + if (!global.params.is64bit) + goto Lmemory; + case 8: + t1 = Type.tint64; + break; + case 16: + t1 = null; // could be a TypeVector + break; + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + if (!global.params.is64bit) + goto Lmemory; + t1 = null; + break; + default: + goto Lmemory; + } + if (global.params.is64bit && t.sym.fields.dim) + { + version (all) + { + t1 = null; + for (size_t i = 0; i < t.sym.fields.dim; i++) + { + VarDeclaration f = t.sym.fields[i]; + //printf("f->type = %s\n", f->type->toChars()); + TypeTuple tup = toArgTypes(f.type); + if (!tup) + goto Lmemory; + size_t dim = tup.arguments.dim; + Type ft1 = null; + Type ft2 = null; + switch (dim) + { + case 2: + ft1 = (*tup.arguments)[0].type; + ft2 = (*tup.arguments)[1].type; + break; + case 1: + if (f.offset < 8) + ft1 = (*tup.arguments)[0].type; + else + ft2 = (*tup.arguments)[0].type; + break; + default: + goto Lmemory; + } + if (f.offset & 7) + { + // Misaligned fields goto Lmemory + uint alignsz = f.type.alignsize(); + if (f.offset & (alignsz - 1)) + goto Lmemory; + // Fields that overlap the 8byte boundary goto Lmemory + d_uns64 fieldsz = f.type.size(Loc()); + if (f.offset < 8 && (f.offset + fieldsz) > 8) + goto Lmemory; + } + // First field in 8byte must be at start of 8byte + assert(t1 || f.offset == 0); + if (ft1) + { + t1 = argtypemerge(t1, ft1, f.offset); + if (!t1) + goto Lmemory; + } + if (ft2) + { + uint off2 = f.offset; + if (ft1) + off2 = 8; + if (!t2 && off2 != 8) + goto Lmemory; + assert(t2 || off2 == 8); + t2 = argtypemerge(t2, ft2, off2 - 8); + if (!t2) + goto Lmemory; + } + } + if (t2) + { + if (t1.isfloating() && t2.isfloating()) + { + if ((t1.ty == Tfloat32 || t1.ty == Tfloat64) && (t2.ty == Tfloat32 || t2.ty == Tfloat64)) + { + } + else + goto Lmemory; + } + else if (t1.isfloating()) + goto Lmemory; + else if (t2.isfloating()) + goto Lmemory; + else + { + } + } + } + else + { + if (t.sym.fields.dim == 1) + { + VarDeclaration f = t.sym.fields[0]; + //printf("f->type = %s\n", f->type->toChars()); + TypeTuple tup = toArgTypes(f.type); + if (tup) + { + size_t dim = tup.arguments.dim; + if (dim == 1) + t1 = (*tup.arguments)[0].type; + } + } + } + } + //printf("\ttoArgTypes() %s => [%s,%s]\n", t->toChars(), t1 ? t1->toChars() : "", t2 ? t2->toChars() : ""); + if (t1) + { + //if (t1) printf("test1: %s => %s\n", toChars(), t1->toChars()); + if (t2) + result = new TypeTuple(t1, t2); + else + result = new TypeTuple(t1); + } + else + goto Lmemory; + } + + void visit(TypeEnum t) + { + t.toBasetype().accept(this); + } + + void visit(TypeClass) + { + result = new TypeTuple(Type.tvoidptr); + } + } + + scope ToArgTypes v = new ToArgTypes(); + t.accept(v); + return v.result; +} diff --git a/src/arrayop.c b/src/arrayop.c deleted file mode 100644 index 8f93e5ab458a..000000000000 --- a/src/arrayop.c +++ /dev/null @@ -1,642 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/arrayop.c - */ - -#include -#include -#include - -#include "rmem.h" - -#include "aav.h" - -#include "expression.h" -#include "statement.h" -#include "mtype.h" -#include "declaration.h" -#include "scope.h" -#include "id.h" -#include "module.h" -#include "init.h" -#include "tokens.h" - -void buildArrayIdent(Expression *e, OutBuffer *buf, Expressions *arguments); -Expression *buildArrayLoop(Expression *e, Parameters *fparams); - -/************************************** - * Hash table of array op functions already generated or known about. - */ - -AA *arrayfuncs; - -/************************************** - * Structure to contain information needed to insert an array op call - */ - -FuncDeclaration *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc, Loc loc) -{ - Parameters *fparams = new Parameters(); - Expression *loopbody = buildArrayLoop(exp, fparams); - - /* Construct the function body: - * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++) - * loopbody; - * return p; - */ - - Parameter *p = (*fparams)[0]; - // foreach (i; 0 .. p.length) - Statement *s1 = new ForeachRangeStatement(Loc(), TOKforeach, - new Parameter(0, NULL, Id::p, NULL), - new IntegerExp(Loc(), 0, Type::tsize_t), - new ArrayLengthExp(Loc(), new IdentifierExp(Loc(), p->ident)), - new ExpStatement(Loc(), loopbody), - Loc()); - //printf("%s\n", s1->toChars()); - Statement *s2 = new ReturnStatement(Loc(), new IdentifierExp(Loc(), p->ident)); - //printf("s2: %s\n", s2->toChars()); - Statement *fbody = new CompoundStatement(Loc(), s1, s2); - - // Built-in array ops should be @trusted, pure, nothrow and nogc - StorageClass stc = STCtrusted | STCpure | STCnothrow | STCnogc; - - /* Construct the function - */ - TypeFunction *ftype = new TypeFunction(fparams, exp->type, 0, LINKc, stc); - //printf("fd: %s %s\n", ident->toChars(), ftype->toChars()); - FuncDeclaration *fd = new FuncDeclaration(Loc(), Loc(), ident, STCundefined, ftype); - fd->fbody = fbody; - fd->protection = Prot(PROTpublic); - fd->linkage = LINKc; - fd->isArrayOp = 1; - - sc->module->importedFrom->members->push(fd); - - sc = sc->push(); - sc->parent = sc->module->importedFrom; - sc->stc = 0; - sc->linkage = LINKc; - fd->semantic(sc); - fd->semantic2(sc); - unsigned errors = global.startGagging(); - fd->semantic3(sc); - if (global.endGagging(errors)) - { - fd->type = Type::terror; - fd->errors = true; - fd->fbody = NULL; - } - sc->pop(); - - return fd; -} - -/********************************************** - * Check that there are no uses of arrays without []. - */ -bool isArrayOpValid(Expression *e) -{ - if (e->op == TOKslice) - return true; - if (e->op == TOKarrayliteral) - { - Type *t = e->type->toBasetype(); - while (t->ty == Tarray || t->ty == Tsarray) - t = t->nextOf()->toBasetype(); - return (t->ty != Tvoid); - } - Type *tb = e->type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) - { - if (isUnaArrayOp(e->op)) - { - return isArrayOpValid(((UnaExp *)e)->e1); - } - if (isBinArrayOp(e->op) || - isBinAssignArrayOp(e->op) || - e->op == TOKassign) - { - BinExp *be = (BinExp *)e; - return isArrayOpValid(be->e1) && isArrayOpValid(be->e2); - } - if (e->op == TOKconstruct) - { - BinExp *be = (BinExp *)e; - return be->e1->op == TOKslice && isArrayOpValid(be->e2); - } - if (e->op == TOKcall) - { - return false; // TODO: Decide if [] is required after arrayop calls. - } - else - { - return false; - } - } - return true; -} - -bool isNonAssignmentArrayOp(Expression *e) -{ - if (e->op == TOKslice) - return isNonAssignmentArrayOp(((SliceExp *)e)->e1); - Type *tb = e->type->toBasetype(); - - if (tb->ty == Tarray || tb->ty == Tsarray) - { - return (isUnaArrayOp(e->op) || isBinArrayOp(e->op)); - } - return false; -} - -bool checkNonAssignmentArrayOp(Expression *e, bool suggestion) -{ - if (isNonAssignmentArrayOp(e)) - { - const char *s = ""; - if (suggestion) - s = " (possible missing [])"; - e->error("array operation %s without destination memory not allowed%s", e->toChars(), s); - return true; - } - return false; -} - -/*********************************** - * Construct the array operation expression. - */ - -Expression *arrayOp(BinExp *e, Scope *sc) -{ - //printf("BinExp::arrayOp() %s\n", toChars()); - - Type *tb = e->type->toBasetype(); - assert(tb->ty == Tarray || tb->ty == Tsarray); - Type *tbn = tb->nextOf()->toBasetype(); - if (tbn->ty == Tvoid) - { - e->error("cannot perform array operations on void[] arrays"); - return new ErrorExp(); - } - if (!isArrayOpValid(e)) - { - e->error("invalid array operation %s (possible missing [])", e->toChars()); - return new ErrorExp(); - } - - Expressions *arguments = new Expressions(); - - /* The expression to generate an array operation for is mangled - * into a name to use as the array operation function name. - * Mangle in the operands and operators in RPN order, and type. - */ - OutBuffer buf; - buf.writestring("_array"); - buildArrayIdent(e, &buf, arguments); - buf.writeByte('_'); - - /* Append deco of array element type - */ - buf.writestring(e->type->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco); - - char *name = buf.peekString(); - Identifier *ident = Identifier::idPool(name); - - FuncDeclaration **pFd = (FuncDeclaration **)dmd_aaGet(&arrayfuncs, (void *)ident); - FuncDeclaration *fd = *pFd; - - if (!fd) - fd = buildArrayOp(ident, e, sc, e->loc); - - if (fd && fd->errors) - { - const char *fmt; - if (tbn->ty == Tstruct || tbn->ty == Tclass) - fmt = "invalid array operation '%s' because %s doesn't support necessary arithmetic operations"; - else if (!tbn->isscalar()) - fmt = "invalid array operation '%s' because %s is not a scalar type"; - else - fmt = "invalid array operation '%s' for element type %s"; - - e->error(fmt, e->toChars(), tbn->toChars()); - return new ErrorExp(); - } - - *pFd = fd; - - Expression *ev = new VarExp(e->loc, fd); - Expression *ec = new CallExp(e->loc, ev, arguments); - - return ec->semantic(sc); -} - -Expression *arrayOp(BinAssignExp *e, Scope *sc) -{ - //printf("BinAssignExp::arrayOp() %s\n", toChars()); - - /* Check that the elements of e1 can be assigned to - */ - Type *tn = e->e1->type->toBasetype()->nextOf(); - - if (tn && (!tn->isMutable() || !tn->isAssignable())) - { - e->error("slice %s is not mutable", e->e1->toChars()); - return new ErrorExp(); - } - if (e->e1->op == TOKarrayliteral) - { - return e->e1->modifiableLvalue(sc, e->e1); - } - - return arrayOp((BinExp *)e, sc); -} - -/****************************************** - * Construct the identifier for the array operation function, - * and build the argument list to pass to it. - */ - -void buildArrayIdent(Expression *e, OutBuffer *buf, Expressions *arguments) -{ - class BuildArrayIdentVisitor : public Visitor - { - OutBuffer *buf; - Expressions *arguments; - public: - BuildArrayIdentVisitor(OutBuffer *buf, Expressions *arguments) - : buf(buf), arguments(arguments) - { - } - - void visit(Expression *e) - { - buf->writestring("Exp"); - arguments->shift(e); - } - - void visit(CastExp *e) - { - Type *tb = e->type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) - { - e->e1->accept(this); - } - else - visit((Expression *)e); - } - - void visit(ArrayLiteralExp *e) - { - buf->writestring("Slice"); - arguments->shift(e); - } - - void visit(SliceExp *e) - { - buf->writestring("Slice"); - arguments->shift(e); - } - - void visit(AssignExp *e) - { - /* Evaluate assign expressions right to left - */ - e->e2->accept(this); - e->e1->accept(this); - buf->writestring("Assign"); - } - - void visit(BinAssignExp *e) - { - /* Evaluate assign expressions right to left - */ - e->e2->accept(this); - e->e1->accept(this); - const char *s; - switch(e->op) - { - case TOKaddass: s = "Addass"; break; - case TOKminass: s = "Subass"; break; - case TOKmulass: s = "Mulass"; break; - case TOKdivass: s = "Divass"; break; - case TOKmodass: s = "Modass"; break; - case TOKxorass: s = "Xorass"; break; - case TOKandass: s = "Andass"; break; - case TOKorass: s = "Orass"; break; - case TOKpowass: s = "Powass"; break; - default: assert(0); - } - buf->writestring(s); - } - - void visit(NegExp *e) - { - e->e1->accept(this); - buf->writestring("Neg"); - } - - void visit(ComExp *e) - { - e->e1->accept(this); - buf->writestring("Com"); - } - - void visit(BinExp *e) - { - /* Evaluate assign expressions left to right - */ - const char *s = NULL; - switch(e->op) - { - case TOKadd: s = "Add"; break; - case TOKmin: s = "Sub"; break; - case TOKmul: s = "Mul"; break; - case TOKdiv: s = "Div"; break; - case TOKmod: s = "Mod"; break; - case TOKxor: s = "Xor"; break; - case TOKand: s = "And"; break; - case TOKor: s = "Or"; break; - case TOKpow: s = "Pow"; break; - default: break; - } - if (s) - { - Type *tb = e->type->toBasetype(); - Type *t1 = e->e1->type->toBasetype(); - Type *t2 = e->e2->type->toBasetype(); - e->e1->accept(this); - if (t1->ty == Tarray && - (t2->ty == Tarray && !t1->equivalent(tb) || - t2->ty != Tarray && !t1->nextOf()->equivalent(e->e2->type))) - { - // Bugzilla 12780: if A is narrower than B - // A[] op B[] - // A[] op B - buf->writestring("Of"); - buf->writestring(t1->nextOf()->mutableOf()->deco); - } - e->e2->accept(this); - if (t2->ty == Tarray && - (t1->ty == Tarray && !t2->equivalent(tb) || - t1->ty != Tarray && !t2->nextOf()->equivalent(e->e1->type))) - { - // Bugzilla 12780: if B is narrower than A: - // A[] op B[] - // A op B[] - buf->writestring("Of"); - buf->writestring(t2->nextOf()->mutableOf()->deco); - } - buf->writestring(s); - } - else - visit((Expression *)e); - } - }; - - BuildArrayIdentVisitor v(buf, arguments); - e->accept(&v); -} - -/****************************************** - * Construct the inner loop for the array operation function, - * and build the parameter list. - */ - -Expression *buildArrayLoop(Expression *e, Parameters *fparams) -{ - class BuildArrayLoopVisitor : public Visitor - { - Parameters *fparams; - Expression *result; - - public: - BuildArrayLoopVisitor(Parameters *fparams) - : fparams(fparams), result(NULL) - { - } - - void visit(Expression *e) - { - Identifier *id = Identifier::generateId("c", fparams->dim); - Parameter *param = new Parameter(0, e->type, id, NULL); - fparams->shift(param); - result = new IdentifierExp(Loc(), id); - } - - void visit(CastExp *e) - { - Type *tb = e->type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) - { - e->e1->accept(this); - } - else - visit((Expression *)e); - } - - void visit(ArrayLiteralExp *e) - { - Identifier *id = Identifier::generateId("p", fparams->dim); - Parameter *param = new Parameter(STCconst, e->type, id, NULL); - fparams->shift(param); - Expression *ie = new IdentifierExp(Loc(), id); - Expressions *arguments = new Expressions(); - Expression *index = new IdentifierExp(Loc(), Id::p); - arguments->push(index); - result = new ArrayExp(Loc(), ie, arguments); - } - - void visit(SliceExp *e) - { - Identifier *id = Identifier::generateId("p", fparams->dim); - Parameter *param = new Parameter(STCconst, e->type, id, NULL); - fparams->shift(param); - Expression *ie = new IdentifierExp(Loc(), id); - Expressions *arguments = new Expressions(); - Expression *index = new IdentifierExp(Loc(), Id::p); - arguments->push(index); - result = new ArrayExp(Loc(), ie, arguments); - } - - void visit(AssignExp *e) - { - /* Evaluate assign expressions right to left - */ - Expression *ex2 = buildArrayLoop(e->e2); - /* Need the cast because: - * b = c + p[i]; - * where b is a byte fails because (c + p[i]) is an int - * which cannot be implicitly cast to byte. - */ - ex2 = new CastExp(Loc(), ex2, e->e1->type->nextOf()); - Expression *ex1 = buildArrayLoop(e->e1); - Parameter *param = (*fparams)[0]; - param->storageClass = 0; - result = new AssignExp(Loc(), ex1, ex2); - } - - void visit(BinAssignExp *e) - { - /* Evaluate assign expressions right to left - */ - Expression *ex2 = buildArrayLoop(e->e2); - Expression *ex1 = buildArrayLoop(e->e1); - Parameter *param = (*fparams)[0]; - param->storageClass = 0; - switch(e->op) - { - case TOKaddass: result = new AddAssignExp(e->loc, ex1, ex2); return; - case TOKminass: result = new MinAssignExp(e->loc, ex1, ex2); return; - case TOKmulass: result = new MulAssignExp(e->loc, ex1, ex2); return; - case TOKdivass: result = new DivAssignExp(e->loc, ex1, ex2); return; - case TOKmodass: result = new ModAssignExp(e->loc, ex1, ex2); return; - case TOKxorass: result = new XorAssignExp(e->loc, ex1, ex2); return; - case TOKandass: result = new AndAssignExp(e->loc, ex1, ex2); return; - case TOKorass: result = new OrAssignExp(e->loc, ex1, ex2); return; - case TOKpowass: result = new PowAssignExp(e->loc, ex1, ex2); return; - default: - assert(0); - } - } - - void visit(NegExp *e) - { - Expression *ex1 = buildArrayLoop(e->e1); - result = new NegExp(Loc(), ex1); - } - - void visit(ComExp *e) - { - Expression *ex1 = buildArrayLoop(e->e1); - result = new ComExp(Loc(), ex1); - } - - void visit(BinExp *e) - { - if (isBinArrayOp(e->op)) - { - /* Evaluate assign expressions left to right - */ - BinExp *be = (BinExp *)e->copy(); - be->e1 = buildArrayLoop(be->e1); - be->e2 = buildArrayLoop(be->e2); - be->type = NULL; - result = be; - return; - } - else - { - visit((Expression *)e); - return; - } - } - - Expression *buildArrayLoop(Expression *e) - { - e->accept(this); - return result; - } - }; - - BuildArrayLoopVisitor v(fparams); - return v.buildArrayLoop(e); -} - -/*********************************************** - * Test if expression is a unary array op. - */ - -bool isUnaArrayOp(TOK op) -{ - switch (op) - { - case TOKneg: - case TOKtilde: - return true; - default: - break; - } - return false; -} - -/*********************************************** - * Test if expression is a binary array op. - */ - -bool isBinArrayOp(TOK op) -{ - switch (op) - { - case TOKadd: - case TOKmin: - case TOKmul: - case TOKdiv: - case TOKmod: - case TOKxor: - case TOKand: - case TOKor: - case TOKpow: - return true; - default: - break; - } - return false; -} - -/*********************************************** - * Test if expression is a binary assignment array op. - */ - -bool isBinAssignArrayOp(TOK op) -{ - switch (op) - { - case TOKaddass: - case TOKminass: - case TOKmulass: - case TOKdivass: - case TOKmodass: - case TOKxorass: - case TOKandass: - case TOKorass: - case TOKpowass: - return true; - default: - break; - } - return false; -} - -/*********************************************** - * Test if operand is a valid array op operand. - */ - -bool isArrayOpOperand(Expression *e) -{ - //printf("Expression::isArrayOpOperand() %s\n", e->toChars()); - if (e->op == TOKslice) - return true; - if (e->op == TOKarrayliteral) - { - Type *t = e->type->toBasetype(); - while (t->ty == Tarray || t->ty == Tsarray) - t = t->nextOf()->toBasetype(); - return (t->ty != Tvoid); - } - Type *tb = e->type->toBasetype(); - if (tb->ty == Tarray) - { - return (isUnaArrayOp(e->op) || - isBinArrayOp(e->op) || - isBinAssignArrayOp(e->op) || - e->op == TOKassign); - } - return false; -} diff --git a/src/arrayop.d b/src/arrayop.d new file mode 100644 index 000000000000..cd7224adfed8 --- /dev/null +++ b/src/arrayop.d @@ -0,0 +1,637 @@ +// 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.arrayop; + +import ddmd.arraytypes, ddmd.declaration, ddmd.dscope, ddmd.dsymbol, ddmd.expression, ddmd.func, ddmd.globals, ddmd.id, ddmd.identifier, ddmd.mtype, ddmd.root.aav, ddmd.root.outbuffer, ddmd.statement, ddmd.tokens, ddmd.visitor; + +/************************************** + * Hash table of array op functions already generated or known about. + */ +extern (C++) __gshared AA* arrayfuncs; + +/************************************** + * Structure to contain information needed to insert an array op call + */ +extern (C++) FuncDeclaration buildArrayOp(Identifier ident, BinExp exp, Scope* sc, Loc loc) +{ + auto fparams = new Parameters(); + Expression loopbody = buildArrayLoop(exp, fparams); + /* Construct the function body: + * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++) + * loopbody; + * return p; + */ + Parameter p = (*fparams)[0]; + // foreach (i; 0 .. p.length) + Statement s1 = new ForeachRangeStatement(Loc(), TOKforeach, new Parameter(0, null, Id.p, null), new IntegerExp(Loc(), 0, Type.tsize_t), new ArrayLengthExp(Loc(), new IdentifierExp(Loc(), p.ident)), new ExpStatement(Loc(), loopbody), Loc()); + //printf("%s\n", s1->toChars()); + Statement s2 = new ReturnStatement(Loc(), new IdentifierExp(Loc(), p.ident)); + //printf("s2: %s\n", s2->toChars()); + Statement fbody = new CompoundStatement(Loc(), s1, s2); + // Built-in array ops should be @trusted, pure, nothrow and nogc + StorageClass stc = STCtrusted | STCpure | STCnothrow | STCnogc; + /* Construct the function + */ + auto ftype = new TypeFunction(fparams, exp.type, 0, LINKc, stc); + //printf("fd: %s %s\n", ident->toChars(), ftype->toChars()); + auto fd = new FuncDeclaration(Loc(), Loc(), ident, STCundefined, ftype); + fd.fbody = fbody; + fd.protection = Prot(PROTpublic); + fd.linkage = LINKc; + fd.isArrayOp = 1; + sc._module.importedFrom.members.push(fd); + sc = sc.push(); + sc.parent = sc._module.importedFrom; + sc.stc = 0; + sc.linkage = LINKc; + fd.semantic(sc); + fd.semantic2(sc); + uint errors = global.startGagging(); + fd.semantic3(sc); + if (global.endGagging(errors)) + { + fd.type = Type.terror; + fd.errors = true; + fd.fbody = null; + } + sc.pop(); + return fd; +} + +/********************************************** + * Check that there are no uses of arrays without []. + */ +extern (C++) bool isArrayOpValid(Expression e) +{ + if (e.op == TOKslice) + return true; + if (e.op == TOKarrayliteral) + { + Type t = e.type.toBasetype(); + while (t.ty == Tarray || t.ty == Tsarray) + t = t.nextOf().toBasetype(); + return (t.ty != Tvoid); + } + Type tb = e.type.toBasetype(); + if (tb.ty == Tarray || tb.ty == Tsarray) + { + if (isUnaArrayOp(e.op)) + { + return isArrayOpValid((cast(UnaExp)e).e1); + } + if (isBinArrayOp(e.op) || isBinAssignArrayOp(e.op) || e.op == TOKassign) + { + BinExp be = cast(BinExp)e; + return isArrayOpValid(be.e1) && isArrayOpValid(be.e2); + } + if (e.op == TOKconstruct) + { + BinExp be = cast(BinExp)e; + return be.e1.op == TOKslice && isArrayOpValid(be.e2); + } + if (e.op == TOKcall) + { + return false; // TODO: Decide if [] is required after arrayop calls. + } + else + { + return false; + } + } + return true; +} + +extern (C++) bool isNonAssignmentArrayOp(Expression e) +{ + if (e.op == TOKslice) + return isNonAssignmentArrayOp((cast(SliceExp)e).e1); + Type tb = e.type.toBasetype(); + if (tb.ty == Tarray || tb.ty == Tsarray) + { + return (isUnaArrayOp(e.op) || isBinArrayOp(e.op)); + } + return false; +} + +extern (C++) bool checkNonAssignmentArrayOp(Expression e, bool suggestion = false) +{ + if (isNonAssignmentArrayOp(e)) + { + const(char)* s = ""; + if (suggestion) + s = " (possible missing [])"; + e.error("array operation %s without destination memory not allowed%s", e.toChars(), s); + return true; + } + return false; +} + +/*********************************** + * Construct the array operation expression. + */ +extern (C++) Expression arrayOp(BinExp e, Scope* sc) +{ + //printf("BinExp::arrayOp() %s\n", toChars()); + Type tb = e.type.toBasetype(); + assert(tb.ty == Tarray || tb.ty == Tsarray); + Type tbn = tb.nextOf().toBasetype(); + if (tbn.ty == Tvoid) + { + e.error("cannot perform array operations on void[] arrays"); + return new ErrorExp(); + } + if (!isArrayOpValid(e)) + { + e.error("invalid array operation %s (possible missing [])", e.toChars()); + return new ErrorExp(); + } + auto arguments = new Expressions(); + /* The expression to generate an array operation for is mangled + * into a name to use as the array operation function name. + * Mangle in the operands and operators in RPN order, and type. + */ + OutBuffer buf; + buf.writestring("_array"); + buildArrayIdent(e, &buf, arguments); + buf.writeByte('_'); + /* Append deco of array element type + */ + buf.writestring(e.type.toBasetype().nextOf().toBasetype().mutableOf().deco); + char* name = buf.peekString(); + Identifier ident = Identifier.idPool(name); + FuncDeclaration* pFd = cast(FuncDeclaration*)dmd_aaGet(&arrayfuncs, cast(void*)ident); + FuncDeclaration fd = *pFd; + if (!fd) + fd = buildArrayOp(ident, e, sc, e.loc); + if (fd && fd.errors) + { + const(char)* fmt; + if (tbn.ty == Tstruct || tbn.ty == Tclass) + fmt = "invalid array operation '%s' because %s doesn't support necessary arithmetic operations"; + else if (!tbn.isscalar()) + fmt = "invalid array operation '%s' because %s is not a scalar type"; + else + fmt = "invalid array operation '%s' for element type %s"; + e.error(fmt, e.toChars(), tbn.toChars()); + return new ErrorExp(); + } + *pFd = fd; + Expression ev = new VarExp(e.loc, fd); + Expression ec = new CallExp(e.loc, ev, arguments); + return ec.semantic(sc); +} + +extern (C++) Expression arrayOp(BinAssignExp e, Scope* sc) +{ + //printf("BinAssignExp::arrayOp() %s\n", toChars()); + /* Check that the elements of e1 can be assigned to + */ + Type tn = e.e1.type.toBasetype().nextOf(); + if (tn && (!tn.isMutable() || !tn.isAssignable())) + { + e.error("slice %s is not mutable", e.e1.toChars()); + return new ErrorExp(); + } + if (e.e1.op == TOKarrayliteral) + { + return e.e1.modifiableLvalue(sc, e.e1); + } + return arrayOp(cast(BinExp)e, sc); +} + +/****************************************** + * Construct the identifier for the array operation function, + * and build the argument list to pass to it. + */ +extern (C++) void buildArrayIdent(Expression e, OutBuffer* buf, Expressions* arguments) +{ + extern (C++) final class BuildArrayIdentVisitor : Visitor + { + alias visit = super.visit; + OutBuffer* buf; + Expressions* arguments; + + public: + extern (D) this(OutBuffer* buf, Expressions* arguments) + { + this.buf = buf; + this.arguments = arguments; + } + + void visit(Expression e) + { + buf.writestring("Exp"); + arguments.shift(e); + } + + void visit(CastExp e) + { + Type tb = e.type.toBasetype(); + if (tb.ty == Tarray || tb.ty == Tsarray) + { + e.e1.accept(this); + } + else + visit(cast(Expression)e); + } + + void visit(ArrayLiteralExp e) + { + buf.writestring("Slice"); + arguments.shift(e); + } + + void visit(SliceExp e) + { + buf.writestring("Slice"); + arguments.shift(e); + } + + void visit(AssignExp e) + { + /* Evaluate assign expressions right to left + */ + e.e2.accept(this); + e.e1.accept(this); + buf.writestring("Assign"); + } + + void visit(BinAssignExp e) + { + /* Evaluate assign expressions right to left + */ + e.e2.accept(this); + e.e1.accept(this); + const(char)* s; + switch (e.op) + { + case TOKaddass: + s = "Addass"; + break; + case TOKminass: + s = "Subass"; + break; + case TOKmulass: + s = "Mulass"; + break; + case TOKdivass: + s = "Divass"; + break; + case TOKmodass: + s = "Modass"; + break; + case TOKxorass: + s = "Xorass"; + break; + case TOKandass: + s = "Andass"; + break; + case TOKorass: + s = "Orass"; + break; + case TOKpowass: + s = "Powass"; + break; + default: + assert(0); + } + buf.writestring(s); + } + + void visit(NegExp e) + { + e.e1.accept(this); + buf.writestring("Neg"); + } + + void visit(ComExp e) + { + e.e1.accept(this); + buf.writestring("Com"); + } + + void visit(BinExp e) + { + /* Evaluate assign expressions left to right + */ + const(char)* s = null; + switch (e.op) + { + case TOKadd: + s = "Add"; + break; + case TOKmin: + s = "Sub"; + break; + case TOKmul: + s = "Mul"; + break; + case TOKdiv: + s = "Div"; + break; + case TOKmod: + s = "Mod"; + break; + case TOKxor: + s = "Xor"; + break; + case TOKand: + s = "And"; + break; + case TOKor: + s = "Or"; + break; + case TOKpow: + s = "Pow"; + break; + default: + break; + } + if (s) + { + Type tb = e.type.toBasetype(); + Type t1 = e.e1.type.toBasetype(); + Type t2 = e.e2.type.toBasetype(); + e.e1.accept(this); + if (t1.ty == Tarray && (t2.ty == Tarray && !t1.equivalent(tb) || t2.ty != Tarray && !t1.nextOf().equivalent(e.e2.type))) + { + // Bugzilla 12780: if A is narrower than B + // A[] op B[] + // A[] op B + buf.writestring("Of"); + buf.writestring(t1.nextOf().mutableOf().deco); + } + e.e2.accept(this); + if (t2.ty == Tarray && (t1.ty == Tarray && !t2.equivalent(tb) || t1.ty != Tarray && !t2.nextOf().equivalent(e.e1.type))) + { + // Bugzilla 12780: if B is narrower than A: + // A[] op B[] + // A op B[] + buf.writestring("Of"); + buf.writestring(t2.nextOf().mutableOf().deco); + } + buf.writestring(s); + } + else + visit(cast(Expression)e); + } + } + + scope BuildArrayIdentVisitor v = new BuildArrayIdentVisitor(buf, arguments); + e.accept(v); +} + +/****************************************** + * Construct the inner loop for the array operation function, + * and build the parameter list. + */ +extern (C++) Expression buildArrayLoop(Expression e, Parameters* fparams) +{ + extern (C++) final class BuildArrayLoopVisitor : Visitor + { + alias visit = super.visit; + Parameters* fparams; + Expression result; + + public: + extern (D) this(Parameters* fparams) + { + this.fparams = fparams; + this.result = null; + } + + void visit(Expression e) + { + Identifier id = Identifier.generateId("c", fparams.dim); + auto param = new Parameter(0, e.type, id, null); + fparams.shift(param); + result = new IdentifierExp(Loc(), id); + } + + void visit(CastExp e) + { + Type tb = e.type.toBasetype(); + if (tb.ty == Tarray || tb.ty == Tsarray) + { + e.e1.accept(this); + } + else + visit(cast(Expression)e); + } + + void visit(ArrayLiteralExp e) + { + Identifier id = Identifier.generateId("p", fparams.dim); + auto param = new Parameter(STCconst, e.type, id, null); + fparams.shift(param); + Expression ie = new IdentifierExp(Loc(), id); + auto arguments = new Expressions(); + Expression index = new IdentifierExp(Loc(), Id.p); + arguments.push(index); + result = new ArrayExp(Loc(), ie, arguments); + } + + void visit(SliceExp e) + { + Identifier id = Identifier.generateId("p", fparams.dim); + auto param = new Parameter(STCconst, e.type, id, null); + fparams.shift(param); + Expression ie = new IdentifierExp(Loc(), id); + auto arguments = new Expressions(); + Expression index = new IdentifierExp(Loc(), Id.p); + arguments.push(index); + result = new ArrayExp(Loc(), ie, arguments); + } + + void visit(AssignExp e) + { + /* Evaluate assign expressions right to left + */ + Expression ex2 = buildArrayLoop(e.e2); + /* Need the cast because: + * b = c + p[i]; + * where b is a byte fails because (c + p[i]) is an int + * which cannot be implicitly cast to byte. + */ + ex2 = new CastExp(Loc(), ex2, e.e1.type.nextOf()); + Expression ex1 = buildArrayLoop(e.e1); + Parameter param = (*fparams)[0]; + param.storageClass = 0; + result = new AssignExp(Loc(), ex1, ex2); + } + + void visit(BinAssignExp e) + { + /* Evaluate assign expressions right to left + */ + Expression ex2 = buildArrayLoop(e.e2); + Expression ex1 = buildArrayLoop(e.e1); + Parameter param = (*fparams)[0]; + param.storageClass = 0; + switch (e.op) + { + case TOKaddass: + result = new AddAssignExp(e.loc, ex1, ex2); + return; + case TOKminass: + result = new MinAssignExp(e.loc, ex1, ex2); + return; + case TOKmulass: + result = new MulAssignExp(e.loc, ex1, ex2); + return; + case TOKdivass: + result = new DivAssignExp(e.loc, ex1, ex2); + return; + case TOKmodass: + result = new ModAssignExp(e.loc, ex1, ex2); + return; + case TOKxorass: + result = new XorAssignExp(e.loc, ex1, ex2); + return; + case TOKandass: + result = new AndAssignExp(e.loc, ex1, ex2); + return; + case TOKorass: + result = new OrAssignExp(e.loc, ex1, ex2); + return; + case TOKpowass: + result = new PowAssignExp(e.loc, ex1, ex2); + return; + default: + assert(0); + } + } + + void visit(NegExp e) + { + Expression ex1 = buildArrayLoop(e.e1); + result = new NegExp(Loc(), ex1); + } + + void visit(ComExp e) + { + Expression ex1 = buildArrayLoop(e.e1); + result = new ComExp(Loc(), ex1); + } + + void visit(BinExp e) + { + if (isBinArrayOp(e.op)) + { + /* Evaluate assign expressions left to right + */ + BinExp be = cast(BinExp)e.copy(); + be.e1 = buildArrayLoop(be.e1); + be.e2 = buildArrayLoop(be.e2); + be.type = null; + result = be; + return; + } + else + { + visit(cast(Expression)e); + return; + } + } + + Expression buildArrayLoop(Expression e) + { + e.accept(this); + return result; + } + } + + scope BuildArrayLoopVisitor v = new BuildArrayLoopVisitor(fparams); + return v.buildArrayLoop(e); +} + +/*********************************************** + * Test if expression is a unary array op. + */ +extern (C++) bool isUnaArrayOp(TOK op) +{ + switch (op) + { + case TOKneg: + case TOKtilde: + return true; + default: + break; + } + return false; +} + +/*********************************************** + * Test if expression is a binary array op. + */ +extern (C++) bool isBinArrayOp(TOK op) +{ + switch (op) + { + case TOKadd: + case TOKmin: + case TOKmul: + case TOKdiv: + case TOKmod: + case TOKxor: + case TOKand: + case TOKor: + case TOKpow: + return true; + default: + break; + } + return false; +} + +/*********************************************** + * Test if expression is a binary assignment array op. + */ +extern (C++) bool isBinAssignArrayOp(TOK op) +{ + switch (op) + { + case TOKaddass: + case TOKminass: + case TOKmulass: + case TOKdivass: + case TOKmodass: + case TOKxorass: + case TOKandass: + case TOKorass: + case TOKpowass: + return true; + default: + break; + } + return false; +} + +/*********************************************** + * Test if operand is a valid array op operand. + */ +extern (C++) bool isArrayOpOperand(Expression e) +{ + //printf("Expression::isArrayOpOperand() %s\n", e->toChars()); + if (e.op == TOKslice) + return true; + if (e.op == TOKarrayliteral) + { + Type t = e.type.toBasetype(); + while (t.ty == Tarray || t.ty == Tsarray) + t = t.nextOf().toBasetype(); + return (t.ty != Tvoid); + } + Type tb = e.type.toBasetype(); + if (tb.ty == Tarray) + { + return (isUnaArrayOp(e.op) || isBinArrayOp(e.op) || isBinAssignArrayOp(e.op) || e.op == TOKassign); + } + return false; +} diff --git a/src/arraytypes.d b/src/arraytypes.d new file mode 100644 index 000000000000..6c6296890eed --- /dev/null +++ b/src/arraytypes.d @@ -0,0 +1,37 @@ +// 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.arraytypes; + +import ddmd.aggregate, ddmd.backend, ddmd.dclass, ddmd.declaration, ddmd.dmodule, ddmd.dsymbol, ddmd.dtemplate, ddmd.expression, ddmd.func, ddmd.globals, ddmd.identifier, ddmd.init, ddmd.mtype, ddmd.root.array, ddmd.root.file, ddmd.root.rootobject, ddmd.statement; + +alias Strings = Array!(const(char)*); +alias Identifiers = Array!(Identifier); +alias TemplateParameters = Array!(TemplateParameter); +alias Expressions = Array!(Expression); +alias Statements = Array!(Statement); +alias BaseClasses = Array!(BaseClass*); +alias ClassDeclarations = Array!(ClassDeclaration); +alias Dsymbols = Array!(Dsymbol); +alias Objects = Array!(RootObject); +alias FuncDeclarations = Array!(FuncDeclaration); +alias Parameters = Array!(Parameter); +alias Initializers = Array!(Initializer); +alias VarDeclarations = Array!(VarDeclaration); +alias Types = Array!(Type); +alias Catches = Array!(Catch); +alias StaticDtorDeclarations = Array!(StaticDtorDeclaration); +alias SharedStaticDtorDeclarations = Array!(SharedStaticDtorDeclaration); +alias AliasDeclarations = Array!(AliasDeclaration); +alias Modules = Array!(Module); +alias CaseStatements = Array!(CaseStatement); +alias ScopeStatements = Array!(ScopeStatement); +alias GotoCaseStatements = Array!(GotoCaseStatement); +alias ReturnStatements = Array!(ReturnStatement); +alias GotoStatements = Array!(GotoStatement); +alias TemplateInstances = Array!(TemplateInstance); diff --git a/src/attrib.c b/src/attrib.c deleted file mode 100644 index 309e8f67db24..000000000000 --- a/src/attrib.c +++ /dev/null @@ -1,1408 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/attrib.c - */ - -#include -#include -#include -#include // memcpy() - -#include "rmem.h" - -#include "init.h" -#include "declaration.h" -#include "attrib.h" -#include "cond.h" -#include "scope.h" -#include "id.h" -#include "expression.h" -#include "dsymbol.h" -#include "aggregate.h" -#include "module.h" -#include "parse.h" -#include "template.h" -#include "utf.h" -#include "mtype.h" - - -/********************************* AttribDeclaration ****************************/ - -AttribDeclaration::AttribDeclaration(Dsymbols *decl) - : Dsymbol() -{ - this->decl = decl; -} - -Dsymbols *AttribDeclaration::include(Scope *sc, ScopeDsymbol *sds) -{ - return decl; -} - -int AttribDeclaration::apply(Dsymbol_apply_ft_t fp, void *param) -{ - Dsymbols *d = include(scope, NULL); - - if (d) - { - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - if (s) - { - if (s->apply(fp, param)) - return 1; - } - } - } - return 0; -} - -/**************************************** - * Create a new scope if one or more given attributes - * are different from the sc's. - * If the returned scope != sc, the caller should pop - * the scope after it used. - */ -Scope *AttribDeclaration::createNewScope(Scope *sc, - StorageClass stc, LINK linkage, Prot protection, int explicitProtection, - structalign_t structalign, PINLINE inlining) -{ - Scope *sc2 = sc; - if (stc != sc->stc || - linkage != sc->linkage || - !protection.isSubsetOf(sc->protection) || - explicitProtection != sc->explicitProtection || - structalign != sc->structalign || - inlining != sc->inlining) - { - // create new one for changes - sc2 = sc->copy(); - sc2->stc = stc; - sc2->linkage = linkage; - sc2->protection = protection; - sc2->explicitProtection = explicitProtection; - sc2->structalign = structalign; - sc2->inlining = inlining; - } - return sc2; -} - -/**************************************** - * A hook point to supply scope for members. - * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this. - */ -Scope *AttribDeclaration::newScope(Scope *sc) -{ - return sc; -} - -void AttribDeclaration::addMember(Scope *sc, ScopeDsymbol *sds) -{ - Dsymbols *d = include(sc, sds); - - if (d) - { - Scope *sc2 = newScope(sc); - - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - //printf("\taddMember %s to %s\n", s->toChars(), sds->toChars()); - s->addMember(sc2, sds); - } - - if (sc2 != sc) - sc2->pop(); - } -} - -void AttribDeclaration::setScope(Scope *sc) -{ - Dsymbols *d = include(sc, NULL); - - //printf("\tAttribDeclaration::setScope '%s', d = %p\n",toChars(), d); - if (d) - { - Scope *sc2 = newScope(sc); - - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->setScope(sc2); - } - - if (sc2 != sc) - sc2->pop(); - } -} - -void AttribDeclaration::importAll(Scope *sc) -{ - Dsymbols *d = include(sc, NULL); - - //printf("\tAttribDeclaration::importAll '%s', d = %p\n", toChars(), d); - if (d) - { - Scope *sc2 = newScope(sc); - - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->importAll(sc2); - } - - if (sc2 != sc) - sc2->pop(); - } -} - -void AttribDeclaration::semantic(Scope *sc) -{ - Dsymbols *d = include(sc, NULL); - - //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d); - if (d) - { - Scope *sc2 = newScope(sc); - - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->semantic(sc2); - } - - if (sc2 != sc) - sc2->pop(); - } -} - -void AttribDeclaration::semantic2(Scope *sc) -{ - Dsymbols *d = include(sc, NULL); - - if (d) - { - Scope *sc2 = newScope(sc); - - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->semantic2(sc2); - } - - if (sc2 != sc) - sc2->pop(); - } -} - -void AttribDeclaration::semantic3(Scope *sc) -{ - Dsymbols *d = include(sc, NULL); - - if (d) - { - Scope *sc2 = newScope(sc); - - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->semantic3(sc2); - } - - if (sc2 != sc) - sc2->pop(); - } -} - -void AttribDeclaration::addComment(const utf8_t *comment) -{ - //printf("AttribDeclaration::addComment %s\n", comment); - if (comment) - { - Dsymbols *d = include(NULL, NULL); - - if (d) - { - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - //printf("AttribDeclaration::addComment %s\n", s->toChars()); - s->addComment(comment); - } - } - } -} - -void AttribDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion) -{ - Dsymbols *d = include(NULL, NULL); - - if (d) - { - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->setFieldOffset(ad, poffset, isunion); - } - } -} - -bool AttribDeclaration::hasPointers() -{ - Dsymbols *d = include(NULL, NULL); - - if (d) - { - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - if (s->hasPointers()) - return true; - } - } - return false; -} - -bool AttribDeclaration::hasStaticCtorOrDtor() -{ - Dsymbols *d = include(NULL, NULL); - - if (d) - { - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - if (s->hasStaticCtorOrDtor()) - return true; - } - } - return false; -} - -const char *AttribDeclaration::kind() -{ - return "attribute"; -} - -bool AttribDeclaration::oneMember(Dsymbol **ps, Identifier *ident) -{ - Dsymbols *d = include(NULL, NULL); - - return Dsymbol::oneMembers(d, ps, ident); -} - -void AttribDeclaration::checkCtorConstInit() -{ - Dsymbols *d = include(NULL, NULL); - - if (d) - { - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->checkCtorConstInit(); - } - } -} - -/**************************************** - */ - -void AttribDeclaration::addLocalClass(ClassDeclarations *aclasses) -{ - Dsymbols *d = include(NULL, NULL); - - if (d) - { - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->addLocalClass(aclasses); - } - } -} - -/************************* StorageClassDeclaration ****************************/ - -StorageClassDeclaration::StorageClassDeclaration(StorageClass stc, Dsymbols *decl) - : AttribDeclaration(decl) -{ - this->stc = stc; -} - -Dsymbol *StorageClassDeclaration::syntaxCopy(Dsymbol *s) -{ - assert(!s); - return new StorageClassDeclaration(stc, Dsymbol::arraySyntaxCopy(decl)); -} - -bool StorageClassDeclaration::oneMember(Dsymbol **ps, Identifier *ident) -{ - bool t = Dsymbol::oneMembers(decl, ps, ident); - if (t && *ps) - { - /* This is to deal with the following case: - * struct Tick { - * template to(T) { const T to() { ... } } - * } - * For eponymous function templates, the 'const' needs to get attached to 'to' - * before the semantic analysis of 'to', so that template overloading based on the - * 'this' pointer can be successful. - */ - - FuncDeclaration *fd = (*ps)->isFuncDeclaration(); - if (fd) - { - /* Use storage_class2 instead of storage_class otherwise when we do .di generation - * we'll wind up with 'const const' rather than 'const'. - */ - /* Don't think we need to worry about mutually exclusive storage classes here - */ - fd->storage_class2 |= stc; - } - } - return t; -} - -Scope *StorageClassDeclaration::newScope(Scope *sc) -{ - StorageClass scstc = sc->stc; - - /* These sets of storage classes are mutually exclusive, - * so choose the innermost or most recent one. - */ - if (stc & (STCauto | STCscope | STCstatic | STCextern | STCmanifest)) - scstc &= ~(STCauto | STCscope | STCstatic | STCextern | STCmanifest); - if (stc & (STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared)) - scstc &= ~(STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared); - if (stc & (STCconst | STCimmutable | STCmanifest)) - scstc &= ~(STCconst | STCimmutable | STCmanifest); - if (stc & (STCgshared | STCshared | STCtls)) - scstc &= ~(STCgshared | STCshared | STCtls); - if (stc & (STCsafe | STCtrusted | STCsystem)) - scstc &= ~(STCsafe | STCtrusted | STCsystem); - scstc |= stc; - //printf("scstc = x%llx\n", scstc); - - return createNewScope(sc, scstc, sc->linkage, sc->protection, sc->explicitProtection, sc->structalign, sc->inlining); -} - -/********************************* DeprecatedDeclaration ****************************/ - -DeprecatedDeclaration::DeprecatedDeclaration(Expression *msg, Dsymbols *decl) - : StorageClassDeclaration(STCdeprecated, decl) -{ - this->msg = msg; -} - -Dsymbol *DeprecatedDeclaration::syntaxCopy(Dsymbol *s) -{ - assert(!s); - return new DeprecatedDeclaration(msg->syntaxCopy(), Dsymbol::arraySyntaxCopy(decl)); -} - -void DeprecatedDeclaration::setScope(Scope *sc) -{ - assert(msg); - char *depmsg = NULL; - StringExp *se = msg->toStringExp(); - if (se) - depmsg = (char *)se->string; - else - msg->error("string expected, not '%s'", msg->toChars()); - - Scope *scx = sc->push(); - scx->depmsg = depmsg; - StorageClassDeclaration::setScope(scx); - scx->pop(); -} - -/********************************* LinkDeclaration ****************************/ - -LinkDeclaration::LinkDeclaration(LINK p, Dsymbols *decl) - : AttribDeclaration(decl) -{ - //printf("LinkDeclaration(linkage = %d, decl = %p)\n", p, decl); - linkage = p; -} - -Dsymbol *LinkDeclaration::syntaxCopy(Dsymbol *s) -{ - assert(!s); - return new LinkDeclaration(linkage, Dsymbol::arraySyntaxCopy(decl)); -} - -Scope *LinkDeclaration::newScope(Scope *sc) -{ - return createNewScope(sc, sc->stc, this->linkage, sc->protection, sc->explicitProtection, sc->structalign, sc->inlining); -} - -char *LinkDeclaration::toChars() -{ - return (char *)"extern ()"; -} - -/********************************* ProtDeclaration ****************************/ - -/** - * Params: - * loc = source location of attribute token - * p = protection attribute data - * decl = declarations which are affected by this protection attribute - */ -ProtDeclaration::ProtDeclaration(Loc loc, Prot p, Dsymbols *decl) - : AttribDeclaration(decl) -{ - this->loc = loc; - this->protection = p; - this->pkg_identifiers = NULL; - //printf("decl = %p\n", decl); -} - -/** - * Params: - * loc = source location of attribute token - * pkg_identifiers = list of identifiers for a qualified package name - * decl = declarations which are affected by this protection attribute - */ -ProtDeclaration::ProtDeclaration(Loc loc, Identifiers* pkg_identifiers, Dsymbols *decl) - : AttribDeclaration(decl) -{ - this->loc = loc; - this->protection.kind = PROTpackage; - this->protection.pkg = NULL; - this->pkg_identifiers = pkg_identifiers; -} - -Dsymbol *ProtDeclaration::syntaxCopy(Dsymbol *s) -{ - assert(!s); - if (protection.kind == PROTpackage) - return new ProtDeclaration(this->loc, pkg_identifiers, Dsymbol::arraySyntaxCopy(decl)); - else - return new ProtDeclaration(this->loc, protection, Dsymbol::arraySyntaxCopy(decl)); -} - -Scope *ProtDeclaration::newScope(Scope *sc) -{ - if (pkg_identifiers) - semantic(sc); - return createNewScope(sc, sc->stc, sc->linkage, this->protection, 1, sc->structalign, sc->inlining); -} - -void ProtDeclaration::addMember(Scope *sc, ScopeDsymbol *sds) -{ - if (pkg_identifiers) - { - Dsymbol* tmp; - Package::resolve(pkg_identifiers, &tmp, NULL); - protection.pkg = tmp ? tmp->isPackage() : NULL; - pkg_identifiers = NULL; - } - - if (protection.kind == PROTpackage && protection.pkg && sc->module) - { - Module *m = sc->module; - Package* pkg = m->parent ? m->parent->isPackage() : NULL; - if (!pkg || !protection.pkg->isAncestorPackageOf(pkg)) - error("does not bind to one of ancestor packages of module '%s'", - m->toPrettyChars(true)); - } - - return AttribDeclaration::addMember(sc, sds); -} - -const char *ProtDeclaration::kind() -{ - return "protection attribute"; -} - -const char *ProtDeclaration::toPrettyChars(bool) -{ - assert(protection.kind > PROTundefined); - - OutBuffer buf; - buf.writeByte('\''); - protectionToBuffer(&buf, protection); - buf.writeByte('\''); - return buf.extractString(); -} - -/********************************* AlignDeclaration ****************************/ - -AlignDeclaration::AlignDeclaration(unsigned sa, Dsymbols *decl) - : AttribDeclaration(decl) -{ - salign = sa; -} - -Dsymbol *AlignDeclaration::syntaxCopy(Dsymbol *s) -{ - assert(!s); - return new AlignDeclaration(salign, Dsymbol::arraySyntaxCopy(decl)); -} - -Scope *AlignDeclaration::newScope(Scope *sc) -{ - return createNewScope(sc, sc->stc, sc->linkage, sc->protection, sc->explicitProtection, this->salign, sc->inlining); -} - -/********************************* AnonDeclaration ****************************/ - -AnonDeclaration::AnonDeclaration(Loc loc, bool isunion, Dsymbols *decl) - : AttribDeclaration(decl) -{ - this->loc = loc; - this->alignment = 0; - this->isunion = isunion; - this->sem = 0; -} - -Dsymbol *AnonDeclaration::syntaxCopy(Dsymbol *s) -{ - assert(!s); - return new AnonDeclaration(loc, isunion, Dsymbol::arraySyntaxCopy(decl)); -} - -void AnonDeclaration::semantic(Scope *sc) -{ - //printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", this); - - assert(sc->parent); - - Dsymbol *p = sc->parent->pastMixin(); - AggregateDeclaration *ad = p->isAggregateDeclaration(); - if (!ad) - { - ::error(loc, "%s can only be a part of an aggregate, not %s %s", - kind(), p->kind(), p->toChars()); - return; - } - - alignment = sc->structalign; - if (decl) - { - sc = sc->push(); - sc->stc &= ~(STCauto | STCscope | STCstatic | STCtls | STCgshared); - sc->inunion = isunion; - sc->flags = 0; - - for (size_t i = 0; i < decl->dim; i++) - { - Dsymbol *s = (*decl)[i]; - s->semantic(sc); - } - sc = sc->pop(); - } -} - -void AnonDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion) -{ - //printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", this); - - if (decl) - { - /* This works by treating an AnonDeclaration as an aggregate 'member', - * so in order to place that member we need to compute the member's - * size and alignment. - */ - - size_t fieldstart = ad->fields.dim; - - /* Hackishly hijack ad's structsize and alignsize fields - * for use in our fake anon aggregate member. - */ - unsigned savestructsize = ad->structsize; - unsigned savealignsize = ad->alignsize; - ad->structsize = 0; - ad->alignsize = 0; - - unsigned offset = 0; - for (size_t i = 0; i < decl->dim; i++) - { - Dsymbol *s = (*decl)[i]; - s->setFieldOffset(ad, &offset, this->isunion); - if (this->isunion) - offset = 0; - } - - unsigned anonstructsize = ad->structsize; - unsigned anonalignsize = ad->alignsize; - ad->structsize = savestructsize; - ad->alignsize = savealignsize; - - if (fieldstart == ad->fields.dim) - { - /* Bugzilla 13613: If the fields in this->members had been already - * added in ad->fields, just update *poffset for the subsequent - * field offset calculation. - */ - *poffset = ad->structsize; - return; - } - - // 0 sized structs are set to 1 byte - // TODO: is this corect hebavior? - if (anonstructsize == 0) - { - anonstructsize = 1; - anonalignsize = 1; - } - - /* Given the anon 'member's size and alignment, - * go ahead and place it. - */ - unsigned anonoffset = AggregateDeclaration::placeField( - poffset, - anonstructsize, anonalignsize, alignment, - &ad->structsize, &ad->alignsize, - isunion); - - // Add to the anon fields the base offset of this anonymous aggregate - //printf("anon fields, anonoffset = %d\n", anonoffset); - for (size_t i = fieldstart; i < ad->fields.dim; i++) - { - VarDeclaration *v = ad->fields[i]; - //printf("\t[%d] %s %d\n", i, v->toChars(), v->offset); - v->offset += anonoffset; - } - } -} - -const char *AnonDeclaration::kind() -{ - return (isunion ? "anonymous union" : "anonymous struct"); -} - -/********************************* PragmaDeclaration ****************************/ - -PragmaDeclaration::PragmaDeclaration(Loc loc, Identifier *ident, Expressions *args, Dsymbols *decl) - : AttribDeclaration(decl) -{ - this->loc = loc; - this->ident = ident; - this->args = args; -} - -Dsymbol *PragmaDeclaration::syntaxCopy(Dsymbol *s) -{ - //printf("PragmaDeclaration::syntaxCopy(%s)\n", toChars()); - assert(!s); - return new PragmaDeclaration(loc, ident, - Expression::arraySyntaxCopy(args), - Dsymbol::arraySyntaxCopy(decl)); -} - -Scope *PragmaDeclaration::newScope(Scope *sc) -{ - if (ident == Id::Pinline) - { - PINLINE inlining = PINLINEdefault; - if (!args || args->dim == 0) - inlining = PINLINEdefault; - else if (args->dim != 1) - { - error("one boolean expression expected for pragma(inline), not %d", args->dim); - args->setDim(1); - (*args)[0] = new ErrorExp(); - } - else - { - Expression *e = (*args)[0]; - - if (e->op != TOKint64 || !e->type->equals(Type::tbool)) - { - if (e->op != TOKerror) - { - error("pragma(inline, true or false) expected, not %s", e->toChars()); - (*args)[0] = new ErrorExp(); - } - } - else if (e->isBool(true)) - inlining = PINLINEalways; - else if (e->isBool(false)) - inlining = PINLINEnever; - } - - return createNewScope(sc, sc->stc, sc->linkage, sc->protection, sc->explicitProtection, sc->structalign, inlining); - } - return sc; -} - -static unsigned setMangleOverride(Dsymbol *s, char *sym) -{ - AttribDeclaration *ad = s->isAttribDeclaration(); - - if (ad) - { - Dsymbols *decls = ad->include(NULL, NULL); - unsigned nestedCount = 0; - - if (decls && decls->dim) - for (size_t i = 0; i < decls->dim; ++i) - nestedCount += setMangleOverride((*decls)[i], sym); - - return nestedCount; - } - else if (s->isFuncDeclaration() || s->isVarDeclaration()) - { - s->isDeclaration()->mangleOverride = sym; - return 1; - } - else - return 0; -} - -void PragmaDeclaration::semantic(Scope *sc) -{ - // Should be merged with PragmaStatement - - //printf("\tPragmaDeclaration::semantic '%s'\n",toChars()); - if (ident == Id::msg) - { - if (args) - { - for (size_t i = 0; i < args->dim; i++) - { - Expression *e = (*args)[i]; - - sc = sc->startCTFE(); - e = e->semantic(sc); - e = resolveProperties(sc, e); - sc = sc->endCTFE(); - - // pragma(msg) is allowed to contain types as well as expressions - e = ctfeInterpretForPragmaMsg(e); - if (e->op == TOKerror) - { - errorSupplemental(loc, "while evaluating pragma(msg, %s)", (*args)[i]->toChars()); - return; - } - StringExp *se = e->toStringExp(); - if (se) - { - se = se->toUTF8(sc); - fprintf(stderr, "%.*s", (int)se->len, (char *)se->string); - } - else - fprintf(stderr, "%s", e->toChars()); - } - fprintf(stderr, "\n"); - } - goto Lnodecl; - } - else if (ident == Id::lib) - { - if (!args || args->dim != 1) - error("string expected for library name"); - else - { - Expression *e = (*args)[0]; - - sc = sc->startCTFE(); - e = e->semantic(sc); - e = resolveProperties(sc, e); - sc = sc->endCTFE(); - - e = e->ctfeInterpret(); - (*args)[0] = e; - if (e->op == TOKerror) - goto Lnodecl; - StringExp *se = e->toStringExp(); - if (!se) - error("string expected for library name, not '%s'", e->toChars()); - else - { - char *name = (char *)mem.xmalloc(se->len + 1); - memcpy(name, se->string, se->len); - name[se->len] = 0; - if (global.params.verbose) - fprintf(global.stdmsg, "library %s\n", name); - if (global.params.moduleDeps && !global.params.moduleDepsFile) - { - OutBuffer *ob = global.params.moduleDeps; - Module *imod = sc->instantiatingModule(); - ob->writestring("depsLib "); - ob->writestring(imod->toPrettyChars()); - ob->writestring(" ("); - escapePath(ob, imod->srcfile->toChars()); - ob->writestring(") : "); - ob->writestring((char *) name); - ob->writenl(); - } - mem.xfree(name); - } - } - goto Lnodecl; - } - else if (ident == Id::startaddress) - { - if (!args || args->dim != 1) - error("function name expected for start address"); - else - { - /* Bugzilla 11980: - * resolveProperties and ctfeInterpret call are not necessary. - */ - Expression *e = (*args)[0]; - - sc = sc->startCTFE(); - e = e->semantic(sc); - sc = sc->endCTFE(); - - (*args)[0] = e; - Dsymbol *sa = getDsymbol(e); - if (!sa || !sa->isFuncDeclaration()) - error("function name expected for start address, not '%s'", e->toChars()); - } - goto Lnodecl; - } - else if (ident == Id::Pinline) - { - goto Ldecl; - } - else if (ident == Id::mangle) - { - if (!args) - args = new Expressions(); - if (args->dim != 1) - { - error("string expected for mangled name"); - args->setDim(1); - (*args)[0] = new ErrorExp(); // error recovery - goto Ldecl; - } - - Expression *e = (*args)[0]; - e = e->semantic(sc); - e = e->ctfeInterpret(); - (*args)[0] = e; - if (e->op == TOKerror) - goto Ldecl; - - StringExp *se = e->toStringExp(); - if (!se) - { - error("string expected for mangled name, not '%s'", e->toChars()); - goto Ldecl; - } - if (!se->len) - { - error("zero-length string not allowed for mangled name"); - goto Ldecl; - } - if (se->sz != 1) - { - error("mangled name characters can only be of type char"); - goto Ldecl; - } - -#if 1 - /* Note: D language specification should not have any assumption about backend - * implementation. Ideally pragma(mangle) can accept a string of any content. - * - * Therefore, this validation is compiler implementation specific. - */ - for (size_t i = 0; i < se->len; ) - { - utf8_t *p = (utf8_t *)se->string; - dchar_t c = p[i]; - if (c < 0x80) - { - if (c >= 'A' && c <= 'Z' || - c >= 'a' && c <= 'z' || - c >= '0' && c <= '9' || - c != 0 && strchr("$%().:?@[]_", c)) - { - ++i; - continue; - } - else - { - error("char 0x%02x not allowed in mangled name", c); - break; - } - } - - if (const char* msg = utf_decodeChar((utf8_t *)se->string, se->len, &i, &c)) - { - error("%s", msg); - break; - } - - if (!isUniAlpha(c)) - { - error("char 0x%04x not allowed in mangled name", c); - break; - } - } -#endif - } - else if (global.params.ignoreUnsupportedPragmas) - { - if (global.params.verbose) - { - /* Print unrecognized pragmas - */ - fprintf(global.stdmsg, "pragma %s", ident->toChars()); - if (args) - { - for (size_t i = 0; i < args->dim; i++) - { - Expression *e = (*args)[i]; - - sc = sc->startCTFE(); - e = e->semantic(sc); - e = resolveProperties(sc, e); - sc = sc->endCTFE(); - - e = e->ctfeInterpret(); - if (i == 0) - fprintf(global.stdmsg, " ("); - else - fprintf(global.stdmsg, ","); - fprintf(global.stdmsg, "%s", e->toChars()); - } - if (args->dim) - fprintf(global.stdmsg, ")"); - } - fprintf(global.stdmsg, "\n"); - } - goto Lnodecl; - } - else - error("unrecognized pragma(%s)", ident->toChars()); - -Ldecl: - if (decl) - { - Scope *sc2 = newScope(sc); - - for (size_t i = 0; i < decl->dim; i++) - { - Dsymbol *s = (*decl)[i]; - - s->semantic(sc2); - - if (ident == Id::mangle) - { - assert(args && args->dim == 1); - if (StringExp *se = (*args)[0]->toStringExp()) - { - char *name = (char *)mem.xmalloc(se->len + 1); - memcpy(name, se->string, se->len); - name[se->len] = 0; - - unsigned cnt = setMangleOverride(s, name); - if (cnt > 1) - error("can only apply to a single declaration"); - } - } - } - - if (sc2 != sc) - sc2->pop(); - } - return; - -Lnodecl: - if (decl) - { - error("pragma is missing closing ';'"); - goto Ldecl; // do them anyway, to avoid segfaults. - } -} - -const char *PragmaDeclaration::kind() -{ - return "pragma"; -} - -/********************************* ConditionalDeclaration ****************************/ - -ConditionalDeclaration::ConditionalDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl) - : AttribDeclaration(decl) -{ - //printf("ConditionalDeclaration::ConditionalDeclaration()\n"); - this->condition = condition; - this->elsedecl = elsedecl; -} - -Dsymbol *ConditionalDeclaration::syntaxCopy(Dsymbol *s) -{ - assert(!s); - return new ConditionalDeclaration(condition->syntaxCopy(), - Dsymbol::arraySyntaxCopy(decl), - Dsymbol::arraySyntaxCopy(elsedecl)); -} - -bool ConditionalDeclaration::oneMember(Dsymbol **ps, Identifier *ident) -{ - //printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition->inc); - if (condition->inc) - { - Dsymbols *d = condition->include(NULL, NULL) ? decl : elsedecl; - return Dsymbol::oneMembers(d, ps, ident); - } - else - { - bool res = (Dsymbol::oneMembers( decl, ps, ident) && *ps == NULL && - Dsymbol::oneMembers(elsedecl, ps, ident) && *ps == NULL); - *ps = NULL; - return res; - } -} - -// Decide if 'then' or 'else' code should be included - -Dsymbols *ConditionalDeclaration::include(Scope *sc, ScopeDsymbol *sds) -{ - //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, scope); - assert(condition); - return condition->include(scope ? scope : sc, sds) ? decl : elsedecl; -} - -void ConditionalDeclaration::setScope(Scope *sc) -{ - Dsymbols *d = include(sc, NULL); - - //printf("\tConditionalDeclaration::setScope '%s', d = %p\n",toChars(), d); - if (d) - { - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->setScope(sc); - } - } -} - -void ConditionalDeclaration::addComment(const utf8_t *comment) -{ - /* Because addComment is called by the parser, if we called - * include() it would define a version before it was used. - * But it's no problem to drill down to both decl and elsedecl, - * so that's the workaround. - */ - - if (comment) - { - Dsymbols *d = decl; - - for (int j = 0; j < 2; j++) - { - if (d) - { - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - //printf("ConditionalDeclaration::addComment %s\n", s->toChars()); - s->addComment(comment); - } - } - d = elsedecl; - } - } -} - -/***************************** StaticIfDeclaration ****************************/ - -StaticIfDeclaration::StaticIfDeclaration(Condition *condition, - Dsymbols *decl, Dsymbols *elsedecl) - : ConditionalDeclaration(condition, decl, elsedecl) -{ - //printf("StaticIfDeclaration::StaticIfDeclaration()\n"); - scopesym = NULL; - addisdone = 0; -} - -Dsymbol *StaticIfDeclaration::syntaxCopy(Dsymbol *s) -{ - assert(!s); - return new StaticIfDeclaration(condition->syntaxCopy(), - Dsymbol::arraySyntaxCopy(decl), - Dsymbol::arraySyntaxCopy(elsedecl)); -} - -/**************************************** - * Different from other AttribDeclaration subclasses, include() call requires - * the completion of addMember and setScope phases. - */ -Dsymbols *StaticIfDeclaration::include(Scope *sc, ScopeDsymbol *sds) -{ - //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, scope); - - if (condition->inc == 0) - { - assert(scopesym); // addMember is already done - assert(scope); // setScope is already done - - Dsymbols *d = ConditionalDeclaration::include(scope, scopesym); - - if (d && !addisdone) - { - // Add members lazily. - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->addMember(scope, scopesym); - } - - // Set the member scopes lazily. - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->setScope(scope); - } - - addisdone = 1; - } - return d; - } - else - { - return ConditionalDeclaration::include(sc, scopesym); - } -} - -void StaticIfDeclaration::addMember(Scope *sc, ScopeDsymbol *sds) -{ - //printf("StaticIfDeclaration::addMember() '%s'\n", toChars()); - /* This is deferred until the condition evaluated later (by the include() call), - * so that expressions in the condition can refer to declarations - * in the same scope, such as: - * - * template Foo(int i) - * { - * const int j = i + 1; - * static if (j == 3) - * const int k; - * } - */ - this->scopesym = sds; -} - -void StaticIfDeclaration::importAll(Scope *sc) -{ - // do not evaluate condition before semantic pass -} - -void StaticIfDeclaration::setScope(Scope *sc) -{ - // do not evaluate condition before semantic pass - - // But do set the scope, in case we need it for forward referencing - Dsymbol::setScope(sc); -} - -void StaticIfDeclaration::semantic(Scope *sc) -{ - AttribDeclaration::semantic(sc); -} - -const char *StaticIfDeclaration::kind() -{ - return "static if"; -} - -/***************************** CompileDeclaration *****************************/ - -// These are mixin declarations, like mixin("int x"); - -CompileDeclaration::CompileDeclaration(Loc loc, Expression *exp) - : AttribDeclaration(NULL) -{ - //printf("CompileDeclaration(loc = %d)\n", loc.linnum); - this->loc = loc; - this->exp = exp; - this->scopesym = NULL; - this->compiled = 0; -} - -Dsymbol *CompileDeclaration::syntaxCopy(Dsymbol *s) -{ - //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars()); - return new CompileDeclaration(loc, exp->syntaxCopy()); -} - -void CompileDeclaration::addMember(Scope *sc, ScopeDsymbol *sds) -{ - //printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum); - this->scopesym = sds; -} - -void CompileDeclaration::setScope(Scope *sc) -{ - Dsymbol::setScope(sc); -} - -void CompileDeclaration::compileIt(Scope *sc) -{ - //printf("CompileDeclaration::compileIt(loc = %d) %s\n", loc.linnum, exp->toChars()); - sc = sc->startCTFE(); - exp = exp->semantic(sc); - exp = resolveProperties(sc, exp); - sc = sc->endCTFE(); - - if (exp->op != TOKerror) - { - Expression *e = exp->ctfeInterpret(); - StringExp *se = e->toStringExp(); - if (!se) - exp->error("argument to mixin must be a string, not (%s) of type %s", exp->toChars(), exp->type->toChars()); - else - { - se = se->toUTF8(sc); - unsigned errors = global.errors; - Parser p(loc, sc->module, (utf8_t *)se->string, se->len, 0); - p.nextToken(); - - decl = p.parseDeclDefs(0); - if (p.token.value != TOKeof) - exp->error("incomplete mixin declaration (%s)", se->toChars()); - if (p.errors) - { - assert(global.errors != errors); - decl = NULL; - } - } - } -} - -void CompileDeclaration::semantic(Scope *sc) -{ - //printf("CompileDeclaration::semantic()\n"); - - if (!compiled) - { - compileIt(sc); - AttribDeclaration::addMember(sc, scopesym); - compiled = 1; - - if (scope && decl) - { - for (size_t i = 0; i < decl->dim; i++) - { - Dsymbol *s = (*decl)[i]; - s->setScope(scope); - } - } - } - AttribDeclaration::semantic(sc); -} - -const char *CompileDeclaration::kind() -{ - return "mixin"; -} - -/***************************** UserAttributeDeclaration *****************************/ - -UserAttributeDeclaration::UserAttributeDeclaration(Expressions *atts, Dsymbols *decl) - : AttribDeclaration(decl) -{ - //printf("UserAttributeDeclaration()\n"); - this->atts = atts; -} - -Dsymbol *UserAttributeDeclaration::syntaxCopy(Dsymbol *s) -{ - //printf("UserAttributeDeclaration::syntaxCopy('%s')\n", toChars()); - assert(!s); - return new UserAttributeDeclaration( - Expression::arraySyntaxCopy(this->atts), - Dsymbol::arraySyntaxCopy(decl)); -} - -Scope *UserAttributeDeclaration::newScope(Scope *sc) -{ - Scope *sc2 = sc; - if (atts && atts->dim) - { - // create new one for changes - sc2 = sc->copy(); - sc2->userAttribDecl = this; - } - return sc2; -} - -void UserAttributeDeclaration::setScope(Scope *sc) -{ - //printf("UserAttributeDeclaration::setScope() %p\n", this); - if (decl) - Dsymbol::setScope(sc); // for forward reference of UDAs - - return AttribDeclaration::setScope(sc); -} - -void UserAttributeDeclaration::semantic(Scope *sc) -{ - //printf("UserAttributeDeclaration::semantic() %p\n", this); - if (decl && !scope) - Dsymbol::setScope(sc); // for function local symbols - - return AttribDeclaration::semantic(sc); -} - -void UserAttributeDeclaration::semantic2(Scope *sc) -{ - if (decl && atts && atts->dim) - { - if (atts && atts->dim && scope) - { - scope = NULL; - arrayExpressionSemantic(atts, sc, true); // run semantic - } - } - - AttribDeclaration::semantic2(sc); -} - -Expressions *UserAttributeDeclaration::concat(Expressions *udas1, Expressions *udas2) -{ - Expressions *udas; - if (!udas1 || udas1->dim == 0) - udas = udas2; - else if (!udas2 || udas2->dim == 0) - udas = udas1; - else - { - /* Create a new tuple that combines them - * (do not append to left operand, as this is a copy-on-write operation) - */ - udas = new Expressions(); - udas->push(new TupleExp(Loc(), udas1)); - udas->push(new TupleExp(Loc(), udas2)); - } - return udas; -} - -Expressions *UserAttributeDeclaration::getAttributes() -{ - if (scope) - { - Scope *sc = scope; - scope = NULL; - arrayExpressionSemantic(atts, sc); - } - - Expressions *exps = new Expressions(); - if (userAttribDecl) - exps->push(new TupleExp(Loc(), userAttribDecl->getAttributes())); - if (atts && atts->dim) - exps->push(new TupleExp(Loc(), atts)); - - return exps; -} - -const char *UserAttributeDeclaration::kind() -{ - return "UserAttribute"; -} diff --git a/src/attrib.d b/src/attrib.d new file mode 100644 index 000000000000..19a4020ab8a5 --- /dev/null +++ b/src/attrib.d @@ -0,0 +1,1408 @@ +// 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.attrib; + +import core.stdc.stdio, core.stdc.string; +import ddmd.aggregate, ddmd.arraytypes, ddmd.cond, ddmd.dclass, ddmd.declaration, ddmd.dinterpret, ddmd.dmodule, ddmd.dscope, ddmd.dstruct, ddmd.dsymbol, ddmd.dtemplate, ddmd.errors, ddmd.expression, ddmd.func, ddmd.globals, ddmd.hdrgen, ddmd.id, ddmd.identifier, ddmd.mars, ddmd.mtype, ddmd.parse, ddmd.root.outbuffer, ddmd.root.rmem, ddmd.tokens, ddmd.utf, ddmd.visitor; + +/**************************************************************/ +extern (C++) class AttribDeclaration : Dsymbol +{ +public: + Dsymbols* decl; // array of Dsymbol's + + /********************************* AttribDeclaration ****************************/ + final extern (D) this(Dsymbols* decl) + { + super(); + this.decl = decl; + } + + Dsymbols* include(Scope* sc, ScopeDsymbol sds) + { + return decl; + } + + final int apply(Dsymbol_apply_ft_t fp, void* param) + { + Dsymbols* d = include(_scope, null); + if (d) + { + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + if (s) + { + if (s.apply(fp, param)) + return 1; + } + } + } + return 0; + } + + /**************************************** + * Create a new scope if one or more given attributes + * are different from the sc's. + * If the returned scope != sc, the caller should pop + * the scope after it used. + */ + final static Scope* createNewScope(Scope* sc, StorageClass stc, LINK linkage, Prot protection, int explicitProtection, structalign_t structalign, PINLINE inlining) + { + Scope* sc2 = sc; + if (stc != sc.stc || linkage != sc.linkage || !protection.isSubsetOf(sc.protection) || explicitProtection != sc.explicitProtection || structalign != sc.structalign || inlining != sc.inlining) + { + // create new one for changes + sc2 = sc.copy(); + sc2.stc = stc; + sc2.linkage = linkage; + sc2.protection = protection; + sc2.explicitProtection = explicitProtection; + sc2.structalign = structalign; + sc2.inlining = inlining; + } + return sc2; + } + + /**************************************** + * A hook point to supply scope for members. + * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this. + */ + Scope* newScope(Scope* sc) + { + return sc; + } + + void addMember(Scope* sc, ScopeDsymbol sds) + { + Dsymbols* d = include(sc, sds); + if (d) + { + Scope* sc2 = newScope(sc); + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + //printf("\taddMember %s to %s\n", s->toChars(), sds->toChars()); + s.addMember(sc2, sds); + } + if (sc2 != sc) + sc2.pop(); + } + } + + void setScope(Scope* sc) + { + Dsymbols* d = include(sc, null); + //printf("\tAttribDeclaration::setScope '%s', d = %p\n",toChars(), d); + if (d) + { + Scope* sc2 = newScope(sc); + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + s.setScope(sc2); + } + if (sc2 != sc) + sc2.pop(); + } + } + + void importAll(Scope* sc) + { + Dsymbols* d = include(sc, null); + //printf("\tAttribDeclaration::importAll '%s', d = %p\n", toChars(), d); + if (d) + { + Scope* sc2 = newScope(sc); + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + s.importAll(sc2); + } + if (sc2 != sc) + sc2.pop(); + } + } + + void semantic(Scope* sc) + { + Dsymbols* d = include(sc, null); + //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d); + if (d) + { + Scope* sc2 = newScope(sc); + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + s.semantic(sc2); + } + if (sc2 != sc) + sc2.pop(); + } + } + + void semantic2(Scope* sc) + { + Dsymbols* d = include(sc, null); + if (d) + { + Scope* sc2 = newScope(sc); + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + s.semantic2(sc2); + } + if (sc2 != sc) + sc2.pop(); + } + } + + void semantic3(Scope* sc) + { + Dsymbols* d = include(sc, null); + if (d) + { + Scope* sc2 = newScope(sc); + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + s.semantic3(sc2); + } + if (sc2 != sc) + sc2.pop(); + } + } + + void addComment(const(char)* comment) + { + //printf("AttribDeclaration::addComment %s\n", comment); + if (comment) + { + Dsymbols* d = include(null, null); + if (d) + { + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + //printf("AttribDeclaration::addComment %s\n", s->toChars()); + s.addComment(comment); + } + } + } + } + + const(char)* kind() + { + return "attribute"; + } + + bool oneMember(Dsymbol* ps, Identifier ident) + { + Dsymbols* d = include(null, null); + return Dsymbol.oneMembers(d, ps, ident); + } + + void setFieldOffset(AggregateDeclaration ad, uint* poffset, bool isunion) + { + Dsymbols* d = include(null, null); + if (d) + { + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + s.setFieldOffset(ad, poffset, isunion); + } + } + } + + final bool hasPointers() + { + Dsymbols* d = include(null, null); + if (d) + { + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + if (s.hasPointers()) + return true; + } + } + return false; + } + + final bool hasStaticCtorOrDtor() + { + Dsymbols* d = include(null, null); + if (d) + { + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + if (s.hasStaticCtorOrDtor()) + return true; + } + } + return false; + } + + final void checkCtorConstInit() + { + Dsymbols* d = include(null, null); + if (d) + { + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + s.checkCtorConstInit(); + } + } + } + + /**************************************** + */ + final void addLocalClass(ClassDeclarations* aclasses) + { + Dsymbols* d = include(null, null); + if (d) + { + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + s.addLocalClass(aclasses); + } + } + } + + final AttribDeclaration isAttribDeclaration() + { + return this; + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) class StorageClassDeclaration : AttribDeclaration +{ +public: + StorageClass stc; + + /************************* StorageClassDeclaration ****************************/ + final extern (D) this(StorageClass stc, Dsymbols* decl) + { + super(decl); + this.stc = stc; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + assert(!s); + return new StorageClassDeclaration(stc, Dsymbol.arraySyntaxCopy(decl)); + } + + final Scope* newScope(Scope* sc) + { + StorageClass scstc = sc.stc; + /* These sets of storage classes are mutually exclusive, + * so choose the innermost or most recent one. + */ + if (stc & (STCauto | STCscope | STCstatic | STCextern | STCmanifest)) + scstc &= ~(STCauto | STCscope | STCstatic | STCextern | STCmanifest); + if (stc & (STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared)) + scstc &= ~(STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared); + if (stc & (STCconst | STCimmutable | STCmanifest)) + scstc &= ~(STCconst | STCimmutable | STCmanifest); + if (stc & (STCgshared | STCshared | STCtls)) + scstc &= ~(STCgshared | STCshared | STCtls); + if (stc & (STCsafe | STCtrusted | STCsystem)) + scstc &= ~(STCsafe | STCtrusted | STCsystem); + scstc |= stc; + //printf("scstc = x%llx\n", scstc); + return createNewScope(sc, scstc, sc.linkage, sc.protection, sc.explicitProtection, sc.structalign, sc.inlining); + } + + final bool oneMember(Dsymbol* ps, Identifier ident) + { + bool t = Dsymbol.oneMembers(decl, ps, ident); + if (t && *ps) + { + /* This is to deal with the following case: + * struct Tick { + * template to(T) { const T to() { ... } } + * } + * For eponymous function templates, the 'const' needs to get attached to 'to' + * before the semantic analysis of 'to', so that template overloading based on the + * 'this' pointer can be successful. + */ + FuncDeclaration fd = (*ps).isFuncDeclaration(); + if (fd) + { + /* Use storage_class2 instead of storage_class otherwise when we do .di generation + * we'll wind up with 'const const' rather than 'const'. + */ + /* Don't think we need to worry about mutually exclusive storage classes here + */ + fd.storage_class2 |= stc; + } + } + return t; + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) final class DeprecatedDeclaration : StorageClassDeclaration +{ +public: + Expression msg; + + /********************************* DeprecatedDeclaration ****************************/ + extern (D) this(Expression msg, Dsymbols* decl) + { + super(STCdeprecated, decl); + this.msg = msg; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + assert(!s); + return new DeprecatedDeclaration(msg.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl)); + } + + void setScope(Scope* sc) + { + assert(msg); + char* depmsg = null; + StringExp se = msg.toStringExp(); + if (se) + depmsg = cast(char*)se.string; + else + msg.error("string expected, not '%s'", msg.toChars()); + Scope* scx = sc.push(); + scx.depmsg = depmsg; + StorageClassDeclaration.setScope(scx); + scx.pop(); + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) final class LinkDeclaration : AttribDeclaration +{ +public: + LINK linkage; + + /********************************* LinkDeclaration ****************************/ + extern (D) this(LINK p, Dsymbols* decl) + { + super(decl); + //printf("LinkDeclaration(linkage = %d, decl = %p)\n", p, decl); + linkage = p; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + assert(!s); + return new LinkDeclaration(linkage, Dsymbol.arraySyntaxCopy(decl)); + } + + Scope* newScope(Scope* sc) + { + return createNewScope(sc, sc.stc, this.linkage, sc.protection, sc.explicitProtection, sc.structalign, sc.inlining); + } + + char* toChars() + { + return cast(char*)"extern ()"; + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) final class ProtDeclaration : AttribDeclaration +{ +public: + Prot protection; + Identifiers* pkg_identifiers; + + /********************************* ProtDeclaration ****************************/ + /** + * Params: + * loc = source location of attribute token + * p = protection attribute data + * decl = declarations which are affected by this protection attribute + */ + extern (D) this(Loc loc, Prot p, Dsymbols* decl) + { + super(decl); + this.loc = loc; + this.protection = p; + this.pkg_identifiers = null; + //printf("decl = %p\n", decl); + } + + /** + * Params: + * loc = source location of attribute token + * pkg_identifiers = list of identifiers for a qualified package name + * decl = declarations which are affected by this protection attribute + */ + extern (D) this(Loc loc, Identifiers* pkg_identifiers, Dsymbols* decl) + { + super(decl); + this.loc = loc; + this.protection.kind = PROTpackage; + this.protection.pkg = null; + this.pkg_identifiers = pkg_identifiers; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + assert(!s); + if (protection.kind == PROTpackage) + return new ProtDeclaration(this.loc, pkg_identifiers, Dsymbol.arraySyntaxCopy(decl)); + else + return new ProtDeclaration(this.loc, protection, Dsymbol.arraySyntaxCopy(decl)); + } + + Scope* newScope(Scope* sc) + { + if (pkg_identifiers) + semantic(sc); + return createNewScope(sc, sc.stc, sc.linkage, this.protection, 1, sc.structalign, sc.inlining); + } + + void addMember(Scope* sc, ScopeDsymbol sds) + { + if (pkg_identifiers) + { + Dsymbol tmp; + Package.resolve(pkg_identifiers, &tmp, null); + protection.pkg = tmp ? tmp.isPackage() : null; + pkg_identifiers = null; + } + if (protection.kind == PROTpackage && protection.pkg && sc._module) + { + Module m = sc._module; + Package pkg = m.parent ? m.parent.isPackage() : null; + if (!pkg || !protection.pkg.isAncestorPackageOf(pkg)) + error("does not bind to one of ancestor packages of module '%s'", m.toPrettyChars(true)); + } + return AttribDeclaration.addMember(sc, sds); + } + + const(char)* kind() + { + return "protection attribute"; + } + + const(char)* toPrettyChars(bool) + { + assert(protection.kind > PROTundefined); + OutBuffer buf; + buf.writeByte('\''); + protectionToBuffer(&buf, protection); + buf.writeByte('\''); + return buf.extractString(); + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) final class AlignDeclaration : AttribDeclaration +{ +public: + uint salign; + + /********************************* AlignDeclaration ****************************/ + extern (D) this(uint sa, Dsymbols* decl) + { + super(decl); + salign = sa; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + assert(!s); + return new AlignDeclaration(salign, Dsymbol.arraySyntaxCopy(decl)); + } + + Scope* newScope(Scope* sc) + { + return createNewScope(sc, sc.stc, sc.linkage, sc.protection, sc.explicitProtection, this.salign, sc.inlining); + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) final class AnonDeclaration : AttribDeclaration +{ +public: + bool isunion; + structalign_t alignment; + int sem; // 1 if successful semantic() + + /********************************* AnonDeclaration ****************************/ + extern (D) this(Loc loc, bool isunion, Dsymbols* decl) + { + super(decl); + this.loc = loc; + this.alignment = 0; + this.isunion = isunion; + this.sem = 0; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + assert(!s); + return new AnonDeclaration(loc, isunion, Dsymbol.arraySyntaxCopy(decl)); + } + + void semantic(Scope* sc) + { + //printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", this); + assert(sc.parent); + Dsymbol p = sc.parent.pastMixin(); + AggregateDeclaration ad = p.isAggregateDeclaration(); + if (!ad) + { + .error(loc, "%s can only be a part of an aggregate, not %s %s", kind(), p.kind(), p.toChars()); + return; + } + alignment = sc.structalign; + if (decl) + { + sc = sc.push(); + sc.stc &= ~(STCauto | STCscope | STCstatic | STCtls | STCgshared); + sc.inunion = isunion; + sc.flags = 0; + for (size_t i = 0; i < decl.dim; i++) + { + Dsymbol s = (*decl)[i]; + s.semantic(sc); + } + sc = sc.pop(); + } + } + + void setFieldOffset(AggregateDeclaration ad, uint* poffset, bool isunion) + { + //printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", this); + if (decl) + { + /* This works by treating an AnonDeclaration as an aggregate 'member', + * so in order to place that member we need to compute the member's + * size and alignment. + */ + size_t fieldstart = ad.fields.dim; + /* Hackishly hijack ad's structsize and alignsize fields + * for use in our fake anon aggregate member. + */ + uint savestructsize = ad.structsize; + uint savealignsize = ad.alignsize; + ad.structsize = 0; + ad.alignsize = 0; + uint offset = 0; + for (size_t i = 0; i < decl.dim; i++) + { + Dsymbol s = (*decl)[i]; + s.setFieldOffset(ad, &offset, this.isunion); + if (this.isunion) + offset = 0; + } + uint anonstructsize = ad.structsize; + uint anonalignsize = ad.alignsize; + ad.structsize = savestructsize; + ad.alignsize = savealignsize; + if (fieldstart == ad.fields.dim) + { + /* Bugzilla 13613: If the fields in this->members had been already + * added in ad->fields, just update *poffset for the subsequent + * field offset calculation. + */ + *poffset = ad.structsize; + return; + } + // 0 sized structs are set to 1 byte + // TODO: is this corect hebavior? + if (anonstructsize == 0) + { + anonstructsize = 1; + anonalignsize = 1; + } + /* Given the anon 'member's size and alignment, + * go ahead and place it. + */ + uint anonoffset = AggregateDeclaration.placeField(poffset, anonstructsize, anonalignsize, alignment, &ad.structsize, &ad.alignsize, isunion); + // Add to the anon fields the base offset of this anonymous aggregate + //printf("anon fields, anonoffset = %d\n", anonoffset); + for (size_t i = fieldstart; i < ad.fields.dim; i++) + { + VarDeclaration v = ad.fields[i]; + //printf("\t[%d] %s %d\n", i, v->toChars(), v->offset); + v.offset += anonoffset; + } + } + } + + const(char)* kind() + { + return (isunion ? "anonymous union" : "anonymous struct"); + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) final class PragmaDeclaration : AttribDeclaration +{ +public: + Expressions* args; // array of Expression's + + /********************************* PragmaDeclaration ****************************/ + extern (D) this(Loc loc, Identifier ident, Expressions* args, Dsymbols* decl) + { + super(decl); + this.loc = loc; + this.ident = ident; + this.args = args; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + //printf("PragmaDeclaration::syntaxCopy(%s)\n", toChars()); + assert(!s); + return new PragmaDeclaration(loc, ident, Expression.arraySyntaxCopy(args), Dsymbol.arraySyntaxCopy(decl)); + } + + void semantic(Scope* sc) + { + // Should be merged with PragmaStatement + //printf("\tPragmaDeclaration::semantic '%s'\n",toChars()); + if (ident == Id.msg) + { + if (args) + { + for (size_t i = 0; i < args.dim; i++) + { + Expression e = (*args)[i]; + sc = sc.startCTFE(); + e = e.semantic(sc); + e = resolveProperties(sc, e); + sc = sc.endCTFE(); + // pragma(msg) is allowed to contain types as well as expressions + e = ctfeInterpretForPragmaMsg(e); + if (e.op == TOKerror) + { + errorSupplemental(loc, "while evaluating pragma(msg, %s)", (*args)[i].toChars()); + return; + } + StringExp se = e.toStringExp(); + if (se) + { + se = se.toUTF8(sc); + fprintf(stderr, "%.*s", cast(int)se.len, cast(char*)se.string); + } + else + fprintf(stderr, "%s", e.toChars()); + } + fprintf(stderr, "\n"); + } + goto Lnodecl; + } + else if (ident == Id.lib) + { + if (!args || args.dim != 1) + error("string expected for library name"); + else + { + Expression e = (*args)[0]; + sc = sc.startCTFE(); + e = e.semantic(sc); + e = resolveProperties(sc, e); + sc = sc.endCTFE(); + e = e.ctfeInterpret(); + (*args)[0] = e; + if (e.op == TOKerror) + goto Lnodecl; + StringExp se = e.toStringExp(); + if (!se) + error("string expected for library name, not '%s'", e.toChars()); + else + { + char* name = cast(char*)mem.xmalloc(se.len + 1); + memcpy(name, se.string, se.len); + name[se.len] = 0; + if (global.params.verbose) + fprintf(global.stdmsg, "library %s\n", name); + if (global.params.moduleDeps && !global.params.moduleDepsFile) + { + OutBuffer* ob = global.params.moduleDeps; + Module imod = sc.instantiatingModule(); + ob.writestring("depsLib "); + ob.writestring(imod.toPrettyChars()); + ob.writestring(" ("); + escapePath(ob, imod.srcfile.toChars()); + ob.writestring(") : "); + ob.writestring(cast(char*)name); + ob.writenl(); + } + mem.xfree(name); + } + } + goto Lnodecl; + } + else if (ident == Id.startaddress) + { + if (!args || args.dim != 1) + error("function name expected for start address"); + else + { + /* Bugzilla 11980: + * resolveProperties and ctfeInterpret call are not necessary. + */ + Expression e = (*args)[0]; + sc = sc.startCTFE(); + e = e.semantic(sc); + sc = sc.endCTFE(); + (*args)[0] = e; + Dsymbol sa = getDsymbol(e); + if (!sa || !sa.isFuncDeclaration()) + error("function name expected for start address, not '%s'", e.toChars()); + } + goto Lnodecl; + } + else if (ident == Id.Pinline) + { + goto Ldecl; + } + else if (ident == Id.mangle) + { + if (!args) + args = new Expressions(); + if (args.dim != 1) + { + error("string expected for mangled name"); + args.setDim(1); + (*args)[0] = new ErrorExp(); // error recovery + goto Ldecl; + } + Expression e = (*args)[0]; + e = e.semantic(sc); + e = e.ctfeInterpret(); + (*args)[0] = e; + if (e.op == TOKerror) + goto Ldecl; + StringExp se = e.toStringExp(); + if (!se) + { + error("string expected for mangled name, not '%s'", e.toChars()); + goto Ldecl; + } + if (!se.len) + { + error("zero-length string not allowed for mangled name"); + goto Ldecl; + } + if (se.sz != 1) + { + error("mangled name characters can only be of type char"); + goto Ldecl; + } + version (all) + { + /* Note: D language specification should not have any assumption about backend + * implementation. Ideally pragma(mangle) can accept a string of any content. + * + * Therefore, this validation is compiler implementation specific. + */ + for (size_t i = 0; i < se.len;) + { + char* p = cast(char*)se.string; + dchar_t c = p[i]; + if (c < 0x80) + { + if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c != 0 && strchr("$%().:?@[]_", c)) + { + ++i; + continue; + } + else + { + error("char 0x%02x not allowed in mangled name", c); + break; + } + } + if (const(char)* msg = utf_decodeChar(cast(char*)se.string, se.len, &i, &c)) + { + error("%s", msg); + break; + } + if (!isUniAlpha(c)) + { + error("char 0x%04x not allowed in mangled name", c); + break; + } + } + } + } + else if (global.params.ignoreUnsupportedPragmas) + { + if (global.params.verbose) + { + /* Print unrecognized pragmas + */ + fprintf(global.stdmsg, "pragma %s", ident.toChars()); + if (args) + { + for (size_t i = 0; i < args.dim; i++) + { + Expression e = (*args)[i]; + sc = sc.startCTFE(); + e = e.semantic(sc); + e = resolveProperties(sc, e); + sc = sc.endCTFE(); + e = e.ctfeInterpret(); + if (i == 0) + fprintf(global.stdmsg, " ("); + else + fprintf(global.stdmsg, ","); + fprintf(global.stdmsg, "%s", e.toChars()); + } + if (args.dim) + fprintf(global.stdmsg, ")"); + } + fprintf(global.stdmsg, "\n"); + } + goto Lnodecl; + } + else + error("unrecognized pragma(%s)", ident.toChars()); + Ldecl: + if (decl) + { + Scope* sc2 = newScope(sc); + for (size_t i = 0; i < decl.dim; i++) + { + Dsymbol s = (*decl)[i]; + s.semantic(sc2); + if (ident == Id.mangle) + { + assert(args && args.dim == 1); + if (StringExp se = (*args)[0].toStringExp()) + { + char* name = cast(char*)mem.xmalloc(se.len + 1); + memcpy(name, se.string, se.len); + name[se.len] = 0; + uint cnt = setMangleOverride(s, name); + if (cnt > 1) + error("can only apply to a single declaration"); + } + } + } + if (sc2 != sc) + sc2.pop(); + } + return; + Lnodecl: + if (decl) + { + error("pragma is missing closing ';'"); + goto Ldecl; + // do them anyway, to avoid segfaults. + } + } + + Scope* newScope(Scope* sc) + { + if (ident == Id.Pinline) + { + PINLINE inlining = PINLINEdefault; + if (!args || args.dim == 0) + inlining = PINLINEdefault; + else if (args.dim != 1) + { + error("one boolean expression expected for pragma(inline), not %d", args.dim); + args.setDim(1); + (*args)[0] = new ErrorExp(); + } + else + { + Expression e = (*args)[0]; + if (e.op != TOKint64 || !e.type.equals(Type.tbool)) + { + if (e.op != TOKerror) + { + error("pragma(inline, true or false) expected, not %s", e.toChars()); + (*args)[0] = new ErrorExp(); + } + } + else if (e.isBool(true)) + inlining = PINLINEalways; + else if (e.isBool(false)) + inlining = PINLINEnever; + } + return createNewScope(sc, sc.stc, sc.linkage, sc.protection, sc.explicitProtection, sc.structalign, inlining); + } + return sc; + } + + const(char)* kind() + { + return "pragma"; + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) class ConditionalDeclaration : AttribDeclaration +{ +public: + Condition condition; + Dsymbols* elsedecl; // array of Dsymbol's for else block + + /********************************* ConditionalDeclaration ****************************/ + final extern (D) this(Condition condition, Dsymbols* decl, Dsymbols* elsedecl) + { + super(decl); + //printf("ConditionalDeclaration::ConditionalDeclaration()\n"); + this.condition = condition; + this.elsedecl = elsedecl; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + assert(!s); + return new ConditionalDeclaration(condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); + } + + final bool oneMember(Dsymbol* ps, Identifier ident) + { + //printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition->inc); + if (condition.inc) + { + Dsymbols* d = condition.include(null, null) ? decl : elsedecl; + return Dsymbol.oneMembers(d, ps, ident); + } + else + { + bool res = (Dsymbol.oneMembers(decl, ps, ident) && *ps is null && Dsymbol.oneMembers(elsedecl, ps, ident) && *ps is null); + *ps = null; + return res; + } + } + + // Decide if 'then' or 'else' code should be included + Dsymbols* include(Scope* sc, ScopeDsymbol sds) + { + //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, scope); + assert(condition); + return condition.include(_scope ? _scope : sc, sds) ? decl : elsedecl; + } + + final void addComment(const(char)* comment) + { + /* Because addComment is called by the parser, if we called + * include() it would define a version before it was used. + * But it's no problem to drill down to both decl and elsedecl, + * so that's the workaround. + */ + if (comment) + { + Dsymbols* d = decl; + for (int j = 0; j < 2; j++) + { + if (d) + { + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + //printf("ConditionalDeclaration::addComment %s\n", s->toChars()); + s.addComment(comment); + } + } + d = elsedecl; + } + } + } + + void setScope(Scope* sc) + { + Dsymbols* d = include(sc, null); + //printf("\tConditionalDeclaration::setScope '%s', d = %p\n",toChars(), d); + if (d) + { + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + s.setScope(sc); + } + } + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) final class StaticIfDeclaration : ConditionalDeclaration +{ +public: + ScopeDsymbol scopesym; + int addisdone; + + /***************************** StaticIfDeclaration ****************************/ + extern (D) this(Condition condition, Dsymbols* decl, Dsymbols* elsedecl) + { + super(condition, decl, elsedecl); + //printf("StaticIfDeclaration::StaticIfDeclaration()\n"); + scopesym = null; + addisdone = 0; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + assert(!s); + return new StaticIfDeclaration(condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); + } + + /**************************************** + * Different from other AttribDeclaration subclasses, include() call requires + * the completion of addMember and setScope phases. + */ + Dsymbols* include(Scope* sc, ScopeDsymbol sds) + { + //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, scope); + if (condition.inc == 0) + { + assert(scopesym); // addMember is already done + assert(_scope); // setScope is already done + Dsymbols* d = ConditionalDeclaration.include(_scope, scopesym); + if (d && !addisdone) + { + // Add members lazily. + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + s.addMember(_scope, scopesym); + } + // Set the member scopes lazily. + for (size_t i = 0; i < d.dim; i++) + { + Dsymbol s = (*d)[i]; + s.setScope(_scope); + } + addisdone = 1; + } + return d; + } + else + { + return ConditionalDeclaration.include(sc, scopesym); + } + } + + void addMember(Scope* sc, ScopeDsymbol sds) + { + //printf("StaticIfDeclaration::addMember() '%s'\n", toChars()); + /* This is deferred until the condition evaluated later (by the include() call), + * so that expressions in the condition can refer to declarations + * in the same scope, such as: + * + * template Foo(int i) + * { + * const int j = i + 1; + * static if (j == 3) + * const int k; + * } + */ + this.scopesym = sds; + } + + void semantic(Scope* sc) + { + AttribDeclaration.semantic(sc); + } + + void importAll(Scope* sc) + { + // do not evaluate condition before semantic pass + } + + void setScope(Scope* sc) + { + // do not evaluate condition before semantic pass + // But do set the scope, in case we need it for forward referencing + Dsymbol.setScope(sc); + } + + const(char)* kind() + { + return "static if"; + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +// Mixin declarations +extern (C++) final class CompileDeclaration : AttribDeclaration +{ +public: + Expression exp; + ScopeDsymbol scopesym; + int compiled; + + /***************************** CompileDeclaration *****************************/ + // These are mixin declarations, like mixin("int x"); + extern (D) this(Loc loc, Expression exp) + { + super(null); + //printf("CompileDeclaration(loc = %d)\n", loc.linnum); + this.loc = loc; + this.exp = exp; + this.scopesym = null; + this.compiled = 0; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars()); + return new CompileDeclaration(loc, exp.syntaxCopy()); + } + + void addMember(Scope* sc, ScopeDsymbol sds) + { + //printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum); + this.scopesym = sds; + } + + void setScope(Scope* sc) + { + Dsymbol.setScope(sc); + } + + void compileIt(Scope* sc) + { + //printf("CompileDeclaration::compileIt(loc = %d) %s\n", loc.linnum, exp->toChars()); + sc = sc.startCTFE(); + exp = exp.semantic(sc); + exp = resolveProperties(sc, exp); + sc = sc.endCTFE(); + if (exp.op != TOKerror) + { + Expression e = exp.ctfeInterpret(); + StringExp se = e.toStringExp(); + if (!se) + exp.error("argument to mixin must be a string, not (%s) of type %s", exp.toChars(), exp.type.toChars()); + else + { + se = se.toUTF8(sc); + uint errors = global.errors; + scope Parser p = new Parser(loc, sc._module, cast(char*)se.string, se.len, 0); + p.nextToken(); + decl = p.parseDeclDefs(0); + if (p.token.value != TOKeof) + exp.error("incomplete mixin declaration (%s)", se.toChars()); + if (p.errors) + { + assert(global.errors != errors); + decl = null; + } + } + } + } + + void semantic(Scope* sc) + { + //printf("CompileDeclaration::semantic()\n"); + if (!compiled) + { + compileIt(sc); + AttribDeclaration.addMember(sc, scopesym); + compiled = 1; + if (_scope && decl) + { + for (size_t i = 0; i < decl.dim; i++) + { + Dsymbol s = (*decl)[i]; + s.setScope(_scope); + } + } + } + AttribDeclaration.semantic(sc); + } + + const(char)* kind() + { + return "mixin"; + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +/** + * User defined attributes look like: + * @(args, ...) + */ +extern (C++) final class UserAttributeDeclaration : AttribDeclaration +{ +public: + Expressions* atts; + + /***************************** UserAttributeDeclaration *****************************/ + extern (D) this(Expressions* atts, Dsymbols* decl) + { + super(decl); + //printf("UserAttributeDeclaration()\n"); + this.atts = atts; + } + + Dsymbol syntaxCopy(Dsymbol s) + { + //printf("UserAttributeDeclaration::syntaxCopy('%s')\n", toChars()); + assert(!s); + return new UserAttributeDeclaration(Expression.arraySyntaxCopy(this.atts), Dsymbol.arraySyntaxCopy(decl)); + } + + Scope* newScope(Scope* sc) + { + Scope* sc2 = sc; + if (atts && atts.dim) + { + // create new one for changes + sc2 = sc.copy(); + sc2.userAttribDecl = this; + } + return sc2; + } + + void semantic(Scope* sc) + { + //printf("UserAttributeDeclaration::semantic() %p\n", this); + if (decl && !_scope) + Dsymbol.setScope(sc); // for function local symbols + return AttribDeclaration.semantic(sc); + } + + void semantic2(Scope* sc) + { + if (decl && atts && atts.dim) + { + if (atts && atts.dim && _scope) + { + _scope = null; + arrayExpressionSemantic(atts, sc, true); // run semantic + } + } + AttribDeclaration.semantic2(sc); + } + + void setScope(Scope* sc) + { + //printf("UserAttributeDeclaration::setScope() %p\n", this); + if (decl) + Dsymbol.setScope(sc); // for forward reference of UDAs + return AttribDeclaration.setScope(sc); + } + + static Expressions* concat(Expressions* udas1, Expressions* udas2) + { + Expressions* udas; + if (!udas1 || udas1.dim == 0) + udas = udas2; + else if (!udas2 || udas2.dim == 0) + udas = udas1; + else + { + /* Create a new tuple that combines them + * (do not append to left operand, as this is a copy-on-write operation) + */ + udas = new Expressions(); + udas.push(new TupleExp(Loc(), udas1)); + udas.push(new TupleExp(Loc(), udas2)); + } + return udas; + } + + Expressions* getAttributes() + { + if (_scope) + { + Scope* sc = _scope; + _scope = null; + arrayExpressionSemantic(atts, sc); + } + auto exps = new Expressions(); + if (userAttribDecl) + exps.push(new TupleExp(Loc(), userAttribDecl.getAttributes())); + if (atts && atts.dim) + exps.push(new TupleExp(Loc(), atts)); + return exps; + } + + const(char)* kind() + { + return "UserAttribute"; + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) static uint setMangleOverride(Dsymbol s, char* sym) +{ + AttribDeclaration ad = s.isAttribDeclaration(); + if (ad) + { + Dsymbols* decls = ad.include(null, null); + uint nestedCount = 0; + if (decls && decls.dim) + for (size_t i = 0; i < decls.dim; ++i) + nestedCount += setMangleOverride((*decls)[i], sym); + return nestedCount; + } + else if (s.isFuncDeclaration() || s.isVarDeclaration()) + { + s.isDeclaration().mangleOverride = sym; + return 1; + } + else + return 0; +} diff --git a/src/builtin.c b/src/builtin.d similarity index 53% rename from src/builtin.c rename to src/builtin.d index 32f158e5d0f5..a322fb98c519 100644 --- a/src/builtin.c +++ b/src/builtin.d @@ -1,180 +1,166 @@ +// 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 -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/builtin.c - */ +module ddmd.builtin; -#include -#include -#include // strcmp() -#include +import core.stdc.math, core.stdc.string; +import ddmd.arraytypes, ddmd.dmangle, ddmd.errors, ddmd.expression, ddmd.func, ddmd.globals, ddmd.mtype, ddmd.root.port, ddmd.root.stringtable, ddmd.tokens; -#include "mars.h" -#include "declaration.h" -#include "attrib.h" -#include "expression.h" -#include "scope.h" -#include "mtype.h" -#include "aggregate.h" -#include "identifier.h" -#include "id.h" -#include "module.h" -#include "root/port.h" -#include "tokens.h" +extern (C++) alias builtin_fp = Expression function(Loc loc, FuncDeclaration fd, Expressions* arguments); -StringTable builtins; +extern (C++) __gshared StringTable builtins; -void add_builtin(const char *mangle, builtin_fp fp) +extern (C++) void add_builtin(const(char)* mangle, builtin_fp fp) { - builtins.insert(mangle, strlen(mangle))->ptrvalue = (void *)fp; + builtins.insert(mangle, strlen(mangle)).ptrvalue = cast(void*)fp; } -builtin_fp builtin_lookup(const char *mangle) +extern (C++) builtin_fp builtin_lookup(const(char)* mangle) { - if (StringValue *sv = builtins.lookup(mangle, strlen(mangle))) - return (builtin_fp)sv->ptrvalue; - return NULL; + if (StringValue* sv = builtins.lookup(mangle, strlen(mangle))) + return cast(builtin_fp)sv.ptrvalue; + return null; } -Expression *eval_unimp(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_unimp(Loc loc, FuncDeclaration fd, Expressions* arguments) { - return NULL; + return null; } -Expression *eval_sin(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_sin(Loc loc, FuncDeclaration fd, Expressions* arguments) { - Expression *arg0 = (*arguments)[0]; - assert(arg0->op == TOKfloat64); - return new RealExp(loc, sinl(arg0->toReal()), arg0->type); + Expression arg0 = (*arguments)[0]; + assert(arg0.op == TOKfloat64); + return new RealExp(loc, sinl(arg0.toReal()), arg0.type); } -Expression *eval_cos(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_cos(Loc loc, FuncDeclaration fd, Expressions* arguments) { - Expression *arg0 = (*arguments)[0]; - assert(arg0->op == TOKfloat64); - return new RealExp(loc, cosl(arg0->toReal()), arg0->type); + Expression arg0 = (*arguments)[0]; + assert(arg0.op == TOKfloat64); + return new RealExp(loc, cosl(arg0.toReal()), arg0.type); } -Expression *eval_tan(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_tan(Loc loc, FuncDeclaration fd, Expressions* arguments) { - Expression *arg0 = (*arguments)[0]; - assert(arg0->op == TOKfloat64); - return new RealExp(loc, tanl(arg0->toReal()), arg0->type); + Expression arg0 = (*arguments)[0]; + assert(arg0.op == TOKfloat64); + return new RealExp(loc, tanl(arg0.toReal()), arg0.type); } -Expression *eval_sqrt(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_sqrt(Loc loc, FuncDeclaration fd, Expressions* arguments) { - Expression *arg0 = (*arguments)[0]; - assert(arg0->op == TOKfloat64); - return new RealExp(loc, Port::sqrt(arg0->toReal()), arg0->type); + Expression arg0 = (*arguments)[0]; + assert(arg0.op == TOKfloat64); + return new RealExp(loc, Port.sqrt(arg0.toReal()), arg0.type); } -Expression *eval_fabs(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_fabs(Loc loc, FuncDeclaration fd, Expressions* arguments) { - Expression *arg0 = (*arguments)[0]; - assert(arg0->op == TOKfloat64); - return new RealExp(loc, fabsl(arg0->toReal()), arg0->type); + Expression arg0 = (*arguments)[0]; + assert(arg0.op == TOKfloat64); + return new RealExp(loc, fabsl(arg0.toReal()), arg0.type); } -Expression *eval_bsf(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_bsf(Loc loc, FuncDeclaration fd, Expressions* arguments) { - Expression *arg0 = (*arguments)[0]; - assert(arg0->op == TOKint64); - uinteger_t n = arg0->toInteger(); + Expression arg0 = (*arguments)[0]; + assert(arg0.op == TOKint64); + uinteger_t n = arg0.toInteger(); if (n == 0) error(loc, "bsf(0) is undefined"); - n = (n ^ (n - 1)) >> 1; // convert trailing 0s to 1, and zero rest + n = (n ^ (n - 1)) >> 1; // convert trailing 0s to 1, and zero rest int k = 0; - while( n ) - { ++k; - n >>=1; + while (n) + { + ++k; + n >>= 1; } - return new IntegerExp(loc, k, Type::tint32); + return new IntegerExp(loc, k, Type.tint32); } -Expression *eval_bsr(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_bsr(Loc loc, FuncDeclaration fd, Expressions* arguments) { - Expression *arg0 = (*arguments)[0]; - assert(arg0->op == TOKint64); - uinteger_t n = arg0->toInteger(); + Expression arg0 = (*arguments)[0]; + assert(arg0.op == TOKint64); + uinteger_t n = arg0.toInteger(); if (n == 0) error(loc, "bsr(0) is undefined"); int k = 0; - while(n >>= 1) + while (n >>= 1) { ++k; } - return new IntegerExp(loc, k, Type::tint32); + return new IntegerExp(loc, k, Type.tint32); } -Expression *eval_bswap(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_bswap(Loc loc, FuncDeclaration fd, Expressions* arguments) { - Expression *arg0 = (*arguments)[0]; - assert(arg0->op == TOKint64); - uinteger_t n = arg0->toInteger(); - #define BYTEMASK 0x00FF00FF00FF00FFLL - #define SHORTMASK 0x0000FFFF0000FFFFLL - #define INTMASK 0x0000FFFF0000FFFFLL + Expression arg0 = (*arguments)[0]; + assert(arg0.op == TOKint64); + uinteger_t n = arg0.toInteger(); + enum BYTEMASK = 0x00FF00FF00FF00FFL; + enum SHORTMASK = 0x0000FFFF0000FFFFL; + enum INTMASK = 0x0000FFFF0000FFFFL; // swap adjacent ubytes - n = ((n >> 8 ) & BYTEMASK) | ((n & BYTEMASK) << 8 ); + n = ((n >> 8) & BYTEMASK) | ((n & BYTEMASK) << 8); // swap adjacent ushorts n = ((n >> 16) & SHORTMASK) | ((n & SHORTMASK) << 16); - TY ty = arg0->type->toBasetype()->ty; + TY ty = arg0.type.toBasetype().ty; // If 64 bits, we need to swap high and low uints if (ty == Tint64 || ty == Tuns64) n = ((n >> 32) & INTMASK) | ((n & INTMASK) << 32); - return new IntegerExp(loc, n, arg0->type); + return new IntegerExp(loc, n, arg0.type); } -Expression *eval_popcnt(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_popcnt(Loc loc, FuncDeclaration fd, Expressions* arguments) { - Expression *arg0 = (*arguments)[0]; - assert(arg0->op == TOKint64); - uinteger_t n = arg0->toInteger(); + Expression arg0 = (*arguments)[0]; + assert(arg0.op == TOKint64); + uinteger_t n = arg0.toInteger(); int cnt = 0; while (n) { cnt += (n & 1); n >>= 1; } - return new IntegerExp(loc, cnt, arg0->type); + return new IntegerExp(loc, cnt, arg0.type); } -Expression *eval_yl2x(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_yl2x(Loc loc, FuncDeclaration fd, Expressions* arguments) { - Expression *arg0 = (*arguments)[0]; - assert(arg0->op == TOKfloat64); - Expression *arg1 = (*arguments)[1]; - assert(arg1->op == TOKfloat64); - longdouble x = arg0->toReal(); - longdouble y = arg1->toReal(); - longdouble result; - Port::yl2x_impl(&x, &y, &result); - return new RealExp(loc, result, arg0->type); + Expression arg0 = (*arguments)[0]; + assert(arg0.op == TOKfloat64); + Expression arg1 = (*arguments)[1]; + assert(arg1.op == TOKfloat64); + real x = arg0.toReal(); + real y = arg1.toReal(); + real result; + Port.yl2x_impl(&x, &y, &result); + return new RealExp(loc, result, arg0.type); } -Expression *eval_yl2xp1(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_yl2xp1(Loc loc, FuncDeclaration fd, Expressions* arguments) { - Expression *arg0 = (*arguments)[0]; - assert(arg0->op == TOKfloat64); - Expression *arg1 = (*arguments)[1]; - assert(arg1->op == TOKfloat64); - longdouble x = arg0->toReal(); - longdouble y = arg1->toReal(); - longdouble result; - Port::yl2xp1_impl(&x, &y, &result); - return new RealExp(loc, result, arg0->type); + Expression arg0 = (*arguments)[0]; + assert(arg0.op == TOKfloat64); + Expression arg1 = (*arguments)[1]; + assert(arg1.op == TOKfloat64); + real x = arg0.toReal(); + real y = arg1.toReal(); + real result; + Port.yl2xp1_impl(&x, &y, &result); + return new RealExp(loc, result, arg0.type); } -void builtin_init() +extern (C++) void builtin_init() { builtins._init(47); - // @safe @nogc pure nothrow real function(real) add_builtin("_D4core4math3sinFNaNbNiNfeZe", &eval_sin); add_builtin("_D4core4math3cosFNaNbNiNfeZe", &eval_cos); @@ -183,7 +169,6 @@ void builtin_init() add_builtin("_D4core4math4fabsFNaNbNiNfeZe", &eval_fabs); add_builtin("_D4core4math5expm1FNaNbNiNfeZe", &eval_unimp); add_builtin("_D4core4math4exp21FNaNbNiNfeZe", &eval_unimp); - // @trusted @nogc pure nothrow real function(real) add_builtin("_D4core4math3sinFNaNbNiNeeZe", &eval_sin); add_builtin("_D4core4math3cosFNaNbNiNeeZe", &eval_cos); @@ -192,16 +177,13 @@ void builtin_init() add_builtin("_D4core4math4fabsFNaNbNiNeeZe", &eval_fabs); add_builtin("_D4core4math5expm1FNaNbNiNeeZe", &eval_unimp); add_builtin("_D4core4math4exp21FNaNbNiNeeZe", &eval_unimp); - // @safe @nogc pure nothrow double function(double) add_builtin("_D4core4math4sqrtFNaNbNiNfdZd", &eval_sqrt); // @safe @nogc pure nothrow float function(float) add_builtin("_D4core4math4sqrtFNaNbNiNffZf", &eval_sqrt); - // @safe @nogc pure nothrow real function(real, real) add_builtin("_D4core4math5atan2FNaNbNiNfeeZe", &eval_unimp); - - if (Port::yl2x_supported) + if (Port.yl2x_supported) { add_builtin("_D4core4math4yl2xFNaNbNiNfeeZe", &eval_yl2x); } @@ -209,8 +191,7 @@ void builtin_init() { add_builtin("_D4core4math4yl2xFNaNbNiNfeeZe", &eval_unimp); } - - if (Port::yl2xp1_supported) + if (Port.yl2xp1_supported) { add_builtin("_D4core4math6yl2xp1FNaNbNiNfeeZe", &eval_yl2xp1); } @@ -218,10 +199,8 @@ void builtin_init() { add_builtin("_D4core4math6yl2xp1FNaNbNiNfeeZe", &eval_unimp); } - // @safe @nogc pure nothrow long function(real) add_builtin("_D4core4math6rndtolFNaNbNiNfeZl", &eval_unimp); - // @safe @nogc pure nothrow real function(real) add_builtin("_D3std4math3sinFNaNbNiNfeZe", &eval_sin); add_builtin("_D3std4math3cosFNaNbNiNfeZe", &eval_cos); @@ -230,7 +209,6 @@ void builtin_init() add_builtin("_D3std4math4fabsFNaNbNiNfeZe", &eval_fabs); add_builtin("_D3std4math5expm1FNaNbNiNfeZe", &eval_unimp); add_builtin("_D3std4math4exp21FNaNbNiNfeZe", &eval_unimp); - // @trusted @nogc pure nothrow real function(real) add_builtin("_D3std4math3sinFNaNbNiNeeZe", &eval_sin); add_builtin("_D3std4math3cosFNaNbNiNeeZe", &eval_cos); @@ -239,16 +217,13 @@ void builtin_init() add_builtin("_D3std4math4fabsFNaNbNiNeeZe", &eval_fabs); add_builtin("_D3std4math5expm1FNaNbNiNeeZe", &eval_unimp); add_builtin("_D3std4math4exp21FNaNbNiNeeZe", &eval_unimp); - // @safe @nogc pure nothrow double function(double) add_builtin("_D3std4math4sqrtFNaNbNiNfdZd", &eval_sqrt); // @safe @nogc pure nothrow float function(float) add_builtin("_D3std4math4sqrtFNaNbNiNffZf", &eval_sqrt); - // @safe @nogc pure nothrow real function(real, real) add_builtin("_D3std4math5atan2FNaNbNiNfeeZe", &eval_unimp); - - if (Port::yl2x_supported) + if (Port.yl2x_supported) { add_builtin("_D3std4math4yl2xFNaNbNiNfeeZe", &eval_yl2x); } @@ -256,8 +231,7 @@ void builtin_init() { add_builtin("_D3std4math4yl2xFNaNbNiNfeeZe", &eval_unimp); } - - if (Port::yl2xp1_supported) + if (Port.yl2xp1_supported) { add_builtin("_D3std4math6yl2xp1FNaNbNiNfeeZe", &eval_yl2xp1); } @@ -265,27 +239,20 @@ void builtin_init() { add_builtin("_D3std4math6yl2xp1FNaNbNiNfeeZe", &eval_unimp); } - // @safe @nogc pure nothrow long function(real) add_builtin("_D3std4math6rndtolFNaNbNiNfeZl", &eval_unimp); - // @safe @nogc pure nothrow int function(uint) add_builtin("_D4core5bitop3bsfFNaNbNiNfkZi", &eval_bsf); add_builtin("_D4core5bitop3bsrFNaNbNiNfkZi", &eval_bsr); - // @safe @nogc pure nothrow int function(ulong) add_builtin("_D4core5bitop3bsfFNaNbNiNfmZi", &eval_bsf); add_builtin("_D4core5bitop3bsrFNaNbNiNfmZi", &eval_bsr); - // @safe @nogc pure nothrow uint function(uint) add_builtin("_D4core5bitop5bswapFNaNbNiNfkZk", &eval_bswap); - // @safe @nogc pure nothrow int function(uint) add_builtin("_D4core5bitop7_popcntFNaNbNiNfkZi", &eval_popcnt); - // @safe @nogc pure nothrow ushort function(ushort) add_builtin("_D4core5bitop7_popcntFNaNbNiNftZt", &eval_popcnt); - // @safe @nogc pure nothrow int function(ulong) if (global.params.is64bit) add_builtin("_D4core5bitop7_popcntFNaNbNiNfmZi", &eval_popcnt); @@ -295,28 +262,27 @@ void builtin_init() * Determine if function is a builtin one that we can * evaluate at compile time. */ -BUILTIN isBuiltin(FuncDeclaration *fd) +extern (C++) BUILTIN isBuiltin(FuncDeclaration fd) { - if (fd->builtin == BUILTINunknown) + if (fd.builtin == BUILTINunknown) { builtin_fp fp = builtin_lookup(mangleExact(fd)); - fd->builtin = fp ? BUILTINyes : BUILTINno; + fd.builtin = fp ? BUILTINyes : BUILTINno; } - return fd->builtin; + return fd.builtin; } /************************************** * Evaluate builtin function. * Return result; NULL if cannot evaluate it. */ - -Expression *eval_builtin(Loc loc, FuncDeclaration *fd, Expressions *arguments) +extern (C++) Expression eval_builtin(Loc loc, FuncDeclaration fd, Expressions* arguments) { - if (fd->builtin == BUILTINyes) + if (fd.builtin == BUILTINyes) { builtin_fp fp = builtin_lookup(mangleExact(fd)); assert(fp); return fp(loc, fd, arguments); } - return NULL; + return null; } diff --git a/src/canthrow.c b/src/canthrow.c deleted file mode 100644 index 0e827344801b..000000000000 --- a/src/canthrow.c +++ /dev/null @@ -1,239 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/canthrow.c - */ - -#include -#include - -#include "mars.h" -#include "init.h" -#include "expression.h" -#include "template.h" -#include "statement.h" -#include "mtype.h" -#include "utf.h" -#include "declaration.h" -#include "aggregate.h" -#include "scope.h" -#include "attrib.h" -#include "tokens.h" - -bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow); -bool walkPostorder(Expression *e, StoppableVisitor *v); - -/******************************************** - * Returns true if the expression may throw exceptions. - * If 'mustNotThrow' is true, generate an error if it throws - */ - -bool canThrow(Expression *e, FuncDeclaration *func, bool mustNotThrow) -{ - //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars()); - - // stop walking if we determine this expression can throw - class CanThrow : public StoppableVisitor - { - FuncDeclaration *func; - bool mustNotThrow; - - public: - CanThrow(FuncDeclaration *func, bool mustNotThrow) - : func(func), mustNotThrow(mustNotThrow) - { - } - - void visit(Expression *) - { - } - - void visit(DeclarationExp *de) - { - stop = Dsymbol_canThrow(de->declaration, func, mustNotThrow); - } - - void visit(CallExp *ce) - { - if (global.errors && !ce->e1->type) - return; // error recovery - - /* If calling a function or delegate that is typed as nothrow, - * then this expression cannot throw. - * Note that pure functions can throw. - */ - Type *t = ce->e1->type->toBasetype(); - if (ce->f && ce->f == func) - ; - else if (t->ty == Tfunction && ((TypeFunction *)t)->isnothrow) - ; - else if (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow) - ; - else - { - if (mustNotThrow) - { - const char *s; - if (ce->f) - s = ce->f->toPrettyChars(); - else if (ce->e1->op == TOKstar) - { - // print 'fp' if ce->e1 is (*fp) - s = ((PtrExp *)ce->e1)->e1->toChars(); - } - else - s = ce->e1->toChars(); - ce->error("'%s' is not nothrow", s); - } - stop = true; - } - } - - void visit(NewExp *ne) - { - if (ne->member) - { - // See if constructor call can throw - Type *t = ne->member->type->toBasetype(); - if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) - { - if (mustNotThrow) - ne->error("constructor %s is not nothrow", ne->member->toChars()); - stop = true; - } - } - // regard storage allocation failures as not recoverable - } - - void visit(AssignExp *ae) - { - // blit-init cannot throw - if (ae->op == TOKblit) - return; - - /* Element-wise assignment could invoke postblits. - */ - Type *t; - if (ae->type->toBasetype()->ty == Tsarray) - { - if (!ae->e2->isLvalue()) - return; - t = ae->type; - } - else if (ae->e1->op == TOKslice) - t = ((SliceExp *)ae->e1)->e1->type; - else - return; - - Type *tv = t->baseElemOf(); - if (tv->ty != Tstruct) - return; - StructDeclaration *sd = ((TypeStruct *)tv)->sym; - if (!sd->postblit || sd->postblit->type->ty != Tfunction) - return; - - if (((TypeFunction *)sd->postblit->type)->isnothrow) - ; - else - { - if (mustNotThrow) - ae->error("'%s' is not nothrow", sd->postblit->toPrettyChars()); - stop = true; - } - } - - void visit(NewAnonClassExp *) - { - assert(0); // should have been lowered by semantic() - } - }; - - CanThrow ct(func, mustNotThrow); - return walkPostorder(e, &ct); -} - -/************************************** - * Does symbol, when initialized, throw? - * Mirrors logic in Dsymbol_toElem(). - */ - -bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow) -{ - AttribDeclaration *ad; - VarDeclaration *vd; - TemplateMixin *tm; - TupleDeclaration *td; - - //printf("Dsymbol_toElem() %s\n", s->toChars()); - ad = s->isAttribDeclaration(); - if (ad) - { - Dsymbols *decl = ad->include(NULL, NULL); - if (decl && decl->dim) - { - for (size_t i = 0; i < decl->dim; i++) - { - s = (*decl)[i]; - if (Dsymbol_canThrow(s, func, mustNotThrow)) - return true; - } - } - } - else if ((vd = s->isVarDeclaration()) != NULL) - { - s = s->toAlias(); - if (s != vd) - return Dsymbol_canThrow(s, func, mustNotThrow); - if (vd->storage_class & STCmanifest) - ; - else if (vd->isStatic() || vd->storage_class & (STCextern | STCtls | STCgshared)) - ; - else - { - if (vd->init) - { - ExpInitializer *ie = vd->init->isExpInitializer(); - if (ie && canThrow(ie->exp, func, mustNotThrow)) - return true; - } - if (vd->edtor && !vd->noscope) - return canThrow(vd->edtor, func, mustNotThrow); - } - } - else if ((tm = s->isTemplateMixin()) != NULL) - { - //printf("%s\n", tm->toChars()); - if (tm->members) - { - for (size_t i = 0; i < tm->members->dim; i++) - { - Dsymbol *sm = (*tm->members)[i]; - if (Dsymbol_canThrow(sm, func, mustNotThrow)) - return true; - } - } - } - else if ((td = s->isTupleDeclaration()) != NULL) - { - for (size_t i = 0; i < td->objects->dim; i++) - { - RootObject *o = (*td->objects)[i]; - if (o->dyncast() == DYNCAST_EXPRESSION) - { - Expression *eo = (Expression *)o; - if (eo->op == TOKdsymbol) - { - DsymbolExp *se = (DsymbolExp *)eo; - if (Dsymbol_canThrow(se->s, func, mustNotThrow)) - return true; - } - } - } - } - return false; -} diff --git a/src/canthrow.d b/src/canthrow.d new file mode 100644 index 000000000000..16fc1e623639 --- /dev/null +++ b/src/canthrow.d @@ -0,0 +1,221 @@ +// 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.canthrow; + +import ddmd.apply, ddmd.arraytypes, ddmd.attrib, ddmd.declaration, ddmd.dstruct, ddmd.dsymbol, ddmd.dtemplate, ddmd.expression, ddmd.func, ddmd.globals, ddmd.init, ddmd.mtype, ddmd.root.rootobject, ddmd.tokens, ddmd.visitor; + +/******************************************** + * Returns true if the expression may throw exceptions. + * If 'mustNotThrow' is true, generate an error if it throws + */ +extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow) +{ + //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars()); + // stop walking if we determine this expression can throw + extern (C++) final class CanThrow : StoppableVisitor + { + alias visit = super.visit; + FuncDeclaration func; + bool mustNotThrow; + + public: + extern (D) this(FuncDeclaration func, bool mustNotThrow) + { + this.func = func; + this.mustNotThrow = mustNotThrow; + } + + void visit(Expression) + { + } + + void visit(DeclarationExp de) + { + stop = Dsymbol_canThrow(de.declaration, func, mustNotThrow); + } + + void visit(CallExp ce) + { + if (global.errors && !ce.e1.type) + return; // error recovery + /* If calling a function or delegate that is typed as nothrow, + * then this expression cannot throw. + * Note that pure functions can throw. + */ + Type t = ce.e1.type.toBasetype(); + if (ce.f && ce.f == func) + { + } + else if (t.ty == Tfunction && (cast(TypeFunction)t).isnothrow) + { + } + else if (t.ty == Tdelegate && (cast(TypeFunction)(cast(TypeDelegate)t).next).isnothrow) + { + } + else + { + if (mustNotThrow) + { + 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(); + ce.error("'%s' is not nothrow", s); + } + stop = true; + } + } + + void visit(NewExp ne) + { + if (ne.member) + { + // See if constructor call can throw + Type t = ne.member.type.toBasetype(); + if (t.ty == Tfunction && !(cast(TypeFunction)t).isnothrow) + { + if (mustNotThrow) + ne.error("constructor %s is not nothrow", ne.member.toChars()); + stop = true; + } + } + // regard storage allocation failures as not recoverable + } + + void visit(AssignExp ae) + { + // blit-init cannot throw + if (ae.op == TOKblit) + return; + /* Element-wise assignment could invoke postblits. + */ + Type t; + if (ae.type.toBasetype().ty == Tsarray) + { + if (!ae.e2.isLvalue()) + return; + t = ae.type; + } + else if (ae.e1.op == TOKslice) + t = (cast(SliceExp)ae.e1).e1.type; + else + return; + Type tv = t.baseElemOf(); + if (tv.ty != Tstruct) + return; + StructDeclaration sd = (cast(TypeStruct)tv).sym; + if (!sd.postblit || sd.postblit.type.ty != Tfunction) + return; + if ((cast(TypeFunction)sd.postblit.type).isnothrow) + { + } + else + { + if (mustNotThrow) + ae.error("'%s' is not nothrow", sd.postblit.toPrettyChars()); + stop = true; + } + } + + void visit(NewAnonClassExp) + { + assert(0); // should have been lowered by semantic() + } + } + + scope CanThrow ct = new CanThrow(func, mustNotThrow); + return walkPostorder(e, ct); +} + +/************************************** + * Does symbol, when initialized, throw? + * Mirrors logic in Dsymbol_toElem(). + */ +extern (C++) bool Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow) +{ + AttribDeclaration ad; + VarDeclaration vd; + TemplateMixin tm; + TupleDeclaration td; + //printf("Dsymbol_toElem() %s\n", s->toChars()); + ad = s.isAttribDeclaration(); + if (ad) + { + Dsymbols* decl = ad.include(null, null); + if (decl && decl.dim) + { + for (size_t i = 0; i < decl.dim; i++) + { + s = (*decl)[i]; + if (Dsymbol_canThrow(s, func, mustNotThrow)) + return true; + } + } + } + else if ((vd = s.isVarDeclaration()) !is null) + { + s = s.toAlias(); + if (s != vd) + return Dsymbol_canThrow(s, func, mustNotThrow); + if (vd.storage_class & STCmanifest) + { + } + else if (vd.isStatic() || vd.storage_class & (STCextern | STCtls | STCgshared)) + { + } + else + { + if (vd._init) + { + ExpInitializer ie = vd._init.isExpInitializer(); + if (ie && canThrow(ie.exp, func, mustNotThrow)) + return true; + } + if (vd.edtor && !vd.noscope) + return canThrow(vd.edtor, func, mustNotThrow); + } + } + else if ((tm = s.isTemplateMixin()) !is null) + { + //printf("%s\n", tm->toChars()); + if (tm.members) + { + for (size_t i = 0; i < tm.members.dim; i++) + { + Dsymbol sm = (*tm.members)[i]; + if (Dsymbol_canThrow(sm, func, mustNotThrow)) + return true; + } + } + } + else if ((td = s.isTupleDeclaration()) !is null) + { + for (size_t i = 0; i < td.objects.dim; i++) + { + RootObject o = (*td.objects)[i]; + if (o.dyncast() == DYNCAST_EXPRESSION) + { + Expression eo = cast(Expression)o; + if (eo.op == TOKdsymbol) + { + DsymbolExp se = cast(DsymbolExp)eo; + if (Dsymbol_canThrow(se.s, func, mustNotThrow)) + return true; + } + } + } + } + return false; +} diff --git a/src/cast.c b/src/cast.c deleted file mode 100644 index c6d1f78b1b3a..000000000000 --- a/src/cast.c +++ /dev/null @@ -1,3746 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/cast.c - */ - -#include -#include -#include // mem{set|cpy}() - -#include "rmem.h" - -#include "expression.h" -#include "mtype.h" -#include "utf.h" -#include "declaration.h" -#include "aggregate.h" -#include "template.h" -#include "scope.h" -#include "id.h" -#include "init.h" -#include "tokens.h" - -bool isCommutative(TOK op); - -/* ==================== implicitCast ====================== */ - -/************************************** - * Do an implicit cast. - * Issue error if it can't be done. - */ - - -Expression *implicitCastTo(Expression *e, Scope *sc, Type *t) -{ - class ImplicitCastTo : public Visitor - { - public: - Type *t; - Scope *sc; - Expression *result; - - ImplicitCastTo(Scope *sc, Type *t) - : sc(sc), t(t) - { - result = NULL; - } - - void visit(Expression *e) - { - //printf("Expression::implicitCastTo(%s of type %s) => %s\n", e->toChars(), e->type->toChars(), t->toChars()); - - MATCH match = e->implicitConvTo(t); - if (match) - { - if (match == MATCHconst && - (e->type->constConv(t) || - !e->isLvalue() && e->type->equivalent(t))) - { - /* Do not emit CastExp for const conversions and - * unique conversions on rvalue. - */ - result = e->copy(); - result->type = t; - return; - } - result = e->castTo(sc, t); - return; - } - - result = e->optimize(WANTvalue); - if (result != e) - { - result->accept(this); - return; - } - - if (t->ty != Terror && e->type->ty != Terror) - { - if (!t->deco) - { - /* Can happen with: - * enum E { One } - * class A - * { static void fork(EDG dg) { dg(E.One); } - * alias void delegate(E) EDG; - * } - * Should eventually make it work. - */ - e->error("forward reference to type %s", t->toChars()); - } - else if (Type *tx = reliesOnTident(t)) - e->error("forward reference to type %s", tx->toChars()); - - //printf("type %p ty %d deco %p\n", type, type->ty, type->deco); - //type = type->semantic(loc, sc); - //printf("type %s t %s\n", type->deco, t->deco); - e->error("cannot implicitly convert expression (%s) of type %s to %s", - e->toChars(), e->type->toChars(), t->toChars()); - } - result = new ErrorExp(); - } - - void visit(StringExp *e) - { - //printf("StringExp::implicitCastTo(%s of type %s) => %s\n", e->toChars(), e->type->toChars(), t->toChars()); - visit((Expression *)e); - if (result->op == TOKstring) - { - // Retain polysemous nature if it started out that way - ((StringExp *)result)->committed = e->committed; - } - } - - void visit(ErrorExp *e) - { - result = e; - } - - void visit(FuncExp *e) - { - //printf("FuncExp::implicitCastTo type = %p %s, t = %s\n", e->type, e->type ? e->type->toChars() : NULL, t->toChars()); - FuncExp *fe; - if (e->matchType(t, sc, &fe) > MATCHnomatch) - { - result = fe; - return; - } - visit((Expression *)e); - } - - void visit(ArrayLiteralExp *e) - { - visit((Expression *)e); - - Type *tb = result->type->toBasetype(); - if (tb->ty == Tarray) - semanticTypeInfo(sc, ((TypeDArray *)tb)->next); - } - - void visit(SliceExp *e) - { - visit((Expression *)e); - if (result->op != TOKslice) - return; - - e = (SliceExp *)result; - if (e->e1->op == TOKarrayliteral) - { - ArrayLiteralExp *ale = (ArrayLiteralExp *)e->e1; - Type *tb = t->toBasetype(); - Type *tx; - if (tb->ty == Tsarray) - tx = tb->nextOf()->sarrayOf(ale->elements ? ale->elements->dim : 0); - else - tx = tb->nextOf()->arrayOf(); - e->e1 = ale->implicitCastTo(sc, tx); - } - } - }; - - ImplicitCastTo v(sc, t); - e->accept(&v); - return v.result; -} - -/******************************************* - * Return MATCH level of implicitly converting e to type t. - * Don't do the actual cast; don't change e. - */ - -MATCH implicitConvTo(Expression *e, Type *t) -{ - class ImplicitConvTo : public Visitor - { - public: - Type *t; - MATCH result; - - ImplicitConvTo(Type *t) - : t(t) - { - result = MATCHnomatch; - } - - void visit(Expression *e) - { - #if 0 - printf("Expression::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - //static int nest; if (++nest == 10) halt(); - if (t == Type::terror) - return; - if (!e->type) - { - e->error("%s is not an expression", e->toChars()); - e->type = Type::terror; - } - Expression *ex = e->optimize(WANTvalue); - if (ex->type->equals(t)) - { - result = MATCHexact; - return; - } - if (ex != e) - { - //printf("\toptimized to %s of type %s\n", e->toChars(), e->type->toChars()); - result = ex->implicitConvTo(t); - return; - } - MATCH match = e->type->implicitConvTo(t); - if (match != MATCHnomatch) - { - result = match; - return; - } - - /* See if we can do integral narrowing conversions - */ - if (e->type->isintegral() && t->isintegral() && - e->type->isTypeBasic() && t->isTypeBasic()) - { - IntRange src = getIntRange(e); - IntRange target = IntRange::fromType(t); - if (target.contains(src)) - { - result = MATCHconvert; - return; - } - } - } - - /****** - * Given expression e of type t, see if we can implicitly convert e - * to type tprime, where tprime is type t with mod bits added. - * Returns: - * match level - */ - static MATCH implicitMod(Expression *e, Type *t, MOD mod) - { - Type *tprime; - if (t->ty == Tpointer) - tprime = t->nextOf()->castMod(mod)->pointerTo(); - else if (t->ty == Tarray) - tprime = t->nextOf()->castMod(mod)->arrayOf(); - else if (t->ty == Tsarray) - tprime = t->nextOf()->castMod(mod)->sarrayOf(t->size() / t->nextOf()->size()); - else - tprime = t->castMod(mod); - - return e->implicitConvTo(tprime); - } - - static MATCH implicitConvToAddMin(BinExp *e, Type *t) - { - /* Is this (ptr +- offset)? If so, then ask ptr - * if the conversion can be done. - * This is to support doing things like implicitly converting a mutable unique - * pointer to an immutable pointer. - */ - - Type *typeb = e->type->toBasetype(); - Type *tb = t->toBasetype(); - if (typeb->ty != Tpointer || tb->ty != Tpointer) - return MATCHnomatch; - - Type *t1b = e->e1->type->toBasetype(); - Type *t2b = e->e2->type->toBasetype(); - if (t1b->ty == Tpointer && t2b->isintegral() && - t1b->equivalent(tb)) - { - // ptr + offset - // ptr - offset - MATCH m = e->e1->implicitConvTo(t); - return (m > MATCHconst) ? MATCHconst : m; - } - if (t2b->ty == Tpointer && t1b->isintegral() && - t2b->equivalent(tb)) - { - // offset + ptr - MATCH m = e->e2->implicitConvTo(t); - return (m > MATCHconst) ? MATCHconst : m; - } - - return MATCHnomatch; - } - - void visit(AddExp *e) - { - #if 0 - printf("AddExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - visit((Expression *)e); - if (result == MATCHnomatch) - result = implicitConvToAddMin(e, t); - } - - void visit(MinExp *e) - { - #if 0 - printf("MinExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - visit((Expression *)e); - if (result == MATCHnomatch) - result = implicitConvToAddMin(e, t); - } - - void visit(IntegerExp *e) - { - #if 0 - printf("IntegerExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - MATCH m = e->type->implicitConvTo(t); - if (m >= MATCHconst) - { - result = m; - return; - } - - TY ty = e->type->toBasetype()->ty; - TY toty = t->toBasetype()->ty; - TY oldty = ty; - - if (m == MATCHnomatch && t->ty == Tenum) - return; - - if (t->ty == Tvector) - { - TypeVector *tv = (TypeVector *)t; - TypeBasic *tb = tv->elementType(); - if (tb->ty == Tvoid) - return; - toty = tb->ty; - } - - switch (ty) - { - case Tbool: - case Tint8: - case Tchar: - case Tuns8: - case Tint16: - case Tuns16: - case Twchar: - ty = Tint32; - break; - - case Tdchar: - ty = Tuns32; - break; - - default: - break; - } - - // Only allow conversion if no change in value - dinteger_t value = e->toInteger(); - switch (toty) - { - case Tbool: - if ((value & 1) != value) - return; - break; - - case Tint8: - if (ty == Tuns64 && value & ~0x7FUL) - return; - else if ((signed char)value != value) - return; - break; - - case Tchar: - if ((oldty == Twchar || oldty == Tdchar) && value > 0x7F) - return; - case Tuns8: - //printf("value = %llu %llu\n", (dinteger_t)(unsigned char)value, value); - if ((unsigned char)value != value) - return; - break; - - case Tint16: - if (ty == Tuns64 && value & ~0x7FFFUL) - return; - else if ((short)value != value) - return; - break; - - case Twchar: - if (oldty == Tdchar && value > 0xD7FF && value < 0xE000) - return; - case Tuns16: - if ((unsigned short)value != value) - return; - break; - - case Tint32: - if (ty == Tuns32) - { - } - else if (ty == Tuns64 && value & ~0x7FFFFFFFUL) - return; - else if ((int)value != value) - return; - break; - - case Tuns32: - if (ty == Tint32) - { - } - else if ((unsigned)value != value) - return; - break; - - case Tdchar: - if (value > 0x10FFFFUL) - return; - break; - - case Tfloat32: - { - volatile float f; - if (e->type->isunsigned()) - { - f = (float)value; - if (f != value) - return; - } - else - { - f = (float)(sinteger_t)value; - if (f != (sinteger_t)value) - return; - } - break; - } - - case Tfloat64: - { - volatile double f; - if (e->type->isunsigned()) - { - f = (double)value; - if (f != value) - return; - } - else - { - f = (double)(sinteger_t)value; - if (f != (sinteger_t)value) - return; - } - break; - } - - case Tfloat80: - { - volatile_longdouble f; - if (e->type->isunsigned()) - { - f = ldouble(value); - if (f != value) // isn't this a noop, because the compiler prefers ld - return; - } - else - { - f = ldouble((sinteger_t)value); - if (f != (sinteger_t)value) - return; - } - break; - } - - case Tpointer: - //printf("type = %s\n", type->toBasetype()->toChars()); - //printf("t = %s\n", t->toBasetype()->toChars()); - if (ty == Tpointer && - e->type->toBasetype()->nextOf()->ty == t->toBasetype()->nextOf()->ty) - { - /* Allow things like: - * const char* P = cast(char *)3; - * char* q = P; - */ - break; - } - - default: - visit((Expression *)e); - return; - } - - //printf("MATCHconvert\n"); - result = MATCHconvert; - } - - void visit(ErrorExp *e) - { - // no match - } - - void visit(NullExp *e) - { - #if 0 - printf("NullExp::implicitConvTo(this=%s, type=%s, t=%s, committed = %d)\n", - e->toChars(), e->type->toChars(), t->toChars(), e->committed); - #endif - if (e->type->equals(t)) - { - result = MATCHexact; - return; - } - - /* Allow implicit conversions from immutable to mutable|const, - * and mutable to immutable. It works because, after all, a null - * doesn't actually point to anything. - */ - if (t->equivalent(e->type)) - { - result = MATCHconst; - return; - } - - visit((Expression *)e); - } - - void visit(StructLiteralExp *e) - { - #if 0 - printf("StructLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - visit((Expression *)e); - if (result != MATCHnomatch) - return; - if (e->type->ty == t->ty && e->type->ty == Tstruct && - ((TypeStruct *)e->type)->sym == ((TypeStruct *)t)->sym) - { - result = MATCHconst; - for (size_t i = 0; i < e->elements->dim; i++) - { - Expression *el = (*e->elements)[i]; - if (!el) - continue; - Type *te = el->type; - te = e->sd->fields[i]->type->addMod(t->mod); - MATCH m2 = el->implicitConvTo(te); - //printf("\t%s => %s, match = %d\n", el->toChars(), te->toChars(), m2); - if (m2 < result) - result = m2; - } - } - } - - void visit(StringExp *e) - { - #if 0 - printf("StringExp::implicitConvTo(this=%s, committed=%d, type=%s, t=%s)\n", - e->toChars(), e->committed, e->type->toChars(), t->toChars()); - #endif - if (!e->committed && t->ty == Tpointer && t->nextOf()->ty == Tvoid) - return; - - if (e->type->ty == Tsarray || e->type->ty == Tarray || e->type->ty == Tpointer) - { - TY tyn = e->type->nextOf()->ty; - if (tyn == Tchar || tyn == Twchar || tyn == Tdchar) - { - switch (t->ty) - { - case Tsarray: - if (e->type->ty == Tsarray) - { - TY tynto = t->nextOf()->ty; - if (tynto == tyn) - { - if (((TypeSArray *)e->type)->dim->toInteger() == - ((TypeSArray *)t)->dim->toInteger()) - { - result = MATCHexact; - } - return; - } - int szto = (int)t->nextOf()->size(); - if (tynto == Tchar || tynto == Twchar || tynto == Tdchar) - { - if (e->committed && tynto != tyn) - return; - size_t fromlen = e->length(szto); - size_t tolen = (size_t)((TypeSArray *)t)->dim->toInteger(); - if (tolen < fromlen) - return; - if (tolen != fromlen) - { - // implicit length extending - result = MATCHconvert; - return; - } - } - if (!e->committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar)) - { - result = MATCHexact; - return; - } - } - else if (e->type->ty == Tarray) - { - TY tynto = t->nextOf()->ty; - int sznto = (int)t->nextOf()->size(); - if (tynto == Tchar || tynto == Twchar || tynto == Tdchar) - { - if (e->committed && tynto != tyn) - return; - size_t fromlen = e->length(sznto); - size_t tolen = (size_t)((TypeSArray *)t)->dim->toInteger(); - if (tolen < fromlen) - return; - if (tolen != fromlen) - { - // implicit length extending - result = MATCHconvert; - return; - } - } - if (tynto == tyn) - { - result = MATCHexact; - return; - } - if (!e->committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar)) - { - result = MATCHexact; - return; - } - } - /* fall through */ - case Tarray: - case Tpointer: - Type *tn = t->nextOf(); - MATCH m = MATCHexact; - if (e->type->nextOf()->mod != tn->mod) - { - if (!tn->isConst()) - return; - m = MATCHconst; - } - if (!e->committed) - { - switch (tn->ty) - { - case Tchar: - if (e->postfix == 'w' || e->postfix == 'd') - m = MATCHconvert; - result = m; - return; - case Twchar: - if (e->postfix != 'w') - m = MATCHconvert; - result = m; - return; - case Tdchar: - if (e->postfix != 'd') - m = MATCHconvert; - result = m; - return; - } - } - break; - } - } - } - - visit((Expression *)e); - } - - void visit(ArrayLiteralExp *e) - { - #if 0 - printf("ArrayLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - Type *typeb = e->type->toBasetype(); - Type *tb = t->toBasetype(); - if ((tb->ty == Tarray || tb->ty == Tsarray) && - (typeb->ty == Tarray || typeb->ty == Tsarray)) - { - result = MATCHexact; - Type *typen = typeb->nextOf()->toBasetype(); - - if (tb->ty == Tsarray) - { - TypeSArray *tsa = (TypeSArray *)tb; - if (e->elements->dim != tsa->dim->toInteger()) - result = MATCHnomatch; - } - - Type *telement = tb->nextOf(); - if (!e->elements->dim) - { - if (typen->ty != Tvoid) - result = typen->implicitConvTo(telement); - } - else - { - for (size_t i = 0; i < e->elements->dim; i++) - { - Expression *el = (*e->elements)[i]; - if (result == MATCHnomatch) - break; // no need to check for worse - MATCH m = el->implicitConvTo(telement); - if (m < result) - result = m; // remember worst match - } - } - - if (!result) - result = e->type->implicitConvTo(t); - - return; - } - else if (tb->ty == Tvector && - (typeb->ty == Tarray || typeb->ty == Tsarray)) - { - result = MATCHexact; - // Convert array literal to vector type - TypeVector *tv = (TypeVector *)tb; - TypeSArray *tbase = (TypeSArray *)tv->basetype; - assert(tbase->ty == Tsarray); - if (e->elements->dim != tbase->dim->toInteger()) - { - result = MATCHnomatch; - return; - } - - Type *telement = tv->elementType(); - for (size_t i = 0; i < e->elements->dim; i++) - { - Expression *el = (*e->elements)[i]; - MATCH m = el->implicitConvTo(telement); - if (m < result) - result = m; // remember worst match - if (result == MATCHnomatch) - break; // no need to check for worse - } - return; - } - - visit((Expression *)e); - } - - void visit(AssocArrayLiteralExp *e) - { - Type *typeb = e->type->toBasetype(); - Type *tb = t->toBasetype(); - if (tb->ty == Taarray && typeb->ty == Taarray) - { - result = MATCHexact; - for (size_t i = 0; i < e->keys->dim; i++) - { - Expression *el = (*e->keys)[i]; - MATCH m = el->implicitConvTo(((TypeAArray *)tb)->index); - if (m < result) - result = m; // remember worst match - if (result == MATCHnomatch) - break; // no need to check for worse - el = (*e->values)[i]; - m = el->implicitConvTo(tb->nextOf()); - if (m < result) - result = m; // remember worst match - if (result == MATCHnomatch) - break; // no need to check for worse - } - return; - } - else - visit((Expression *)e); - } - - void visit(CallExp *e) - { -#define LOG 0 - #if LOG - printf("CallExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - - visit((Expression *)e); - if (result != MATCHnomatch) - return; - - /* Allow the result of strongly pure functions to - * convert to immutable - */ - if (e->f && e->f->isolateReturn()) - { - result = e->type->immutableOf()->implicitConvTo(t); - if (result > MATCHconst) // Match level is MATCHconst at best. - result = MATCHconst; - return; - } - - /* Conversion is 'const' conversion if: - * 1. function is pure (weakly pure is ok) - * 2. implicit conversion only fails because of mod bits - * 3. each function parameter can be implicitly converted to the mod bits - */ - Type *tx = e->f ? e->f->type : e->e1->type; - tx = tx->toBasetype(); - if (tx->ty != Tfunction) - return; - TypeFunction *tf = (TypeFunction *)tx; - - if (tf->purity == PUREimpure) - return; - if (e->f && e->f->isNested()) - return; - - /* See if fail only because of mod bits. - * - * Bugzilla 14155: All pure functions can access global immutable data. - * So the returned pointer may refer an immutable global data, - * and then the returned pointer that points non-mutable object - * cannot be unique pointer. - * - * Example: - * immutable g; - * static this() { g = 1; } - * const(int*) foo() pure { return &g; } - * void test() { - * immutable(int*) ip = foo(); // OK - * int* mp = foo(); // should be disallowed - * } - */ - if (e->type->immutableOf()->implicitConvTo(t) < MATCHconst && - e->type->addMod(MODshared)->implicitConvTo(t) < MATCHconst && - e->type->implicitConvTo(t->addMod(MODshared)) < MATCHconst) - { - return; - } - // Allow a conversion to immutable type, or - // conversions of mutable types between thread-local and shared. - - /* Get mod bits of what we're converting to - */ - Type *tb = t->toBasetype(); - MOD mod = tb->mod; - if (tf->isref) - ; - else - { - Type *ti = getIndirection(t); - if (ti) - mod = ti->mod; - } -#if LOG - printf("mod = x%x\n", mod); -#endif - if (mod & MODwild) - return; // not sure what to do with this - - /* Apply mod bits to each function parameter, - * and see if we can convert the function argument to the modded type - */ - - size_t nparams = Parameter::dim(tf->parameters); - size_t j = (tf->linkage == LINKd && tf->varargs == 1); // if TypeInfoArray was prepended - if (e->e1->op == TOKdotvar) - { - /* Treat 'this' as just another function argument - */ - DotVarExp *dve = (DotVarExp *)e->e1; - Type *targ = dve->e1->type; - if (targ->constConv(targ->castMod(mod)) == MATCHnomatch) - return; - } - for (size_t i = j; i < e->arguments->dim; ++i) - { - Expression *earg = (*e->arguments)[i]; - Type *targ = earg->type->toBasetype(); -#if LOG - printf("[%d] earg: %s, targ: %s\n", (int)i, earg->toChars(), targ->toChars()); -#endif - if (i - j < nparams) - { - Parameter *fparam = Parameter::getNth(tf->parameters, i - j); - if (fparam->storageClass & STClazy) - return; // not sure what to do with this - Type *tparam = fparam->type; - if (!tparam) - continue; - if (fparam->storageClass & (STCout | STCref)) - { - if (targ->constConv(tparam->castMod(mod)) == MATCHnomatch) - return; - continue; - } - } - -#if LOG - printf("[%d] earg: %s, targm: %s\n", (int)i, earg->toChars(), targ->addMod(mod)->toChars()); -#endif - if (implicitMod(earg, targ, mod) == MATCHnomatch) - return; - } - - /* Success - */ - result = MATCHconst; -#undef LOG - } - - void visit(AddrExp *e) - { - #if 0 - printf("AddrExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - result = e->type->implicitConvTo(t); - //printf("\tresult = %d\n", result); - - if (result != MATCHnomatch) - return; - - // Look for pointers to functions where the functions are overloaded. - - t = t->toBasetype(); - - if (e->e1->op == TOKoverloadset && - (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) - { - OverExp *eo = (OverExp *)e->e1; - FuncDeclaration *f = NULL; - for (size_t i = 0; i < eo->vars->a.dim; i++) - { - Dsymbol *s = eo->vars->a[i]; - FuncDeclaration *f2 = s->isFuncDeclaration(); - assert(f2); - if (f2->overloadExactMatch(t->nextOf())) - { - if (f) - { - /* Error if match in more than one overload set, - * even if one is a 'better' match than the other. - */ - ScopeDsymbol::multiplyDefined(e->loc, f, f2); - } - else - f = f2; - result = MATCHexact; - } - } - } - - if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction && - t->ty == Tpointer && t->nextOf()->ty == Tfunction && - e->e1->op == TOKvar) - { - /* I don't think this can ever happen - - * it should have been - * converted to a SymOffExp. - */ - assert(0); - } - - //printf("\tresult = %d\n", result); - } - - void visit(SymOffExp *e) - { - #if 0 - printf("SymOffExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - result = e->type->implicitConvTo(t); - //printf("\tresult = %d\n", result); - if (result != MATCHnomatch) - return; - - // Look for pointers to functions where the functions are overloaded. - t = t->toBasetype(); - if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction && - (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) - { - if (FuncDeclaration *f = e->var->isFuncDeclaration()) - { - f = f->overloadExactMatch(t->nextOf()); - if (f) - { - if ((t->ty == Tdelegate && (f->needThis() || f->isNested())) || - (t->ty == Tpointer && !(f->needThis() || f->isNested()))) - { - result = MATCHexact; - } - } - } - } - //printf("\tresult = %d\n", result); - } - - void visit(DelegateExp *e) - { - #if 0 - printf("DelegateExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - result = e->type->implicitConvTo(t); - if (result != MATCHnomatch) - return; - - // Look for pointers to functions where the functions are overloaded. - t = t->toBasetype(); - if (e->type->ty == Tdelegate && - t->ty == Tdelegate) - { - if (e->func && e->func->overloadExactMatch(t->nextOf())) - result = MATCHexact; - } - } - - void visit(FuncExp *e) - { - //printf("FuncExp::implicitConvTo type = %p %s, t = %s\n", e->type, e->type ? e->type->toChars() : NULL, t->toChars()); - MATCH m = e->matchType(t, NULL, NULL, 1); - if (m > MATCHnomatch) - { - result = m; - return; - } - visit((Expression *)e); - } - - void visit(OrExp *e) - { - visit((Expression *)e); - if (result != MATCHnomatch) - return; - - MATCH m1 = e->e1->implicitConvTo(t); - MATCH m2 = e->e2->implicitConvTo(t); - - // Pick the worst match - result = (m1 < m2) ? m1 : m2; - } - - void visit(XorExp *e) - { - visit((Expression *)e); - if (result != MATCHnomatch) - return; - - MATCH m1 = e->e1->implicitConvTo(t); - MATCH m2 = e->e2->implicitConvTo(t); - - // Pick the worst match - result = (m1 < m2) ? m1 : m2; - } - - void visit(CondExp *e) - { - MATCH m1 = e->e1->implicitConvTo(t); - MATCH m2 = e->e2->implicitConvTo(t); - //printf("CondExp: m1 %d m2 %d\n", m1, m2); - - // Pick the worst match - result = (m1 < m2) ? m1 : m2; - } - - void visit(CommaExp *e) - { - e->e2->accept(this); - } - - void visit(CastExp *e) - { - #if 0 - printf("CastExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - result = e->type->implicitConvTo(t); - if (result != MATCHnomatch) - return; - - if (t->isintegral() && - e->e1->type->isintegral() && - e->e1->implicitConvTo(t) != MATCHnomatch) - result = MATCHconvert; - else - visit((Expression *)e); - } - - void visit(NewExp *e) - { - #if 0 - printf("NewExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - visit((Expression *)e); - if (result != MATCHnomatch) - return; - - /* Calling new() is like calling a pure function. We can implicitly convert the - * return from new() to t using the same algorithm as in CallExp, with the function - * 'arguments' being: - * thisexp - * newargs - * arguments - * .init - * 'member' and 'allocator' need to be pure. - */ - - /* See if fail only because of mod bits - */ - if (e->type->immutableOf()->implicitConvTo(t->immutableOf()) == MATCHnomatch) - return; - - /* Get mod bits of what we're converting to - */ - Type *tb = t->toBasetype(); - MOD mod = tb->mod; - if (Type *ti = getIndirection(t)) - mod = ti->mod; -#if LOG - printf("mod = x%x\n", mod); -#endif - if (mod & MODwild) - return; // not sure what to do with this - - /* Apply mod bits to each argument, - * and see if we can convert the argument to the modded type - */ - - if (e->thisexp) - { - /* Treat 'this' as just another function argument - */ - Type *targ = e->thisexp->type; - if (targ->constConv(targ->castMod(mod)) == MATCHnomatch) - return; - } - - /* Check call to 'allocator', then 'member' - */ - FuncDeclaration *fd = e->allocator; - for (int count = 0; count < 2; ++count, (fd = e->member)) - { - if (!fd) - continue; - if (fd->errors || fd->type->ty != Tfunction) - return; // error - TypeFunction *tf = (TypeFunction *)fd->type; - if (tf->purity == PUREimpure) - return; // impure - - if (fd == e->member) - { - if (e->type->immutableOf()->implicitConvTo(t) < MATCHconst && - e->type->addMod(MODshared)->implicitConvTo(t) < MATCHconst && - e->type->implicitConvTo(t->addMod(MODshared)) < MATCHconst) - { - return; - } - // Allow a conversion to immutable type, or - // conversions of mutable types between thread-local and shared. - } - - Expressions *args = (fd == e->allocator) ? e->newargs : e->arguments; - - size_t nparams = Parameter::dim(tf->parameters); - size_t j = (tf->linkage == LINKd && tf->varargs == 1); // if TypeInfoArray was prepended - for (size_t i = j; i < e->arguments->dim; ++i) - { - Expression *earg = (*args)[i]; - Type *targ = earg->type->toBasetype(); -#if LOG - printf("[%d] earg: %s, targ: %s\n", (int)i, earg->toChars(), targ->toChars()); -#endif - if (i - j < nparams) - { - Parameter *fparam = Parameter::getNth(tf->parameters, i - j); - if (fparam->storageClass & STClazy) - return; // not sure what to do with this - Type *tparam = fparam->type; - if (!tparam) - continue; - if (fparam->storageClass & (STCout | STCref)) - { - if (targ->constConv(tparam->castMod(mod)) == MATCHnomatch) - return; - continue; - } - } - -#if LOG - printf("[%d] earg: %s, targm: %s\n", (int)i, earg->toChars(), targ->addMod(mod)->toChars()); -#endif - if (implicitMod(earg, targ, mod) == MATCHnomatch) - return; - } - } - - /* If no 'member', then construction is by simple assignment, - * and just straight check 'arguments' - */ - if (!e->member && e->arguments) - { - for (size_t i = 0; i < e->arguments->dim; ++i) - { - Expression *earg = (*e->arguments)[i]; - if (!earg) // Bugzilla 14853: if it's on overlapped field - continue; - Type *targ = earg->type->toBasetype(); -#if LOG - printf("[%d] earg: %s, targ: %s\n", (int)i, earg->toChars(), targ->toChars()); - printf("[%d] earg: %s, targm: %s\n", (int)i, earg->toChars(), targ->addMod(mod)->toChars()); -#endif - if (implicitMod(earg, targ, mod) == MATCHnomatch) - return; - } - } - - /* Consider the .init expression as an argument - */ - Type *ntb = e->newtype->toBasetype(); - if (ntb->ty == Tarray) - ntb = ntb->nextOf()->toBasetype(); - if (ntb->ty == Tstruct) - { - // Don't allow nested structs - uplevel reference may not be convertible - StructDeclaration *sd = ((TypeStruct *)ntb)->sym; - sd->size(e->loc); // resolve any forward references - if (sd->isNested()) - return; - } - if (ntb->isZeroInit(e->loc)) - { - /* Zeros are implicitly convertible, except for special cases. - */ - if (ntb->ty == Tclass) - { - /* With new() must look at the class instance initializer. - */ - ClassDeclaration *cd = ((TypeClass *)ntb)->sym; - - cd->size(e->loc); // resolve any forward references - - if (cd->isNested()) - return; // uplevel reference may not be convertible - - assert(!cd->isInterfaceDeclaration()); - - struct ClassCheck - { - static bool convertible(Loc loc, ClassDeclaration *cd, MOD mod) - { - for (size_t i = 0; i < cd->fields.dim; i++) - { - VarDeclaration *v = cd->fields[i]; - Initializer *init = v->init; - if (init) - { - if (init->isVoidInitializer()) - ; - else if (ExpInitializer *ei = init->isExpInitializer()) - { - Type *tb = v->type->toBasetype(); - if (implicitMod(ei->exp, tb, mod) == MATCHnomatch) - return false; - } - else - { - /* Enhancement: handle StructInitializer and ArrayInitializer - */ - return false; - } - } - else if (!v->type->isZeroInit(loc)) - return false; - } - return cd->baseClass ? convertible(loc, cd->baseClass, mod) : true; - } - }; - - if (!ClassCheck::convertible(e->loc, cd, mod)) - return; - } - } - else - { - Expression *earg = e->newtype->defaultInitLiteral(e->loc); - Type *targ = e->newtype->toBasetype(); - - if (implicitMod(earg, targ, mod) == MATCHnomatch) - return; - } - - /* Success - */ - result = MATCHconst; - } - - void visit(SliceExp *e) - { - //printf("SliceExp::implicitConvTo e = %s, type = %s\n", e->toChars(), e->type->toChars()); - visit((Expression *)e); - if (result != MATCHnomatch) - return; - - Type *tb = t->toBasetype(); - Type *typeb = e->type->toBasetype(); - if (tb->ty == Tsarray && typeb->ty == Tarray) - { - typeb = toStaticArrayType(e); - if (typeb) - result = typeb->implicitConvTo(t); - return; - } - - /* If the only reason it won't convert is because of the mod bits, - * then test for conversion by seeing if e1 can be converted with those - * same mod bits. - */ - Type *t1b = e->e1->type->toBasetype(); - if (tb->ty == Tarray && typeb->equivalent(tb)) - { - Type *tbn = tb->nextOf(); - Type *tx = NULL; - - /* If e->e1 is dynamic array or pointer, the uniqueness of e->e1 - * is equivalent with the uniqueness of the referred data. And in here - * we can have arbitrary typed reference for that. - */ - if (t1b->ty == Tarray) - tx = tbn->arrayOf(); - if (t1b->ty == Tpointer) - tx = tbn->pointerTo(); - - /* If e->e1 is static array, at least it should be an rvalue. - * If not, e->e1 is a reference, and its uniqueness does not link - * to the uniqueness of the referred data. - */ - if (t1b->ty == Tsarray && !e->e1->isLvalue()) - tx = tbn->sarrayOf(t1b->size() / tbn->size()); - - if (tx) - { - result = e->e1->implicitConvTo(tx); - if (result > MATCHconst) // Match level is MATCHconst at best. - result = MATCHconst; - } - } - - // Enhancement 10724 - if (tb->ty == Tpointer && e->e1->op == TOKstring) - e->e1->accept(this); - } - }; - - ImplicitConvTo v(t); - e->accept(&v); - return v.result; -} - -Type *toStaticArrayType(SliceExp *e) -{ - if (e->lwr && e->upr) - { - // For the following code to work, e should be optimized beforehand. - // (eg. $ in lwr and upr should be already resolved, if possible) - Expression *lwr = e->lwr->optimize(WANTvalue); - Expression *upr = e->upr->optimize(WANTvalue); - if (lwr->isConst() && upr->isConst()) - { - size_t len = (size_t)(upr->toUInteger() - lwr->toUInteger()); - return e->type->toBasetype()->nextOf()->sarrayOf(len); - } - } - else - { - Type *t1b = e->e1->type->toBasetype(); - if (t1b->ty == Tsarray) - return t1b; - } - return NULL; -} - -/* ==================== castTo ====================== */ - -/************************************** - * Do an explicit cast. - * Assume that the 'this' expression does not have any indirections. - */ - -Expression *castTo(Expression *e, Scope *sc, Type *t) -{ - class CastTo : public Visitor - { - public: - Type *t; - Scope *sc; - Expression *result; - - CastTo(Scope *sc, Type *t) - : sc(sc), t(t) - { - result = NULL; - } - - void visit(Expression *e) - { - //printf("Expression::castTo(this=%s, t=%s)\n", e->toChars(), t->toChars()); - #if 0 - printf("Expression::castTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - if (e->type->equals(t)) - { - result = e; - return; - } - if (e->op == TOKvar) - { - VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); - if (v && v->storage_class & STCmanifest) - { - result = e->ctfeInterpret(); - result = result->castTo(sc, t); - return; - } - } - - Type *tob = t->toBasetype(); - Type *t1b = e->type->toBasetype(); - if (tob->equals(t1b)) - { - result = e->copy(); // because of COW for assignment to e->type - result->type = t; - return; - } - - /* Make semantic error against invalid cast between concrete types. - * Assume that 'e' is never be any placeholder expressions. - * The result of these checks should be consistent with CastExp::toElem(). - */ - - // Fat Value types - const bool tob_isFV = (tob->ty == Tstruct || tob->ty == Tsarray); - const bool t1b_isFV = (t1b->ty == Tstruct || t1b->ty == Tsarray); - - // Fat Reference types - const bool tob_isFR = (tob->ty == Tarray || tob->ty == Tdelegate); - const bool t1b_isFR = (t1b->ty == Tarray || t1b->ty == Tdelegate); - - // Reference types - const bool tob_isR = (tob_isFR || tob->ty == Tpointer || tob->ty == Taarray || tob->ty == Tclass); - const bool t1b_isR = (t1b_isFR || t1b->ty == Tpointer || t1b->ty == Taarray || t1b->ty == Tclass); - - // Arithmetic types (== valueable basic types) - const bool tob_isA = (tob->isintegral() || tob->isfloating()); - const bool t1b_isA = (t1b->isintegral() || t1b->isfloating()); - - if (AggregateDeclaration *t1ad = isAggregate(t1b)) - { - AggregateDeclaration *toad = isAggregate(tob); - if (t1ad != toad && t1ad->aliasthis) - { - if (t1b->ty == Tclass && tob->ty == Tclass) - { - ClassDeclaration *t1cd = t1b->isClassHandle(); - ClassDeclaration *tocd = tob->isClassHandle(); - int offset; - if (tocd->isBaseOf(t1cd, &offset)) - goto Lok; - } - - /* Forward the cast to our alias this member, rewrite to: - * cast(to)e1.aliasthis - */ - result = resolveAliasThis(sc, e); - result = result->castTo(sc, t); - return; - } - } - else if (tob->ty == Tvector && t1b->ty != Tvector) - { - //printf("test1 e = %s, e->type = %s, tob = %s\n", e->toChars(), e->type->toChars(), tob->toChars()); - TypeVector *tv = (TypeVector *)tob; - result = new CastExp(e->loc, e, tv->elementType()); - result = new VectorExp(e->loc, result, tob); - result = result->semantic(sc); - return; - } - else if (tob->ty != Tvector && t1b->ty == Tvector) - { - // T[n] <-- __vector(U[m]) - if (tob->ty == Tsarray) - { - if (t1b->size(e->loc) == tob->size(e->loc)) - goto Lok; - } - goto Lfail; - } - else if (t1b->implicitConvTo(tob) == MATCHconst && t->equals(e->type->constOf())) - { - result = e->copy(); - result->type = t; - return; - } - - // arithmetic values vs. other arithmetic values - // arithmetic values vs. T* - if (tob_isA && (t1b_isA || t1b->ty == Tpointer) || - t1b_isA && (tob_isA || tob->ty == Tpointer)) - { - goto Lok; - } - - // arithmetic values vs. references or fat values - if (tob_isA && (t1b_isR || t1b_isFV) || - t1b_isA && (tob_isR || tob_isFV)) - { - goto Lfail; - } - - // Bugzlla 3133: A cast between fat values is possible only when the sizes match. - if (tob_isFV && t1b_isFV) - { - if (t1b->size(e->loc) == tob->size(e->loc)) - goto Lok; - e->error("cannot cast expression %s of type %s to %s because of different sizes", - e->toChars(), e->type->toChars(), t->toChars()); - result = new ErrorExp(); - return; - } - - // Fat values vs. null or references - if (tob_isFV && (t1b->ty == Tnull || t1b_isR) || - t1b_isFV && (tob->ty == Tnull || tob_isR)) - { - if (tob->ty == Tpointer && t1b->ty == Tsarray) - { - // T[n] sa; - // cast(U*)sa; // ==> cast(U*)sa.ptr; - result = new AddrExp(e->loc, e); - result->type = t; - return; - } - if (tob->ty == Tarray && t1b->ty == Tsarray) - { - // T[n] sa; - // cast(U[])sa; // ==> cast(U[])sa[]; - d_uns64 fsize = t1b->nextOf()->size(); - d_uns64 tsize = tob->nextOf()->size(); - if ((((TypeSArray *)t1b)->dim->toInteger() * fsize) % tsize != 0) - { - // copied from sarray_toDarray() in e2ir.c - e->error("cannot cast expression %s of type %s to %s since sizes don't line up", - e->toChars(), e->type->toChars(), t->toChars()); - result = new ErrorExp(); - return; - } - goto Lok; - } - goto Lfail; - } - - /* For references, any reinterpret casts are allowed to same 'ty' type. - * T* to U* - * R1 function(P1) to R2 function(P2) - * R1 delegate(P1) to R2 delegate(P2) - * T[] to U[] - * V1[K1] to V2[K2] - * class/interface A to B (will be a dynamic cast if possible) - */ - if (tob->ty == t1b->ty && tob_isR && t1b_isR) - goto Lok; - - // typeof(null) <-- non-null references or values - if (tob->ty == Tnull && t1b->ty != Tnull) - goto Lfail; // Bugzilla 14629 - // typeof(null) --> non-null references or arithmetic values - if (t1b->ty == Tnull && tob->ty != Tnull) - goto Lok; - - // Check size mismatch of references. - // Tarray and Tdelegate are (void*).sizeof*2, but others have (void*).sizeof. - if (tob_isFR && t1b_isR || - t1b_isFR && tob_isR) - { - if (tob->ty == Tpointer && t1b->ty == Tarray) - { - // T[] da; - // cast(U*)da; // ==> cast(U*)da.ptr; - goto Lok; - } - if (tob->ty == Tpointer && t1b->ty == Tdelegate) - { - // void delegate() dg; - // cast(U*)dg; // ==> cast(U*)dg.ptr; - // Note that it happens even when U is a Tfunction! - e->deprecation("casting from %s to %s is deprecated", e->type->toChars(), t->toChars()); - goto Lok; - } - goto Lfail; - } - - if (t1b->ty == Tvoid && tob->ty != Tvoid) - { - Lfail: - e->error("cannot cast expression %s of type %s to %s", - e->toChars(), e->type->toChars(), t->toChars()); - result = new ErrorExp(); - return; - } - - Lok: - result = new CastExp(e->loc, e, tob); - result->type = t; // Don't call semantic() - //printf("Returning: %s\n", result->toChars()); - } - - void visit(ErrorExp *e) - { - result = e; - } - - void visit(RealExp *e) - { - if (!e->type->equals(t)) - { - if ((e->type->isreal() && t->isreal()) || - (e->type->isimaginary() && t->isimaginary()) - ) - { - result = e->copy(); - result->type = t; - } - else - visit((Expression *)e); - return; - } - result = e; - } - - void visit(ComplexExp *e) - { - if (!e->type->equals(t)) - { - if (e->type->iscomplex() && t->iscomplex()) - { - result = e->copy(); - result->type = t; - } - else - visit((Expression *)e); - return; - } - result = e; - } - - void visit(NullExp *e) - { - //printf("NullExp::castTo(t = %s) %s\n", t->toChars(), toChars()); - visit((Expression *)e); - if (result->op == TOKnull) - { - NullExp *ex = (NullExp *)result; - ex->committed = 1; - return; - } - } - - void visit(StructLiteralExp *e) - { - visit((Expression *)e); - if (result->op == TOKstructliteral) - ((StructLiteralExp *)result)->stype = t; // commit type - } - - void visit(StringExp *e) - { - /* This follows copy-on-write; any changes to 'this' - * will result in a copy. - * The this->string member is considered immutable. - */ - int copied = 0; - - //printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t->toChars(), e->toChars(), e->committed); - - if (!e->committed && t->ty == Tpointer && t->nextOf()->ty == Tvoid) - { - e->error("cannot convert string literal to void*"); - result = new ErrorExp(); - return; - } - - StringExp *se = e; - if (!e->committed) - { - se = (StringExp *)e->copy(); - se->committed = 1; - copied = 1; - } - - if (e->type->equals(t)) - { - result = se; - return; - } - - Type *tb = t->toBasetype(); - //printf("\ttype = %s\n", e->type->toChars()); - if (tb->ty == Tdelegate && e->type->toBasetype()->ty != Tdelegate) - { - visit((Expression *)e); - return; - } - - Type *typeb = e->type->toBasetype(); - if (typeb->equals(tb)) - { - if (!copied) - { - se = (StringExp *)e->copy(); - copied = 1; - } - se->type = t; - result = se; - return; - } - - /* Handle reinterpret casts: - * cast(wchar[3])"abcd"c --> [\u6261, \u6463, \u0000] - * cast(wchar[2])"abcd"c --> [\u6261, \u6463] - * cast(wchar[1])"abcd"c --> [\u6261] - */ - if (e->committed && tb->ty == Tsarray && typeb->ty == Tarray) - { - se = (StringExp *)e->copy(); - d_uns64 szx = tb->nextOf()->size(); - assert(szx <= 255); - se->sz = (unsigned char)szx; - se->len = (size_t)((TypeSArray *)tb)->dim->toInteger(); - se->committed = 1; - se->type = t; - - /* Assure space for terminating 0 - */ - if ((se->len + 1) * se->sz > (e->len + 1) * e->sz) - { - void *s = (void *)mem.xmalloc((se->len + 1) * se->sz); - memcpy(s, se->string, se->len * se->sz); - memset((char *)s + se->len * se->sz, 0, se->sz); - se->string = s; - } - result = se; - return; - } - - if (tb->ty != Tsarray && tb->ty != Tarray && tb->ty != Tpointer) - { - if (!copied) - { - se = (StringExp *)e->copy(); - copied = 1; - } - goto Lcast; - } - if (typeb->ty != Tsarray && typeb->ty != Tarray && typeb->ty != Tpointer) - { - if (!copied) - { - se = (StringExp *)e->copy(); - copied = 1; - } - goto Lcast; - } - - if (typeb->nextOf()->size() == tb->nextOf()->size()) - { - if (!copied) - { - se = (StringExp *)e->copy(); - copied = 1; - } - if (tb->ty == Tsarray) - goto L2; // handle possible change in static array dimension - se->type = t; - result = se; - return; - } - - if (e->committed) - goto Lcast; - - #define X(tf,tt) ((int)(tf) * 256 + (int)(tt)) - { - OutBuffer buffer; - size_t newlen = 0; - int tfty = typeb->nextOf()->toBasetype()->ty; - int ttty = tb->nextOf()->toBasetype()->ty; - switch (X(tfty, ttty)) - { - case X(Tchar, Tchar): - case X(Twchar,Twchar): - case X(Tdchar,Tdchar): - break; - - case X(Tchar, Twchar): - for (size_t u = 0; u < e->len;) - { - unsigned c; - const char *p = utf_decodeChar((utf8_t *)se->string, e->len, &u, &c); - if (p) - e->error("%s", p); - else - buffer.writeUTF16(c); - } - newlen = buffer.offset / 2; - buffer.writeUTF16(0); - goto L1; - - case X(Tchar, Tdchar): - for (size_t u = 0; u < e->len;) - { - unsigned c; - const char *p = utf_decodeChar((utf8_t *)se->string, e->len, &u, &c); - if (p) - e->error("%s", p); - buffer.write4(c); - newlen++; - } - buffer.write4(0); - goto L1; - - case X(Twchar,Tchar): - for (size_t u = 0; u < e->len;) - { - unsigned c; - const char *p = utf_decodeWchar((unsigned short *)se->string, e->len, &u, &c); - if (p) - e->error("%s", p); - else - buffer.writeUTF8(c); - } - newlen = buffer.offset; - buffer.writeUTF8(0); - goto L1; - - case X(Twchar,Tdchar): - for (size_t u = 0; u < e->len;) - { - unsigned c; - const char *p = utf_decodeWchar((unsigned short *)se->string, e->len, &u, &c); - if (p) - e->error("%s", p); - buffer.write4(c); - newlen++; - } - buffer.write4(0); - goto L1; - - case X(Tdchar,Tchar): - for (size_t u = 0; u < e->len; u++) - { - unsigned c = ((unsigned *)se->string)[u]; - if (!utf_isValidDchar(c)) - e->error("invalid UCS-32 char \\U%08x", c); - else - buffer.writeUTF8(c); - newlen++; - } - newlen = buffer.offset; - buffer.writeUTF8(0); - goto L1; - - case X(Tdchar,Twchar): - for (size_t u = 0; u < e->len; u++) - { - unsigned c = ((unsigned *)se->string)[u]; - if (!utf_isValidDchar(c)) - e->error("invalid UCS-32 char \\U%08x", c); - else - buffer.writeUTF16(c); - newlen++; - } - newlen = buffer.offset / 2; - buffer.writeUTF16(0); - goto L1; - - L1: - if (!copied) - { - se = (StringExp *)e->copy(); - copied = 1; - } - se->string = buffer.extractData(); - se->len = newlen; - - { - d_uns64 szx = tb->nextOf()->size(); - assert(szx <= 255); - se->sz = (unsigned char)szx; - } - break; - - default: - assert(typeb->nextOf()->size() != tb->nextOf()->size()); - goto Lcast; - } - } - #undef X - L2: - assert(copied); - - // See if need to truncate or extend the literal - if (tb->ty == Tsarray) - { - size_t dim2 = (size_t)((TypeSArray *)tb)->dim->toInteger(); - - //printf("dim from = %d, to = %d\n", (int)se->len, (int)dim2); - - // Changing dimensions - if (dim2 != se->len) - { - // Copy when changing the string literal - size_t newsz = se->sz; - size_t d = (dim2 < se->len) ? dim2 : se->len; - void *s = (void *)mem.xmalloc((dim2 + 1) * newsz); - memcpy(s, se->string, d * newsz); - // Extend with 0, add terminating 0 - memset((char *)s + d * newsz, 0, (dim2 + 1 - d) * newsz); - se->string = s; - se->len = dim2; - } - } - se->type = t; - result = se; - return; - - Lcast: - result = new CastExp(e->loc, se, t); - result->type = t; // so semantic() won't be run on e - } - - void visit(AddrExp *e) - { - Type *tb; - - #if 0 - printf("AddrExp::castTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - result = e; - - tb = t->toBasetype(); - e->type = e->type->toBasetype(); - if (!tb->equals(e->type)) - { - // Look for pointers to functions where the functions are overloaded. - - if (e->e1->op == TOKoverloadset && - (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) - { - OverExp *eo = (OverExp *)e->e1; - FuncDeclaration *f = NULL; - for (size_t i = 0; i < eo->vars->a.dim; i++) - { - Dsymbol *s = eo->vars->a[i]; - FuncDeclaration *f2 = s->isFuncDeclaration(); - assert(f2); - if (f2->overloadExactMatch(t->nextOf())) - { - if (f) - { - /* Error if match in more than one overload set, - * even if one is a 'better' match than the other. - */ - ScopeDsymbol::multiplyDefined(e->loc, f, f2); - } - else - f = f2; - } - } - if (f) - { - f->tookAddressOf++; - SymOffExp *se = new SymOffExp(e->loc, f, 0, 0); - se->semantic(sc); - // Let SymOffExp::castTo() do the heavy lifting - visit(se); - return; - } - } - - if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction && - tb->ty == Tpointer && tb->nextOf()->ty == Tfunction && - e->e1->op == TOKvar) - { - VarExp *ve = (VarExp *)e->e1; - FuncDeclaration *f = ve->var->isFuncDeclaration(); - if (f) - { - assert(f->isImportedSymbol()); - f = f->overloadExactMatch(tb->nextOf()); - if (f) - { - result = new VarExp(e->loc, f); - result->type = f->type; - result = new AddrExp(e->loc, result); - result->type = t; - return; - } - } - } - visit((Expression *)e); - } - result->type = t; - } - - void visit(TupleExp *e) - { - if (e->type->equals(t)) - { - result = e; - return; - } - - TupleExp *te = (TupleExp *)e->copy(); - te->e0 = e->e0 ? e->e0->copy() : NULL; - te->exps = (Expressions *)e->exps->copy(); - for (size_t i = 0; i < te->exps->dim; i++) - { - Expression *ex = (*te->exps)[i]; - ex = ex->castTo(sc, t); - (*te->exps)[i] = ex; - } - result = te; - - /* Questionable behavior: In here, result->type is not set to t. - * Therefoe: - * TypeTuple!(int, int) values; - * auto values2 = cast(long)values; - * // typeof(values2) == TypeTuple!(int, int) !! - * - * Only when the casted tuple is immediately expanded, it would work. - * auto arr = [cast(long)values]; - * // typeof(arr) == long[] - */ - } - - void visit(ArrayLiteralExp *e) - { - #if 0 - printf("ArrayLiteralExp::castTo(this=%s, type=%s, => %s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - if (e->type == t) - { - result = e; - return; - } - ArrayLiteralExp *ae = e; - Type *typeb = e->type->toBasetype(); - Type *tb = t->toBasetype(); - if ((tb->ty == Tarray || tb->ty == Tsarray) && - (typeb->ty == Tarray || typeb->ty == Tsarray)) - { - if (tb->nextOf()->toBasetype()->ty == Tvoid && typeb->nextOf()->toBasetype()->ty != Tvoid) - { - // Don't do anything to cast non-void[] to void[] - } - else if (typeb->ty == Tsarray && typeb->nextOf()->toBasetype()->ty == Tvoid) - { - // Don't do anything for casting void[n] to others - } - else - { - if (tb->ty == Tsarray) - { - TypeSArray *tsa = (TypeSArray *)tb; - if (e->elements->dim != tsa->dim->toInteger()) - goto L1; - } - - ae = (ArrayLiteralExp *)e->copy(); - ae->elements = e->elements->copy(); - for (size_t i = 0; i < e->elements->dim; i++) - { - Expression *ex = (*e->elements)[i]; - ex = ex->castTo(sc, tb->nextOf()); - (*ae->elements)[i] = ex; - } - ae->type = t; - result = ae; - return; - } - } - else if (tb->ty == Tpointer && typeb->ty == Tsarray) - { - Type *tp = typeb->nextOf()->pointerTo(); - if (!tp->equals(ae->type)) - { - ae = (ArrayLiteralExp *)e->copy(); - ae->type = tp; - } - } - else if (tb->ty == Tvector && - (typeb->ty == Tarray || typeb->ty == Tsarray)) - { - // Convert array literal to vector type - TypeVector *tv = (TypeVector *)tb; - TypeSArray *tbase = (TypeSArray *)tv->basetype; - assert(tbase->ty == Tsarray); - if (e->elements->dim != tbase->dim->toInteger()) - goto L1; - - ae = (ArrayLiteralExp *)e->copy(); - ae->type = tbase; // Bugzilla 12642 - ae->elements = e->elements->copy(); - Type *telement = tv->elementType(); - for (size_t i = 0; i < e->elements->dim; i++) - { - Expression *ex = (*e->elements)[i]; - ex = ex->castTo(sc, telement); - (*ae->elements)[i] = ex; - } - Expression *ev = new VectorExp(e->loc, ae, tb); - ev = ev->semantic(sc); - result = ev; - return; - } - L1: - visit((Expression *)ae); - } - - void visit(AssocArrayLiteralExp *e) - { - if (e->type == t) - { - result = e; - return; - } - Type *typeb = e->type->toBasetype(); - Type *tb = t->toBasetype(); - if (tb->ty == Taarray && typeb->ty == Taarray && - tb->nextOf()->toBasetype()->ty != Tvoid) - { - AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e->copy(); - ae->keys = e->keys->copy(); - ae->values = e->values->copy(); - assert(e->keys->dim == e->values->dim); - for (size_t i = 0; i < e->keys->dim; i++) - { - Expression *ex = (*e->values)[i]; - ex = ex->castTo(sc, tb->nextOf()); - (*ae->values)[i] = ex; - - ex = (*e->keys)[i]; - ex = ex->castTo(sc, ((TypeAArray *)tb)->index); - (*ae->keys)[i] = ex; - } - ae->type = t; - result = ae; - return; - } - visit((Expression *)e); - } - - void visit(SymOffExp *e) - { - #if 0 - printf("SymOffExp::castTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - if (e->type == t && !e->hasOverloads) - { - result = e; - return; - } - Type *tb = t->toBasetype(); - Type *typeb = e->type->toBasetype(); - - if (tb->equals(typeb)) - { - result = e->copy(); - result->type = t; - ((SymOffExp *)result)->hasOverloads = false; - return; - } - - // Look for pointers to functions where the functions are overloaded. - if (e->hasOverloads && - typeb->ty == Tpointer && typeb->nextOf()->ty == Tfunction && - (tb->ty == Tpointer || tb->ty == Tdelegate) && tb->nextOf()->ty == Tfunction) - { - FuncDeclaration *f = e->var->isFuncDeclaration(); - f = f ? f->overloadExactMatch(tb->nextOf()) : NULL; - if (f) - { - if (tb->ty == Tdelegate) - { - if (f->needThis() && hasThis(sc)) - { - result = new DelegateExp(e->loc, new ThisExp(e->loc), f); - result = result->semantic(sc); - } - else if (f->isNested()) - { - result = new DelegateExp(e->loc, new IntegerExp(0), f); - result = result->semantic(sc); - } - else if (f->needThis()) - { - e->error("no 'this' to create delegate for %s", f->toChars()); - result = new ErrorExp(); - return; - } - else - { - e->error("cannot cast from function pointer to delegate"); - result = new ErrorExp(); - return; - } - } - else - { - result = new SymOffExp(e->loc, f, 0); - result->type = t; - } - f->tookAddressOf++; - return; - } - } - visit((Expression *)e); - } - - void visit(DelegateExp *e) - { - #if 0 - printf("DelegateExp::castTo(this=%s, type=%s, t=%s)\n", - e->toChars(), e->type->toChars(), t->toChars()); - #endif - static const char msg[] = "cannot form delegate due to covariant return type"; - - Type *tb = t->toBasetype(); - Type *typeb = e->type->toBasetype(); - if (!tb->equals(typeb) || e->hasOverloads) - { - // Look for delegates to functions where the functions are overloaded. - if (typeb->ty == Tdelegate && - tb->ty == Tdelegate) - { - if (e->func) - { - FuncDeclaration *f = e->func->overloadExactMatch(tb->nextOf()); - if (f) - { - int offset; - if (f->tintro && f->tintro->nextOf()->isBaseOf(f->type->nextOf(), &offset) && offset) - e->error("%s", msg); - f->tookAddressOf++; - result = new DelegateExp(e->loc, e->e1, f); - result->type = t; - return; - } - if (e->func->tintro) - e->error("%s", msg); - } - } - visit((Expression *)e); - } - else - { - int offset; - e->func->tookAddressOf++; - if (e->func->tintro && e->func->tintro->nextOf()->isBaseOf(e->func->type->nextOf(), &offset) && offset) - e->error("%s", msg); - result = e->copy(); - result->type = t; - } - } - - void visit(FuncExp *e) - { - //printf("FuncExp::castTo type = %s, t = %s\n", e->type->toChars(), t->toChars()); - FuncExp *fe; - if (e->matchType(t, sc, &fe, 1) > MATCHnomatch) - { - result = fe; - return; - } - visit((Expression *)e); - } - - void visit(CondExp *e) - { - if (!e->type->equals(t)) - { - result = new CondExp(e->loc, e->econd, e->e1->castTo(sc, t), e->e2->castTo(sc, t)); - result->type = t; - return; - } - result = e; - } - - void visit(CommaExp *e) - { - Expression *e2c = e->e2->castTo(sc, t); - - if (e2c != e->e2) - { - result = new CommaExp(e->loc, e->e1, e2c); - result->type = e2c->type; - } - else - { - result = e; - result->type = e->e2->type; - } - } - - void visit(SliceExp *e) - { - //printf("SliceExp::castTo e = %s, type = %s, t = %s\n", e->toChars(), e->type->toChars(), t->toChars()); - Type *typeb = e->type->toBasetype(); - Type *tb = t->toBasetype(); - if (e->type->equals(t) || typeb->ty != Tarray || - (tb->ty != Tarray && tb->ty != Tsarray)) - { - visit((Expression *)e); - return; - } - - if (tb->ty == Tarray) - { - if (typeb->nextOf()->equivalent(tb->nextOf())) - { - // T[] to const(T)[] - result = e->copy(); - result->type = t; - } - else - { - visit((Expression *)e); - } - return; - } - - // Handle the cast from Tarray to Tsarray with CT-known slicing - - TypeSArray *tsa = (TypeSArray *)toStaticArrayType(e); - if (tsa && tsa->size(e->loc) == tb->size(e->loc)) - { - /* Match if the sarray sizes are equal: - * T[a .. b] to const(T)[b-a] - * T[a .. b] to U[dim] if (T.sizeof*(b-a) == U.sizeof*dim) - * - * If a SliceExp has Tsarray, it will become lvalue. - * That's handled in SliceExp::isLvalue and toLvalue - */ - result = e->copy(); - result->type = t; - return; - } - if (tsa && tsa->dim->equals(((TypeSArray *)tb)->dim)) - { - /* Match if the dimensions are equal - * with the implicit conversion of e->e1: - * cast(float[2]) [2.0, 1.0, 0.0][0..2]; - */ - Type *t1b = e->e1->type->toBasetype(); - if (t1b->ty == Tsarray) - t1b = tb->nextOf()->sarrayOf(((TypeSArray *)t1b)->dim->toInteger()); - else if (t1b->ty == Tarray) - t1b = tb->nextOf()->arrayOf(); - else if (t1b->ty == Tpointer) - t1b = tb->nextOf()->pointerTo(); - else - assert(0); - if (e->e1->implicitConvTo(t1b) > MATCHnomatch) - { - Expression *e1x = e->e1->implicitCastTo(sc, t1b); - assert(e1x->op != TOKerror); - e = (SliceExp *)e->copy(); - e->e1 = e1x; - e->type = t; - result = e; - return; - } - } - e->error("cannot cast expression %s of type %s to %s", - e->toChars(), tsa ? tsa->toChars() : e->type->toChars(), - t->toChars()); - result = new ErrorExp(); - } - }; - - CastTo v(sc, t); - e->accept(&v); - return v.result; -} - -/* ==================== inferType ====================== */ - -/**************************************** - * Set type inference target - * t Target type - * flag 1: don't put an error when inference fails - */ - -Expression *inferType(Expression *e, Type *t, int flag) -{ - class InferType : public Visitor - { - public: - Type *t; - int flag; - Expression *result; - - InferType(Type *t, int flag) - : t(t), flag(flag) - { - result = NULL; - } - - - void visit(Expression *e) - { - result = e; - } - - void visit(ArrayLiteralExp *ale) - { - Type *tb = t->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) - { - Type *tn = tb->nextOf(); - for (size_t i = 0; i < ale->elements->dim; i++) - { - Expression *e = (*ale->elements)[i]; - if (e) - { - e = inferType(e, tn, flag); - (*ale->elements)[i] = e; - } - } - } - result = ale; - } - - void visit(AssocArrayLiteralExp *aale) - { - Type *tb = t->toBasetype(); - if (tb->ty == Taarray) - { - TypeAArray *taa = (TypeAArray *)tb; - Type *ti = taa->index; - Type *tv = taa->nextOf(); - for (size_t i = 0; i < aale->keys->dim; i++) - { - Expression *e = (*aale->keys)[i]; - if (e) - { - e = inferType(e, ti, flag); - (*aale->keys)[i] = e; - } - } - for (size_t i = 0; i < aale->values->dim; i++) - { - Expression *e = (*aale->values)[i]; - if (e) - { - e = inferType(e, tv, flag); - (*aale->values)[i] = e; - } - } - } - result = aale; - } - - void visit(FuncExp *fe) - { - //printf("FuncExp::inferType('%s'), to=%s\n", fe->type ? fe->type->toChars() : "null", t->toChars()); - if (t->ty == Tdelegate || - t->ty == Tpointer && t->nextOf()->ty == Tfunction) - { - fe->fd->treq = t; - } - result = fe; - } - - void visit(CondExp *ce) - { - Type *tb = t->toBasetype(); - ce->e1 = inferType(ce->e1, tb, flag); - ce->e2 = inferType(ce->e2, tb, flag); - result = ce; - } - }; - - if (!t) - return e; - - InferType v(t, flag); - e->accept(&v); - return v.result; -} - -/* ==================== ====================== */ - -/**************************************** - * Scale addition/subtraction to/from pointer. - */ - -Expression *scaleFactor(BinExp *be, Scope *sc) -{ - Type *t1b = be->e1->type->toBasetype(); - Type *t2b = be->e2->type->toBasetype(); - Expression *eoff; - - if (t1b->ty == Tpointer && t2b->isintegral()) - { - // Need to adjust operator by the stride - // Replace (ptr + int) with (ptr + (int * stride)) - Type *t = Type::tptrdiff_t; - - d_uns64 stride = t1b->nextOf()->size(be->loc); - if (!t->equals(t2b)) - be->e2 = be->e2->castTo(sc, t); - eoff = be->e2; - be->e2 = new MulExp(be->loc, be->e2, new IntegerExp(Loc(), stride, t)); - be->e2->type = t; - be->type = be->e1->type; - } - else if (t2b->ty == Tpointer && t1b->isintegral()) - { - // Need to adjust operator by the stride - // Replace (int + ptr) with (ptr + (int * stride)) - Type *t = Type::tptrdiff_t; - Expression *e; - - d_uns64 stride = t2b->nextOf()->size(be->loc); - if (!t->equals(t1b)) - e = be->e1->castTo(sc, t); - else - e = be->e1; - eoff = e; - e = new MulExp(be->loc, e, new IntegerExp(Loc(), stride, t)); - e->type = t; - be->type = be->e2->type; - be->e1 = be->e2; - be->e2 = e; - } - else - assert(0); - - if (sc->func && !sc->intypeof) - { - eoff = eoff->optimize(WANTvalue); - if (eoff->op == TOKint64 && eoff->toInteger() == 0) - ; - else if (sc->func->setUnsafe()) - { - be->error("pointer arithmetic not allowed in @safe functions"); - return new ErrorExp(); - } - } - - return be; -} - -/************************************** - * Return true if e is an empty array literal with dimensionality - * equal to or less than type of other array. - * [], [[]], [[[]]], etc. - * I.e., make sure that [1,2] is compatible with [], - * [[1,2]] is compatible with [[]], etc. - */ -bool isVoidArrayLiteral(Expression *e, Type *other) -{ - while (e->op == TOKarrayliteral && e->type->ty == Tarray - && (((ArrayLiteralExp *)e)->elements->dim == 1)) - { - e = (*((ArrayLiteralExp *)e)->elements)[0]; - if (other->ty == Tsarray || other->ty == Tarray) - other = other->nextOf(); - else - return false; - } - if (other->ty != Tsarray && other->ty != Tarray) - return false; - Type *t = e->type; - return (e->op == TOKarrayliteral && t->ty == Tarray && - t->nextOf()->ty == Tvoid && - ((ArrayLiteralExp *)e)->elements->dim == 0); -} - - -/************************************** - * Combine types. - * Output: - * *pt merged type, if *pt is not NULL - * *pe1 rewritten e1 - * *pe2 rewritten e2 - * Returns: - * true success - * false failed - */ - -bool typeMerge(Scope *sc, TOK op, Type **pt, Expression **pe1, Expression **pe2) -{ - //printf("typeMerge() %s op %s\n", (*pe1)->toChars(), (*pe2)->toChars()); - - MATCH m; - Expression *e1 = *pe1; - Expression *e2 = *pe2; - Type *t1b = e1->type->toBasetype(); - Type *t2b = e2->type->toBasetype(); - - if (op != TOKquestion || - t1b->ty != t2b->ty && (t1b->isTypeBasic() && t2b->isTypeBasic())) - { - e1 = integralPromotions(e1, sc); - e2 = integralPromotions(e2, sc); - } - - Type *t1 = e1->type; - Type *t2 = e2->type; - assert(t1); - Type *t = t1; - - /* The start type of alias this type recursion. - * In following case, we should save A, and stop recursion - * if it appears again. - * X -> Y -> [A] -> B -> A -> B -> ... - */ - Type *att1 = NULL; - Type *att2 = NULL; - - //if (t1) printf("\tt1 = %s\n", t1->toChars()); - //if (t2) printf("\tt2 = %s\n", t2->toChars()); -#ifdef DEBUG - if (!t2) printf("\te2 = '%s'\n", e2->toChars()); -#endif - assert(t2); - -Lagain: - t1b = t1->toBasetype(); - t2b = t2->toBasetype(); - - TY ty = (TY)impcnvResult[t1b->ty][t2b->ty]; - if (ty != Terror) - { - TY ty1 = (TY)impcnvType1[t1b->ty][t2b->ty]; - TY ty2 = (TY)impcnvType2[t1b->ty][t2b->ty]; - - if (t1b->ty == ty1) // if no promotions - { - if (t1->equals(t2)) - { - t = t1; - goto Lret; - } - - if (t1b->equals(t2b)) - { - t = t1b; - goto Lret; - } - } - - t = Type::basic[ty]; - - t1 = Type::basic[ty1]; - t2 = Type::basic[ty2]; - e1 = e1->castTo(sc, t1); - e2 = e2->castTo(sc, t2); - //printf("after typeCombine():\n"); - //print(); - //printf("ty = %d, ty1 = %d, ty2 = %d\n", ty, ty1, ty2); - goto Lret; - } - - t1 = t1b; - t2 = t2b; - - if (t1->ty == Ttuple || t2->ty == Ttuple) - goto Lincompatible; - - if (t1->equals(t2)) - { - // merging can not result in new enum type - if (t->ty == Tenum) - t = t1b; - } - else if ((t1->ty == Tpointer && t2->ty == Tpointer) || - (t1->ty == Tdelegate && t2->ty == Tdelegate)) - { - // Bring pointers to compatible type - Type *t1n = t1->nextOf(); - Type *t2n = t2->nextOf(); - - if (t1n->equals(t2n)) - ; - else if (t1n->ty == Tvoid) // pointers to void are always compatible - t = t2; - else if (t2n->ty == Tvoid) - ; - else if (t1->implicitConvTo(t2)) - { - goto Lt2; - } - else if (t2->implicitConvTo(t1)) - { - goto Lt1; - } - else if (t1n->ty == Tfunction && t2n->ty == Tfunction) - { - TypeFunction *tf1 = (TypeFunction *)t1n; - TypeFunction *tf2 = (TypeFunction *)t2n; - tf1->purityLevel(); - tf2->purityLevel(); - - TypeFunction *d = (TypeFunction *)tf1->syntaxCopy(); - - if (tf1->purity != tf2->purity) - d->purity = PUREimpure; - assert(d->purity != PUREfwdref); - - d->isnothrow = (tf1->isnothrow && tf2->isnothrow); - d->isnogc = (tf1->isnogc && tf2->isnogc); - - if (tf1->trust == tf2->trust) - d->trust = tf1->trust; - else if (tf1->trust <= TRUSTsystem || tf2->trust <= TRUSTsystem) - d->trust = TRUSTsystem; - else - d->trust = TRUSTtrusted; - - Type *tx = NULL; - if (t1->ty == Tdelegate) - { - tx = new TypeDelegate(d); - } - else - tx = d->pointerTo(); - - tx = tx->semantic(e1->loc, sc); - - if (t1->implicitConvTo(tx) && t2->implicitConvTo(tx)) - { - t = tx; - e1 = e1->castTo(sc, t); - e2 = e2->castTo(sc, t); - goto Lret; - } - goto Lincompatible; - } - else if (t1n->mod != t2n->mod) - { - if (!t1n->isImmutable() && !t2n->isImmutable() && t1n->isShared() != t2n->isShared()) - goto Lincompatible; - unsigned char mod = MODmerge(t1n->mod, t2n->mod); - t1 = t1n->castMod(mod)->pointerTo(); - t2 = t2n->castMod(mod)->pointerTo(); - t = t1; - goto Lagain; - } - else if (t1n->ty == Tclass && t2n->ty == Tclass) - { - ClassDeclaration *cd1 = t1n->isClassHandle(); - ClassDeclaration *cd2 = t2n->isClassHandle(); - int offset; - - if (cd1->isBaseOf(cd2, &offset)) - { - if (offset) - e2 = e2->castTo(sc, t); - } - else if (cd2->isBaseOf(cd1, &offset)) - { - t = t2; - if (offset) - e1 = e1->castTo(sc, t); - } - else - goto Lincompatible; - } - else - { - t1 = t1n->constOf()->pointerTo(); - t2 = t2n->constOf()->pointerTo(); - if (t1->implicitConvTo(t2)) - { - goto Lt2; - } - else if (t2->implicitConvTo(t1)) - { - goto Lt1; - } - goto Lincompatible; - } - } - else if ((t1->ty == Tsarray || t1->ty == Tarray) && - (e2->op == TOKnull && t2->ty == Tpointer && t2->nextOf()->ty == Tvoid || - e2->op == TOKarrayliteral && t2->ty == Tsarray && t2->nextOf()->ty == Tvoid && ((TypeSArray *)t2)->dim->toInteger() == 0 || - isVoidArrayLiteral(e2, t1)) - ) - { - /* (T[n] op void*) => T[] - * (T[] op void*) => T[] - * (T[n] op void[0]) => T[] - * (T[] op void[0]) => T[] - * (T[n] op void[]) => T[] - * (T[] op void[]) => T[] - */ - goto Lx1; - } - else if ((t2->ty == Tsarray || t2->ty == Tarray) && - (e1->op == TOKnull && t1->ty == Tpointer && t1->nextOf()->ty == Tvoid || - e1->op == TOKarrayliteral && t1->ty == Tsarray && t1->nextOf()->ty == Tvoid && ((TypeSArray *)t1)->dim->toInteger() == 0 || - isVoidArrayLiteral(e1, t2)) - ) - { - /* (void* op T[n]) => T[] - * (void* op T[]) => T[] - * (void[0] op T[n]) => T[] - * (void[0] op T[]) => T[] - * (void[] op T[n]) => T[] - * (void[] op T[]) => T[] - */ - goto Lx2; - } - else if ((t1->ty == Tsarray || t1->ty == Tarray) && - (m = t1->implicitConvTo(t2)) != MATCHnomatch) - { - // Bugzilla 7285: Tsarray op [x, y, ...] should to be Tsarray - // Bugzilla 14737: Tsarray ~ [x, y, ...] should to be Tarray - if (t1->ty == Tsarray && e2->op == TOKarrayliteral && op != TOKcat) - goto Lt1; - if (m == MATCHconst && - (op == TOKaddass || op == TOKminass || op == TOKmulass || - op == TOKdivass || op == TOKmodass || op == TOKpowass || - op == TOKandass || op == TOKorass || op == TOKxorass) - ) - { - // Don't make the lvalue const - t = t2; - goto Lret; - } - goto Lt2; - } - else if ((t2->ty == Tsarray || t2->ty == Tarray) && t2->implicitConvTo(t1)) - { - // Bugzilla 7285 & 14737 - if (t2->ty == Tsarray && e1->op == TOKarrayliteral && op != TOKcat) - goto Lt2; - goto Lt1; - } - else if ((t1->ty == Tsarray || t1->ty == Tarray || t1->ty == Tpointer) && - (t2->ty == Tsarray || t2->ty == Tarray || t2->ty == Tpointer) && - t1->nextOf()->mod != t2->nextOf()->mod - ) - { - /* If one is mutable and the other invariant, then retry - * with both of them as const - */ - Type *t1n = t1->nextOf(); - Type *t2n = t2->nextOf(); - unsigned char mod; - if (e1->op == TOKnull && e2->op != TOKnull) - mod = t2n->mod; - else if (e1->op != TOKnull && e2->op == TOKnull) - mod = t1n->mod; - else if (!t1n->isImmutable() && !t2n->isImmutable() && t1n->isShared() != t2n->isShared()) - goto Lincompatible; - else - mod = MODmerge(t1n->mod, t2n->mod); - - if (t1->ty == Tpointer) - t1 = t1n->castMod(mod)->pointerTo(); - else - t1 = t1n->castMod(mod)->arrayOf(); - - if (t2->ty == Tpointer) - t2 = t2n->castMod(mod)->pointerTo(); - else - t2 = t2n->castMod(mod)->arrayOf(); - t = t1; - goto Lagain; - } - else if (t1->ty == Tclass && t2->ty == Tclass) - { - if (t1->mod != t2->mod) - { - unsigned char mod; - if (e1->op == TOKnull && e2->op != TOKnull) - mod = t2->mod; - else if (e1->op != TOKnull && e2->op == TOKnull) - mod = t1->mod; - else if (!t1->isImmutable() && !t2->isImmutable() && t1->isShared() != t2->isShared()) - goto Lincompatible; - else - mod = MODmerge(t1->mod, t2->mod); - t1 = t1->castMod(mod); - t2 = t2->castMod(mod); - t = t1; - goto Lagain; - } - goto Lcc; - } - else if (t1->ty == Tclass || t2->ty == Tclass) - { -Lcc: - while (1) - { - MATCH i1 = e2->implicitConvTo(t1); - MATCH i2 = e1->implicitConvTo(t2); - - if (i1 && i2) - { - // We have the case of class vs. void*, so pick class - if (t1->ty == Tpointer) - i1 = MATCHnomatch; - else if (t2->ty == Tpointer) - i2 = MATCHnomatch; - } - - if (i2) - { - e2 = e2->castTo(sc, t2); - goto Lt2; - } - else if (i1) - { - e1 = e1->castTo(sc, t1); - goto Lt1; - } - else if (t1->ty == Tclass && t2->ty == Tclass) - { - TypeClass *tc1 = (TypeClass *)t1; - TypeClass *tc2 = (TypeClass *)t2; - - /* Pick 'tightest' type - */ - ClassDeclaration *cd1 = tc1->sym->baseClass; - ClassDeclaration *cd2 = tc2->sym->baseClass; - - if (cd1 && cd2) - { - t1 = cd1->type; - t2 = cd2->type; - } - else if (cd1) - t1 = cd1->type; - else if (cd2) - t2 = cd2->type; - else - goto Lincompatible; - } - else if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis) - { - if (att1 && e1->type == att1) - goto Lincompatible; - if (!att1 && e1->type->checkAliasThisRec()) - att1 = e1->type; - //printf("att tmerge(c || c) e1 = %s\n", e1->type->toChars()); - e1 = resolveAliasThis(sc, e1); - t1 = e1->type; - continue; - } - else if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis) - { - if (att2 && e2->type == att2) - goto Lincompatible; - if (!att2 && e2->type->checkAliasThisRec()) - att2 = e2->type; - //printf("att tmerge(c || c) e2 = %s\n", e2->type->toChars()); - e2 = resolveAliasThis(sc, e2); - t2 = e2->type; - continue; - } - else - goto Lincompatible; - } - } - else if (t1->ty == Tstruct && t2->ty == Tstruct) - { - if (t1->mod != t2->mod) - { - if (!t1->isImmutable() && !t2->isImmutable() && t1->isShared() != t2->isShared()) - goto Lincompatible; - unsigned char mod = MODmerge(t1->mod, t2->mod); - t1 = t1->castMod(mod); - t2 = t2->castMod(mod); - t = t1; - goto Lagain; - } - - TypeStruct *ts1 = (TypeStruct *)t1; - TypeStruct *ts2 = (TypeStruct *)t2; - if (ts1->sym != ts2->sym) - { - if (!ts1->sym->aliasthis && !ts2->sym->aliasthis) - goto Lincompatible; - - MATCH i1 = MATCHnomatch; - MATCH i2 = MATCHnomatch; - - Expression *e1b = NULL; - Expression *e2b = NULL; - if (ts2->sym->aliasthis) - { - if (att2 && e2->type == att2) - goto Lincompatible; - if (!att2 && e2->type->checkAliasThisRec()) - att2 = e2->type; - //printf("att tmerge(s && s) e2 = %s\n", e2->type->toChars()); - e2b = resolveAliasThis(sc, e2); - i1 = e2b->implicitConvTo(t1); - } - if (ts1->sym->aliasthis) - { - if (att1 && e1->type == att1) - goto Lincompatible; - if (!att1 && e1->type->checkAliasThisRec()) - att1 = e1->type; - //printf("att tmerge(s && s) e1 = %s\n", e1->type->toChars()); - e1b = resolveAliasThis(sc, e1); - i2 = e1b->implicitConvTo(t2); - } - if (i1 && i2) - goto Lincompatible; - - if (i1) - goto Lt1; - else if (i2) - goto Lt2; - - if (e1b) - { - e1 = e1b; - t1 = e1b->type->toBasetype(); - } - if (e2b) - { - e2 = e2b; - t2 = e2b->type->toBasetype(); - } - t = t1; - goto Lagain; - } - } - else if (t1->ty == Tstruct || t2->ty == Tstruct) - { - if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis) - { - if (att1 && e1->type == att1) - goto Lincompatible; - if (!att1 && e1->type->checkAliasThisRec()) - att1 = e1->type; - //printf("att tmerge(s || s) e1 = %s\n", e1->type->toChars()); - e1 = resolveAliasThis(sc, e1); - t1 = e1->type; - t = t1; - goto Lagain; - } - if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis) - { - if (att2 && e2->type == att2) - goto Lincompatible; - if (!att2 && e2->type->checkAliasThisRec()) - att2 = e2->type; - //printf("att tmerge(s || s) e2 = %s\n", e2->type->toChars()); - e2 = resolveAliasThis(sc, e2); - t2 = e2->type; - t = t2; - goto Lagain; - } - goto Lincompatible; - } - else if ((e1->op == TOKstring || e1->op == TOKnull) && e1->implicitConvTo(t2)) - { - goto Lt2; - } - else if ((e2->op == TOKstring || e2->op == TOKnull) && e2->implicitConvTo(t1)) - { - goto Lt1; - } - else if (t1->ty == Tsarray && t2->ty == Tsarray && - e2->implicitConvTo(t1->nextOf()->arrayOf())) - { - Lx1: - t = t1->nextOf()->arrayOf(); // T[] - e1 = e1->castTo(sc, t); - e2 = e2->castTo(sc, t); - } - else if (t1->ty == Tsarray && t2->ty == Tsarray && - e1->implicitConvTo(t2->nextOf()->arrayOf())) - { - Lx2: - t = t2->nextOf()->arrayOf(); - e1 = e1->castTo(sc, t); - e2 = e2->castTo(sc, t); - } - else if (t1->ty == Tvector && t2->ty == Tvector) - { - // Bugzilla 13841, all vector types should have no common types between - // different vectors, even though their sizes are same. - goto Lincompatible; - } - else if (t1->ty == Tvector && t2->ty != Tvector && - e2->implicitConvTo(t1)) - { - e2 = e2->castTo(sc, t1); - t2 = t1; - t = t1; - goto Lagain; - } - else if (t2->ty == Tvector && t1->ty != Tvector && - e1->implicitConvTo(t2)) - { - e1 = e1->castTo(sc, t2); - t1 = t2; - t = t1; - goto Lagain; - } - else if (t1->isintegral() && t2->isintegral()) - { - if (t1->ty != t2->ty) - { - e1 = integralPromotions(e1, sc); - e2 = integralPromotions(e2, sc); - t1 = e1->type; - t2 = e2->type; - goto Lagain; - } - assert(t1->ty == t2->ty); - if (!t1->isImmutable() && !t2->isImmutable() && t1->isShared() != t2->isShared()) - goto Lincompatible; - unsigned char mod = MODmerge(t1->mod, t2->mod); - - t1 = t1->castMod(mod); - t2 = t2->castMod(mod); - t = t1; - e1 = e1->castTo(sc, t); - e2 = e2->castTo(sc, t); - goto Lagain; - } - else if (t1->ty == Tnull && t2->ty == Tnull) - { - unsigned char mod = MODmerge(t1->mod, t2->mod); - - t = t1->castMod(mod); - e1 = e1->castTo(sc, t); - e2 = e2->castTo(sc, t); - goto Lret; - } - else if (t2->ty == Tnull && - (t1->ty == Tpointer || t1->ty == Taarray || t1->ty == Tarray)) - { - goto Lt1; - } - else if (t1->ty == Tnull && - (t2->ty == Tpointer || t2->ty == Taarray || t2->ty == Tarray)) - { - goto Lt2; - } - else if (t1->ty == Tarray && isBinArrayOp(op) && isArrayOpOperand(e1)) - { - if (e2->implicitConvTo(t1->nextOf())) - { - // T[] op T - // T[] op cast(T)U - e2 = e2->castTo(sc, t1->nextOf()); - t = t1->nextOf()->arrayOf(); - } - else if (t1->nextOf()->implicitConvTo(e2->type)) - { - // (cast(T)U)[] op T (Bugzilla 12780) - // e1 is left as U[], it will be handled in arrayOp() later. - t = e2->type->arrayOf(); - } - else if (t2->ty == Tarray && isArrayOpOperand(e2)) - { - if (t1->nextOf()->implicitConvTo(t2->nextOf())) - { - // (cast(T)U)[] op T[] (Bugzilla 12780) - // e1 is left as U[], it will be handled in arrayOp() later. - t = t2->nextOf()->arrayOf(); - } - else if (t2->nextOf()->implicitConvTo(t1->nextOf())) - { - // T[] op (cast(T)U)[] (Bugzilla 12780) - // e2 is left as U[], it will be handled in arrayOp() later. - t = t1->nextOf()->arrayOf(); - } - else - goto Lincompatible; - } - else - goto Lincompatible; - } - else if (t2->ty == Tarray && isBinArrayOp(op) && isArrayOpOperand(e2)) - { - if (e1->implicitConvTo(t2->nextOf())) - { - // T op T[] - // cast(T)U op T[] - e1 = e1->castTo(sc, t2->nextOf()); - t = t2->nextOf()->arrayOf(); - } - else if (t2->nextOf()->implicitConvTo(e1->type)) - { - // T op (cast(T)U)[] (Bugzilla 12780) - // e2 is left as U[], it will be handled in arrayOp() later. - t = e1->type->arrayOf(); - } - else - goto Lincompatible; - - //printf("test %s\n", Token::toChars(op)); - e1 = e1->optimize(WANTvalue); - if (isCommutative(op) && e1->isConst()) - { - /* Swap operands to minimize number of functions generated - */ - //printf("swap %s\n", Token::toChars(op)); - Expression *tmp = e1; - e1 = e2; - e2 = tmp; - } - } - else - { - Lincompatible: - return false; - } -Lret: - if (!*pt) - *pt = t; - *pe1 = e1; - *pe2 = e2; -#if 0 - printf("-typeMerge() %s op %s\n", e1->toChars(), e2->toChars()); - if (e1->type) printf("\tt1 = %s\n", e1->type->toChars()); - if (e2->type) printf("\tt2 = %s\n", e2->type->toChars()); - printf("\ttype = %s\n", t->toChars()); -#endif - //print(); - return true; - - -Lt1: - e2 = e2->castTo(sc, t1); - t = t1; - goto Lret; - -Lt2: - e1 = e1->castTo(sc, t2); - t = t2; - goto Lret; -} - -/************************************ - * Bring leaves to common type. - * Returns ErrorExp if error occurs. otherwise returns NULL. - */ - -Expression *typeCombine(BinExp *be, Scope *sc) -{ - Type *t1 = be->e1->type->toBasetype(); - Type *t2 = be->e2->type->toBasetype(); - - if (be->op == TOKmin || be->op == TOKadd) - { - // struct+struct, and class+class are errors - if (t1->ty == Tstruct && t2->ty == Tstruct) - goto Lerror; - else if (t1->ty == Tclass && t2->ty == Tclass) - goto Lerror; - else if (t1->ty == Taarray && t2->ty == Taarray) - goto Lerror; - } - - if (!typeMerge(sc, be->op, &be->type, &be->e1, &be->e2)) - goto Lerror; - // If the types have no value, return an error - if (be->e1->op == TOKerror) - return be->e1; - if (be->e2->op == TOKerror) - return be->e2; - return NULL; - -Lerror: - Expression *ex = be->incompatibleTypes(); - if (ex->op == TOKerror) - return ex; - return new ErrorExp(); -} - -/*********************************** - * Do integral promotions (convertchk). - * Don't convert to - */ - -Expression *integralPromotions(Expression *e, Scope *sc) -{ - //printf("integralPromotions %s %s\n", e->toChars(), e->type->toChars()); - switch (e->type->toBasetype()->ty) - { - case Tvoid: - e->error("void has no value"); - return new ErrorExp(); - - case Tint8: - case Tuns8: - case Tint16: - case Tuns16: - case Tbool: - case Tchar: - case Twchar: - e = e->castTo(sc, Type::tint32); - break; - - case Tdchar: - e = e->castTo(sc, Type::tuns32); - break; - default: - break; - } - return e; -} - -/*********************************** - * See if both types are arrays that can be compared - * for equality. Return true if so. - * If they are arrays, but incompatible, issue error. - * This is to enable comparing things like an immutable - * array with a mutable one. - */ - -bool arrayTypeCompatible(Loc loc, Type *t1, Type *t2) -{ - t1 = t1->toBasetype()->merge2(); - t2 = t2->toBasetype()->merge2(); - - if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) && - (t2->ty == Tarray || t2->ty == Tsarray || t2->ty == Tpointer)) - { - if (t1->nextOf()->implicitConvTo(t2->nextOf()) < MATCHconst && - t2->nextOf()->implicitConvTo(t1->nextOf()) < MATCHconst && - (t1->nextOf()->ty != Tvoid && t2->nextOf()->ty != Tvoid)) - { - error(loc, "array equality comparison type mismatch, %s vs %s", t1->toChars(), t2->toChars()); - } - return true; - } - return false; -} - -/*********************************** - * See if both types are arrays that can be compared - * for equality without any casting. Return true if so. - * This is to enable comparing things like an immutable - * array with a mutable one. - */ -bool arrayTypeCompatibleWithoutCasting(Loc loc, Type *t1, Type *t2) -{ - t1 = t1->toBasetype(); - t2 = t2->toBasetype(); - - if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) && - t2->ty == t1->ty) - { - if (t1->nextOf()->implicitConvTo(t2->nextOf()) >= MATCHconst || - t2->nextOf()->implicitConvTo(t1->nextOf()) >= MATCHconst) - return true; - } - return false; -} - -/******************************************************************/ - -/* Determine the integral ranges of an expression. - * This is used to determine if implicit narrowing conversions will - * be allowed. - */ - -IntRange getIntRange(Expression *e) -{ - class IntRangeVisitor : public Visitor - { - private: - static uinteger_t getMask(uinteger_t v) - { - // Ref: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v |= v >> 32; - return v; - } - - // The algorithms for &, |, ^ are not yet the best! Sometimes they will produce - // not the tightest bound. See - // https://github.com/D-Programming-Language/dmd/pull/116 - // for detail. - static IntRange unsignedBitwiseAnd(const IntRange& a, const IntRange& b) - { - // the DiffMasks stores the mask of bits which are variable in the range. - uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); - uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); - // Since '&' computes the digitwise-minimum, the we could set all varying - // digits to 0 to get a lower bound, and set all varying digits to 1 to get - // an upper bound. - IntRange result; - result.imin.value = (a.imin.value & ~aDiffMask) & (b.imin.value & ~bDiffMask); - result.imax.value = (a.imax.value | aDiffMask) & (b.imax.value | bDiffMask); - // Sometimes the upper bound is overestimated. The upper bound will never - // exceed the input. - if (result.imax.value > a.imax.value) - result.imax.value = a.imax.value; - if (result.imax.value > b.imax.value) - result.imax.value = b.imax.value; - result.imin.negative = result.imax.negative = a.imin.negative && b.imin.negative; - return result; - } - static IntRange unsignedBitwiseOr(const IntRange& a, const IntRange& b) - { - // the DiffMasks stores the mask of bits which are variable in the range. - uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); - uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); - // The imax algorithm by Adam D. Ruppe. - // http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=108796 - IntRange result; - result.imin.value = (a.imin.value & ~aDiffMask) | (b.imin.value & ~bDiffMask); - result.imax.value = a.imax.value | b.imax.value | getMask(a.imax.value & b.imax.value); - // Sometimes the lower bound is underestimated. The lower bound will never - // less than the input. - if (result.imin.value < a.imin.value) - result.imin.value = a.imin.value; - if (result.imin.value < b.imin.value) - result.imin.value = b.imin.value; - result.imin.negative = result.imax.negative = a.imin.negative || b.imin.negative; - return result; - } - static IntRange unsignedBitwiseXor(const IntRange& a, const IntRange& b) - { - // the DiffMasks stores the mask of bits which are variable in the range. - uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); - uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); - IntRange result; - result.imin.value = (a.imin.value ^ b.imin.value) & ~(aDiffMask | bDiffMask); - result.imax.value = (a.imax.value ^ b.imax.value) | (aDiffMask | bDiffMask); - result.imin.negative = result.imax.negative = a.imin.negative != b.imin.negative; - return result; - } - - public: - IntRange range; - - void visit(Expression *e) - { - range = IntRange::fromType(e->type); - } - - void visit(IntegerExp *e) - { - range = IntRange(SignExtendedNumber(e->getInteger())).cast(e->type); - } - - void visit(CastExp *e) - { - range = getIntRange(e->e1).cast(e->type); - } - - void visit(AddExp *e) - { - IntRange ir1 = getIntRange(e->e1); - IntRange ir2 = getIntRange(e->e2); - range = IntRange(ir1.imin + ir2.imin, ir1.imax + ir2.imax).cast(e->type); - } - - void visit(MinExp *e) - { - IntRange ir1 = getIntRange(e->e1); - IntRange ir2 = getIntRange(e->e2); - range = IntRange(ir1.imin - ir2.imax, ir1.imax - ir2.imin).cast(e->type); - } - - void visit(DivExp *e) - { - IntRange ir1 = getIntRange(e->e1); - IntRange ir2 = getIntRange(e->e2); - - // Should we ignore the possibility of div-by-0??? - if (ir2.containsZero()) - { - visit((Expression *)e); - return; - } - - // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)] - SignExtendedNumber bdy[4]; - bdy[0] = ir1.imin / ir2.imin; - bdy[1] = ir1.imin / ir2.imax; - bdy[2] = ir1.imax / ir2.imin; - bdy[3] = ir1.imax / ir2.imax; - range = IntRange::fromNumbers4(bdy).cast(e->type); - } - - void visit(MulExp *e) - { - IntRange ir1 = getIntRange(e->e1); - IntRange ir2 = getIntRange(e->e2); - - // [a,b] * [c,d] = [min (ac, ad, bc, bd), max (ac, ad, bc, bd)] - SignExtendedNumber bdy[4]; - bdy[0] = ir1.imin * ir2.imin; - bdy[1] = ir1.imin * ir2.imax; - bdy[2] = ir1.imax * ir2.imin; - bdy[3] = ir1.imax * ir2.imax; - range = IntRange::fromNumbers4(bdy).cast(e->type); - } - - void visit(ModExp *e) - { - IntRange irNum = getIntRange(e->e1); - IntRange irDen = getIntRange(e->e2).absNeg(); - - /* - due to the rules of D (C)'s % operator, we need to consider the cases - separately in different range of signs. - - case 1. [500, 1700] % [7, 23] (numerator is always positive) - = [0, 22] - case 2. [-500, 1700] % [7, 23] (numerator can be negative) - = [-22, 22] - case 3. [-1700, -500] % [7, 23] (numerator is always negative) - = [-22, 0] - - the number 22 is the maximum absolute value in the denomator's range. We - don't care about divide by zero. - */ - - // Modding on 0 is invalid anyway. - if (!irDen.imin.negative) - { - visit((Expression *)e); - return; - } - - ++ irDen.imin; - irDen.imax = -irDen.imin; - - if (!irNum.imin.negative) - irNum.imin.value = 0; - else if (irNum.imin < irDen.imin) - irNum.imin = irDen.imin; - - if (irNum.imax.negative) - { - irNum.imax.negative = false; - irNum.imax.value = 0; - } - else if (irNum.imax > irDen.imax) - irNum.imax = irDen.imax; - - range = irNum.cast(e->type); - } - - void visit(AndExp *e) - { - IntRange ir1 = getIntRange(e->e1); - IntRange ir2 = getIntRange(e->e2); - - IntRange ir1neg, ir1pos, ir2neg, ir2pos; - bool has1neg, has1pos, has2neg, has2pos; - - ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); - ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); - - IntRange result; - bool hasResult = false; - if (has1pos && has2pos) - result.unionOrAssign(unsignedBitwiseAnd(ir1pos, ir2pos), hasResult); - if (has1pos && has2neg) - result.unionOrAssign(unsignedBitwiseAnd(ir1pos, ir2neg), hasResult); - if (has1neg && has2pos) - result.unionOrAssign(unsignedBitwiseAnd(ir1neg, ir2pos), hasResult); - if (has1neg && has2neg) - result.unionOrAssign(unsignedBitwiseAnd(ir1neg, ir2neg), hasResult); - assert(hasResult); - range = result.cast(e->type); - } - - void visit(OrExp *e) - { - IntRange ir1 = getIntRange(e->e1); - IntRange ir2 = getIntRange(e->e2); - - IntRange ir1neg, ir1pos, ir2neg, ir2pos; - bool has1neg, has1pos, has2neg, has2pos; - - ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); - ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); - - IntRange result; - bool hasResult = false; - if (has1pos && has2pos) - result.unionOrAssign(unsignedBitwiseOr(ir1pos, ir2pos), hasResult); - if (has1pos && has2neg) - result.unionOrAssign(unsignedBitwiseOr(ir1pos, ir2neg), hasResult); - if (has1neg && has2pos) - result.unionOrAssign(unsignedBitwiseOr(ir1neg, ir2pos), hasResult); - if (has1neg && has2neg) - result.unionOrAssign(unsignedBitwiseOr(ir1neg, ir2neg), hasResult); - - assert(hasResult); - range = result.cast(e->type); - } - - void visit(XorExp *e) - { - IntRange ir1 = getIntRange(e->e1); - IntRange ir2 = getIntRange(e->e2); - - IntRange ir1neg, ir1pos, ir2neg, ir2pos; - bool has1neg, has1pos, has2neg, has2pos; - - ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); - ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); - - IntRange result; - bool hasResult = false; - if (has1pos && has2pos) - result.unionOrAssign(unsignedBitwiseXor(ir1pos, ir2pos), hasResult); - if (has1pos && has2neg) - result.unionOrAssign(unsignedBitwiseXor(ir1pos, ir2neg), hasResult); - if (has1neg && has2pos) - result.unionOrAssign(unsignedBitwiseXor(ir1neg, ir2pos), hasResult); - if (has1neg && has2neg) - result.unionOrAssign(unsignedBitwiseXor(ir1neg, ir2neg), hasResult); - - assert(hasResult); - range = result.cast(e->type); - } - - void visit(ShlExp *e) - { - IntRange ir1 = getIntRange(e->e1); - IntRange ir2 = getIntRange(e->e2); - - if (ir2.imin.negative) - ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); - - SignExtendedNumber lower = ir1.imin << (ir1.imin.negative ? ir2.imax : ir2.imin); - SignExtendedNumber upper = ir1.imax << (ir1.imax.negative ? ir2.imin : ir2.imax); - - range = IntRange(lower, upper).cast(e->type); - } - - void visit(ShrExp *e) - { - IntRange ir1 = getIntRange(e->e1); - IntRange ir2 = getIntRange(e->e2); - - if (ir2.imin.negative) - ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); - - SignExtendedNumber lower = ir1.imin >> (ir1.imin.negative ? ir2.imin : ir2.imax); - SignExtendedNumber upper = ir1.imax >> (ir1.imax.negative ? ir2.imax : ir2.imin); - - range = IntRange(lower, upper).cast(e->type); - } - - void visit(UshrExp *e) - { - IntRange ir1 = getIntRange(e->e1).castUnsigned(e->e1->type); - IntRange ir2 = getIntRange(e->e2); - - if (ir2.imin.negative) - ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); - - range = IntRange(ir1.imin >> ir2.imax, ir1.imax >> ir2.imin).cast(e->type); - } - - void visit(AssignExp *e) - { - range = getIntRange(e->e2).cast(e->type); - } - - void visit(CondExp *e) - { - // No need to check e->econd; assume caller has called optimize() - IntRange ir1 = getIntRange(e->e1); - IntRange ir2 = getIntRange(e->e2); - range = ir1.unionWith(ir2).cast(e->type); - } - - void visit(VarExp *e) - { - Expression *ie; - VarDeclaration* vd = e->var->isVarDeclaration(); - if (vd && vd->range) - range = vd->range->cast(e->type); - else if (vd && vd->init && !vd->type->isMutable() && - (ie = vd->getConstInitializer()) != NULL) - ie->accept(this); - else - visit((Expression *)e); - } - - void visit(CommaExp *e) - { - e->e2->accept(this); - } - - void visit(ComExp *e) - { - IntRange ir = getIntRange(e->e1); - range = IntRange(SignExtendedNumber(~ir.imax.value, !ir.imax.negative), - SignExtendedNumber(~ir.imin.value, !ir.imin.negative)).cast(e->type); - } - - void visit(NegExp *e) - { - IntRange ir = getIntRange(e->e1); - range = IntRange(-ir.imax, -ir.imin).cast(e->type); - } - }; - - IntRangeVisitor v; - e->accept(&v); - return v.range; -} diff --git a/src/class.c b/src/class.c deleted file mode 100644 index 61db2d0de316..000000000000 --- a/src/class.c +++ /dev/null @@ -1,1855 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/class.c - */ - -#include -#include -#include -#include // mem{cpy|set}() - -#include "root.h" -#include "rmem.h" -#include "target.h" - -#include "enum.h" -#include "init.h" -#include "attrib.h" -#include "declaration.h" -#include "aggregate.h" -#include "id.h" -#include "mtype.h" -#include "scope.h" -#include "module.h" -#include "expression.h" -#include "statement.h" -#include "template.h" -#include "objc.h" - -/********************************* ClassDeclaration ****************************/ - -ClassDeclaration *ClassDeclaration::object; -ClassDeclaration *ClassDeclaration::throwable; -ClassDeclaration *ClassDeclaration::exception; -ClassDeclaration *ClassDeclaration::errorException; - -ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses, bool inObject) - : AggregateDeclaration(loc, id) -{ - static const char msg[] = "only object.d can define this reserved class name"; - - if (baseclasses) - { - // Actually, this is a transfer - this->baseclasses = baseclasses; - } - else - this->baseclasses = new BaseClasses(); - baseClass = NULL; - - interfaces_dim = 0; - interfaces = NULL; - - vtblInterfaces = NULL; - - //printf("ClassDeclaration(%s), dim = %d\n", id->toChars(), this->baseclasses->dim); - - // For forward references - type = new TypeClass(this); - - staticCtor = NULL; - staticDtor = NULL; - - vtblsym = NULL; - vclassinfo = NULL; - - if (id) - { - // Look for special class names - - if (id == Id::__sizeof || id == Id::__xalignof || id == Id::_mangleof) - error("illegal class name"); - - // BUG: What if this is the wrong TypeInfo, i.e. it is nested? - if (id->toChars()[0] == 'T') - { - if (id == Id::TypeInfo) - { - if (!inObject) - error("%s", msg); - Type::dtypeinfo = this; - } - - if (id == Id::TypeInfo_Class) - { - if (!inObject) - error("%s", msg); - Type::typeinfoclass = this; - } - - if (id == Id::TypeInfo_Interface) - { - if (!inObject) - error("%s", msg); - Type::typeinfointerface = this; - } - - if (id == Id::TypeInfo_Struct) - { - if (!inObject) - error("%s", msg); - Type::typeinfostruct = this; - } - - if (id == Id::TypeInfo_Pointer) - { - if (!inObject) - error("%s", msg); - Type::typeinfopointer = this; - } - - if (id == Id::TypeInfo_Array) - { - if (!inObject) - error("%s", msg); - Type::typeinfoarray = this; - } - - if (id == Id::TypeInfo_StaticArray) - { - //if (!inObject) - // Type::typeinfostaticarray->error("%s", msg); - Type::typeinfostaticarray = this; - } - - if (id == Id::TypeInfo_AssociativeArray) - { - if (!inObject) - error("%s", msg); - Type::typeinfoassociativearray = this; - } - - if (id == Id::TypeInfo_Enum) - { - if (!inObject) - error("%s", msg); - Type::typeinfoenum = this; - } - - if (id == Id::TypeInfo_Function) - { - if (!inObject) - error("%s", msg); - Type::typeinfofunction = this; - } - - if (id == Id::TypeInfo_Delegate) - { - if (!inObject) - error("%s", msg); - Type::typeinfodelegate = this; - } - - if (id == Id::TypeInfo_Tuple) - { - if (!inObject) - error("%s", msg); - Type::typeinfotypelist = this; - } - - if (id == Id::TypeInfo_Const) - { - if (!inObject) - error("%s", msg); - Type::typeinfoconst = this; - } - - if (id == Id::TypeInfo_Invariant) - { - if (!inObject) - error("%s", msg); - Type::typeinfoinvariant = this; - } - - if (id == Id::TypeInfo_Shared) - { - if (!inObject) - error("%s", msg); - Type::typeinfoshared = this; - } - - if (id == Id::TypeInfo_Wild) - { - if (!inObject) - error("%s", msg); - Type::typeinfowild = this; - } - - if (id == Id::TypeInfo_Vector) - { - if (!inObject) - error("%s", msg); - Type::typeinfovector = this; - } - } - - if (id == Id::Object) - { - if (!inObject) - error("%s", msg); - object = this; - } - - if (id == Id::Throwable) - { - if (!inObject) - error("%s", msg); - throwable = this; - } - - if (id == Id::Exception) - { - if (!inObject) - error("%s", msg); - exception = this; - } - - if (id == Id::Error) - { - if (!inObject) - error("%s", msg); - errorException = this; - } - } - - com = false; - cpp = false; - isscope = false; - isabstract = false; - inuse = 0; - baseok = BASEOKnone; - objc.objc = false; -} - -Dsymbol *ClassDeclaration::syntaxCopy(Dsymbol *s) -{ - //printf("ClassDeclaration::syntaxCopy('%s')\n", toChars()); - ClassDeclaration *cd = - s ? (ClassDeclaration *)s - : new ClassDeclaration(loc, ident, NULL); - - cd->storage_class |= storage_class; - - cd->baseclasses->setDim(this->baseclasses->dim); - for (size_t i = 0; i < cd->baseclasses->dim; i++) - { - BaseClass *b = (*this->baseclasses)[i]; - BaseClass *b2 = new BaseClass(b->type->syntaxCopy(), b->protection); - (*cd->baseclasses)[i] = b2; - } - - return ScopeDsymbol::syntaxCopy(cd); -} - -void ClassDeclaration::semantic(Scope *sc) -{ - //printf("ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); - //printf("\tparent = %p, '%s'\n", sc->parent, sc->parent ? sc->parent->toChars() : ""); - //printf("sc->stc = %x\n", sc->stc); - - //{ static int n; if (++n == 20) *(char*)0=0; } - - if (semanticRun >= PASSsemanticdone) - return; - unsigned dprogress_save = Module::dprogress; - int errors = global.errors; - - //printf("+ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); - - Scope *scx = NULL; - if (scope) - { - sc = scope; - scx = scope; // save so we don't make redundant copies - scope = NULL; - } - - if (!parent) - { - assert(sc->parent && (sc->func || !ident)); - parent = sc->parent; - - if (!ident) // if anonymous class - { - const char *id = "__anonclass"; - ident = Identifier::generateId(id); - } - } - assert(parent && !isAnonymous()); - type = type->semantic(loc, sc); - - if (type->ty == Tclass && ((TypeClass *)type)->sym != this) - { - TemplateInstance *ti = ((TypeClass *)type)->sym->isInstantiated(); - if (ti && isError(ti)) - ((TypeClass *)type)->sym = this; - } - - // Ungag errors when not speculative - Ungag ungag = ungagSpeculative(); - - if (semanticRun == PASSinit) - { - protection = sc->protection; - - storage_class |= sc->stc; - if (storage_class & STCdeprecated) - isdeprecated = true; - if (storage_class & STCauto) - error("storage class 'auto' is invalid when declaring a class, did you mean to use 'scope'?"); - if (storage_class & STCscope) - isscope = true; - if (storage_class & STCabstract) - isabstract = true; - - userAttribDecl = sc->userAttribDecl; - - if (sc->linkage == LINKcpp) - cpp = true; - if (sc->linkage == LINKobjc) - objc_ClassDeclaration_semantic_PASSinit_LINKobjc(this); - } - else if (symtab && !scx) - { - semanticRun = PASSsemanticdone; - return; - } - semanticRun = PASSsemantic; - - if (baseok < BASEOKdone) - { - baseok = BASEOKin; - - // Expand any tuples in baseclasses[] - for (size_t i = 0; i < baseclasses->dim; ) - { - scope = scx ? scx : sc->copy(); - scope->setNoFree(); - - BaseClass *b = (*baseclasses)[i]; - //printf("+ %s [%d] b->type = %s\n", toChars(), i, b->type->toChars()); - b->type = b->type->semantic(loc, sc); - //printf("- %s [%d] b->type = %s\n", toChars(), i, b->type->toChars()); - - scope = NULL; - - Type *tb = b->type->toBasetype(); - if (tb->ty == Ttuple) - { - TypeTuple *tup = (TypeTuple *)tb; - Prot protection = b->protection; - baseclasses->remove(i); - size_t dim = Parameter::dim(tup->arguments); - for (size_t j = 0; j < dim; j++) - { - Parameter *arg = Parameter::getNth(tup->arguments, j); - b = new BaseClass(arg->type, protection); - baseclasses->insert(i + j, b); - } - } - else - i++; - } - - if (baseok >= BASEOKdone) - { - //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun); - if (semanticRun >= PASSsemanticdone) - return; - goto Lancestorsdone; - } - - // See if there's a base class as first in baseclasses[] - if (baseclasses->dim) - { - BaseClass *b = (*baseclasses)[0]; - Type *tb = b->type->toBasetype(); - TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; - if (!tc) - { - if (b->type != Type::terror) - error("base type must be class or interface, not %s", b->type->toChars()); - baseclasses->remove(0); - goto L7; - } - - if (tc->sym->isDeprecated()) - { - if (!isDeprecated()) - { - // Deriving from deprecated class makes this one deprecated too - isdeprecated = true; - - tc->checkDeprecated(loc, sc); - } - } - - if (tc->sym->isInterfaceDeclaration()) - goto L7; - - for (ClassDeclaration *cdb = tc->sym; cdb; cdb = cdb->baseClass) - { - if (cdb == this) - { - error("circular inheritance"); - baseclasses->remove(0); - goto L7; - } - } - - /* Bugzilla 11034: Essentially, class inheritance hierarchy - * and instance size of each classes are orthogonal information. - * Therefore, even if tc->sym->sizeof == SIZEOKnone, - * we need to set baseClass field for class covariance check. - */ - baseClass = tc->sym; - b->sym = baseClass; - - if (tc->sym->scope && tc->sym->baseok < BASEOKdone) - tc->sym->semantic(NULL); // Try to resolve forward reference - if (tc->sym->baseok < BASEOKdone) - { - //printf("\ttry later, forward reference of base class %s\n", tc->sym->toChars()); - if (tc->sym->scope) - tc->sym->scope->module->addDeferredSemantic(tc->sym); - baseok = BASEOKnone; - } - L7: ; - } - - // Treat the remaining entries in baseclasses as interfaces - // Check for errors, handle forward references - for (size_t i = (baseClass ? 1 : 0); i < baseclasses->dim; ) - { - BaseClass *b = (*baseclasses)[i]; - Type *tb = b->type->toBasetype(); - TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; - if (!tc || !tc->sym->isInterfaceDeclaration()) - { - if (b->type != Type::terror) - error("base type must be interface, not %s", b->type->toChars()); - baseclasses->remove(i); - continue; - } - - // Check for duplicate interfaces - for (size_t j = (baseClass ? 1 : 0); j < i; j++) - { - BaseClass *b2 = (*baseclasses)[j]; - if (b2->sym == tc->sym) - { - error("inherits from duplicate interface %s", b2->sym->toChars()); - baseclasses->remove(i); - continue; - } - } - - if (tc->sym->isDeprecated()) - { - if (!isDeprecated()) - { - // Deriving from deprecated class makes this one deprecated too - isdeprecated = true; - - tc->checkDeprecated(loc, sc); - } - } - - b->sym = tc->sym; - - if (tc->sym->scope && tc->sym->baseok < BASEOKdone) - tc->sym->semantic(NULL); // Try to resolve forward reference - if (tc->sym->baseok < BASEOKdone) - { - //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars()); - if (tc->sym->scope) - tc->sym->scope->module->addDeferredSemantic(tc->sym); - baseok = BASEOKnone; - } - i++; - } - if (baseok == BASEOKnone) - { - // Forward referencee of one or more bases, try again later - scope = scx ? scx : sc->copy(); - scope->setNoFree(); - scope->module->addDeferredSemantic(this); - //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars()); - return; - } - baseok = BASEOKdone; - - // If no base class, and this is not an Object, use Object as base class - if (!baseClass && ident != Id::Object && !cpp) - { - if (!object) - { - error("missing or corrupt object.d"); - fatal(); - } - - Type *t = object->type; - t = t->semantic(loc, sc)->toBasetype(); - assert(t->ty == Tclass); - TypeClass *tc = (TypeClass *)t; - - BaseClass *b = new BaseClass(tc, Prot(PROTpublic)); - baseclasses->shift(b); - - baseClass = tc->sym; - assert(!baseClass->isInterfaceDeclaration()); - b->sym = baseClass; - } - if (baseClass) - { - if (baseClass->storage_class & STCfinal) - error("cannot inherit from final class %s", baseClass->toChars()); - - // Inherit properties from base class - if (baseClass->isCOMclass()) - com = true; - if (baseClass->isCPPclass()) - cpp = true; - if (baseClass->isscope) - isscope = true; - enclosing = baseClass->enclosing; - storage_class |= baseClass->storage_class & STC_TYPECTOR; - } - - interfaces_dim = baseclasses->dim - (baseClass ? 1 : 0); - interfaces = baseclasses->tdata() + (baseClass ? 1 : 0); - - for (size_t i = 0; i < interfaces_dim; i++) - { - BaseClass *b = interfaces[i]; - // If this is an interface, and it derives from a COM interface, - // then this is a COM interface too. - if (b->sym->isCOMinterface()) - com = true; - if (cpp && !b->sym->isCPPinterface()) - { - ::error(loc, "C++ class '%s' cannot implement D interface '%s'", toPrettyChars(), b->sym->toPrettyChars()); - } - } - - interfaceSemantic(sc); - } -Lancestorsdone: - //printf("\tClassDeclaration::semantic(%s) baseok = %d\n", toChars(), baseok); - - if (!members) // if opaque declaration - { - semanticRun = PASSsemanticdone; - return; - } - if (!symtab) - { - symtab = new DsymbolTable(); - - /* Bugzilla 12152: The semantic analysis of base classes should be finished - * before the members semantic analysis of this class, in order to determine - * vtbl in this class. However if a base class refers the member of this class, - * it can be resolved as a normal forward reference. - * Call addMember() and setScope() to make this class members visible from the base classes. - */ - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - s->addMember(sc, this); - } - - Scope *sc2 = sc->push(this); - sc2->stc &= STCsafe | STCtrusted | STCsystem; - sc2->parent = this; - sc2->inunion = 0; - if (isCOMclass()) - { - if (global.params.isWindows) - sc2->linkage = LINKwindows; - else - sc2->linkage = LINKc; - } - sc2->protection = Prot(PROTpublic); - sc2->explicitProtection = 0; - sc2->structalign = STRUCTALIGN_DEFAULT; - sc2->userAttribDecl = NULL; - - /* Set scope so if there are forward references, we still might be able to - * resolve individual members like enums. - */ - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - //printf("[%d] setScope %s %s, sc2 = %p\n", i, s->kind(), s->toChars(), sc2); - s->setScope(sc2); - } - - sc2->pop(); - } - - for (size_t i = 0; i < baseclasses->dim; i++) - { - BaseClass *b = (*baseclasses)[i]; - Type *tb = b->type->toBasetype(); - assert(tb->ty == Tclass); - TypeClass *tc = (TypeClass *)tb; - - if (tc->sym->semanticRun < PASSsemanticdone) - { - // Forward referencee of one or more bases, try again later - scope = scx ? scx : sc->copy(); - scope->setNoFree(); - if (tc->sym->scope) - tc->sym->scope->module->addDeferredSemantic(tc->sym); - scope->module->addDeferredSemantic(this); - //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars()); - return; - } - } - - if (baseok == BASEOKdone) - { - baseok = BASEOKsemanticdone; - - // initialize vtbl - if (baseClass) - { - // Copy vtbl[] from base class - vtbl.setDim(baseClass->vtbl.dim); - memcpy(vtbl.tdata(), baseClass->vtbl.tdata(), sizeof(void *) * vtbl.dim); - - vthis = baseClass->vthis; - } - else - { - // No base class, so this is the root of the class hierarchy - vtbl.setDim(0); - if (vtblOffset()) - vtbl.push(this); // leave room for classinfo as first member - } - - /* If this is a nested class, add the hidden 'this' - * member which is a pointer to the enclosing scope. - */ - if (vthis) // if inheriting from nested class - { - // Use the base class's 'this' member - if (storage_class & STCstatic) - error("static class cannot inherit from nested class %s", baseClass->toChars()); - if (toParent2() != baseClass->toParent2() && - (!toParent2() || - !baseClass->toParent2()->getType() || - !baseClass->toParent2()->getType()->isBaseOf(toParent2()->getType(), NULL))) - { - if (toParent2()) - { - error("is nested within %s, but super class %s is nested within %s", - toParent2()->toChars(), - baseClass->toChars(), - baseClass->toParent2()->toChars()); - } - else - { - error("is not nested, but super class %s is nested within %s", - baseClass->toChars(), - baseClass->toParent2()->toChars()); - } - enclosing = NULL; - } - } - else - makeNested(); - } - - // it might be determined already, by AggregateDeclaration::size(). - if (sizeok != SIZEOKdone) - sizeok = SIZEOKnone; - - Scope *sc2 = sc->push(this); - //sc2->stc &= ~(STCfinal | STCauto | STCscope | STCstatic | STCabstract | STCdeprecated | STC_TYPECTOR | STCtls | STCgshared); - //sc2->stc |= storage_class & STC_TYPECTOR; - sc2->stc &= STCsafe | STCtrusted | STCsystem; - sc2->parent = this; - sc2->inunion = 0; - if (isCOMclass()) - { - if (global.params.isWindows) - sc2->linkage = LINKwindows; - else - { - /* This enables us to use COM objects under Linux and - * work with things like XPCOM - */ - sc2->linkage = LINKc; - } - } - sc2->protection = Prot(PROTpublic); - sc2->explicitProtection = 0; - sc2->structalign = STRUCTALIGN_DEFAULT; - sc2->userAttribDecl = NULL; - - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - s->importAll(sc2); - } - - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - s->semantic(sc2); - } - finalizeSize(sc2); - - if (sizeok == SIZEOKfwd) - { - // semantic() failed due to forward references - // Unwind what we did, and defer it for later - for (size_t i = 0; i < fields.dim; i++) - { - VarDeclaration *v = fields[i]; - v->offset = 0; - } - fields.setDim(0); - structsize = 0; - alignsize = 0; - - sc2->pop(); - - scope = scx ? scx : sc->copy(); - scope->setNoFree(); - scope->module->addDeferredSemantic(this); - - Module::dprogress = dprogress_save; - //printf("\tsemantic('%s') failed due to forward references\n", toChars()); - return; - } - - Module::dprogress++; - semanticRun = PASSsemanticdone; - - //printf("-ClassDeclaration::semantic(%s), type = %p\n", toChars(), type); - //members->print(); - - /* Look for special member functions. - * They must be in this class, not in a base class. - */ - - // Can be in base class - aggNew = (NewDeclaration *)search(Loc(), Id::classNew); - aggDelete = (DeleteDeclaration *)search(Loc(), Id::classDelete); - - // this->ctor is already set in finalizeSize() - - if (!ctor && noDefaultCtor) - { - // A class object is always created by constructor, so this check is legitimate. - for (size_t i = 0; i < fields.dim; i++) - { - VarDeclaration *v = fields[i]; - if (v->storage_class & STCnodefaultctor) - ::error(v->loc, "field %s must be initialized in constructor", v->toChars()); - } - } - - // If this class has no constructor, but base class has a default - // ctor, create a constructor: - // this() { } - if (!ctor && baseClass && baseClass->ctor) - { - FuncDeclaration *fd = resolveFuncCall(loc, sc2, baseClass->ctor, NULL, NULL, NULL, 1); - if (fd && !fd->errors) - { - //printf("Creating default this(){} for class %s\n", toChars()); - TypeFunction *btf = (TypeFunction *)fd->type; - TypeFunction *tf = new TypeFunction(NULL, NULL, 0, LINKd, fd->storage_class); - tf->purity = btf->purity; - tf->isnothrow = btf->isnothrow; - tf->isnogc = btf->isnogc; - tf->trust = btf->trust; - CtorDeclaration *ctor = new CtorDeclaration(loc, Loc(), 0, tf); - ctor->fbody = new CompoundStatement(Loc(), new Statements()); - members->push(ctor); - ctor->addMember(sc, this); - ctor->semantic(sc2); - this->ctor = ctor; - defaultCtor = ctor; - } - else - { - error("cannot implicitly generate a default ctor when base class %s is missing a default ctor", baseClass->toPrettyChars()); - } - } - - dtor = buildDtor(this, sc2); - - if (FuncDeclaration *f = hasIdentityOpAssign(this, sc2)) - { - if (!(f->storage_class & STCdisable)) - error(f->loc, "identity assignment operator overload is illegal"); - } - - inv = buildInv(this, sc2); - - sc2->pop(); - - if (global.errors != errors) - { - // The type is no good. - type = Type::terror; - this->errors = true; - if (deferred) - deferred->errors = true; - } - - if (deferred && !global.gag) - { - deferred->semantic2(sc); - deferred->semantic3(sc); - } - -#if 0 - if (type->ty == Tclass && ((TypeClass *)type)->sym != this) - { - printf("this = %p %s\n", this, this->toChars()); - printf("type = %d sym = %p\n", type->ty, ((TypeClass *)type)->sym); - } -#endif - assert(type->ty != Tclass || ((TypeClass *)type)->sym == this); - - //printf("-ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); -} - -/********************************************* - * Determine if 'this' is a base class of cd. - * This is used to detect circular inheritance only. - */ - -bool ClassDeclaration::isBaseOf2(ClassDeclaration *cd) -{ - if (!cd) - return false; - //printf("ClassDeclaration::isBaseOf2(this = '%s', cd = '%s')\n", toChars(), cd->toChars()); - for (size_t i = 0; i < cd->baseclasses->dim; i++) - { - BaseClass *b = (*cd->baseclasses)[i]; - if (b->sym == this || isBaseOf2(b->sym)) - return true; - } - return false; -} - -/******************************************* - * Determine if 'this' is a base class of cd. - */ - -bool ClassDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) -{ - //printf("ClassDeclaration::isBaseOf(this = '%s', cd = '%s')\n", toChars(), cd->toChars()); - if (poffset) - *poffset = 0; - while (cd) - { - /* cd->baseClass might not be set if cd is forward referenced. - */ - if (!cd->baseClass && cd->scope && !cd->isInterfaceDeclaration()) - { - cd->semantic(NULL); - if (!cd->baseClass && cd->scope) - cd->error("base class is forward referenced by %s", toChars()); - } - - if (this == cd->baseClass) - return true; - - cd = cd->baseClass; - } - return false; -} - -/********************************************* - * Determine if 'this' has complete base class information. - * This is used to detect forward references in covariant overloads. - */ - -bool ClassDeclaration::isBaseInfoComplete() -{ - return baseok >= BASEOKdone; -} - -Dsymbol *ClassDeclaration::search(Loc loc, Identifier *ident, int flags) -{ - //printf("%s.ClassDeclaration::search('%s')\n", toChars(), ident->toChars()); - - //if (scope) printf("%s baseok = %d\n", toChars(), baseok); - if (scope && baseok < BASEOKdone) - { - if (!inuse) - { - // must semantic on base class/interfaces - ++inuse; - semantic(NULL); - --inuse; - } - } - - if (!members || !symtab) // opaque or addMember is not yet done - { - error("is forward referenced when looking for '%s'", ident->toChars()); - //*(char*)0=0; - return NULL; - } - - Dsymbol *s = ScopeDsymbol::search(loc, ident, flags); - if (!s) - { - // Search bases classes in depth-first, left to right order - - for (size_t i = 0; i < baseclasses->dim; i++) - { - BaseClass *b = (*baseclasses)[i]; - - if (b->sym) - { - if (!b->sym->symtab) - error("base %s is forward referenced", b->sym->ident->toChars()); - else - { - s = b->sym->search(loc, ident, flags); - if (s == this) // happens if s is nested in this and derives from this - s = NULL; - else if (s) - break; - } - } - } - } - return s; -} - -ClassDeclaration *ClassDeclaration::searchBase(Loc loc, Identifier *ident) -{ - // Search bases classes in depth-first, left to right order - - for (size_t i = 0; i < baseclasses->dim; i++) - { - BaseClass *b = (*baseclasses)[i]; - ClassDeclaration *cdb = b->type->isClassHandle(); - if (!cdb) // Bugzilla 10616 - return NULL; - if (cdb->ident->equals(ident)) - return cdb; - cdb = cdb->searchBase(loc, ident); - if (cdb) - return cdb; - } - return NULL; -} - -void ClassDeclaration::finalizeSize(Scope *sc) -{ - if (sizeok != SIZEOKnone) - return; - - // Set the offsets of the fields and determine the size of the class - if (baseClass) - { - assert(baseClass->sizeok == SIZEOKdone); - - alignsize = baseClass->alignsize; - structsize = baseClass->structsize; - if (cpp && global.params.isWindows) - structsize = (structsize + alignsize - 1) & ~(alignsize - 1); - } - else - { - alignsize = Target::ptrsize; - if (cpp) - structsize = Target::ptrsize; // allow room for __vptr - else - structsize = Target::ptrsize * 2; // allow room for __vptr and __monitor - } - - unsigned offset = structsize; - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - s->setFieldOffset(this, &offset, false); - } - if (sizeok == SIZEOKfwd) - return; - - // Add vptr's for any interfaces implemented by this class - structsize += setBaseInterfaceOffsets(structsize); - sizeok = SIZEOKdone; - - // Look for the constructor - ctor = searchCtor(); - if (ctor && ctor->toParent() != this) - ctor = NULL; // search() looks through ancestor classes - if (ctor) - { - // Finish all constructors semantics to determine this->noDefaultCtor. - struct SearchCtor - { - static int fp(Dsymbol *s, void *ctxt) - { - CtorDeclaration *f = s->isCtorDeclaration(); - if (f && f->semanticRun == PASSinit) - f->semantic(NULL); - return 0; - } - }; - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - s->apply(&SearchCtor::fp, NULL); - } - } -} - -/********************************************************** - * fd is in the vtbl[] for this class. - * Return 1 if function is hidden (not findable through search). - */ - -int isf(void *param, Dsymbol *s) -{ - FuncDeclaration *fd = s->isFuncDeclaration(); - if (!fd) - return 0; - //printf("param = %p, fd = %p %s\n", param, fd, fd->toChars()); - return (RootObject *)param == fd; -} - -bool ClassDeclaration::isFuncHidden(FuncDeclaration *fd) -{ - //printf("ClassDeclaration::isFuncHidden(class = %s, fd = %s)\n", toChars(), fd->toChars()); - Dsymbol *s = search(Loc(), fd->ident, IgnoreAmbiguous | IgnoreErrors); - if (!s) - { - //printf("not found\n"); - /* Because, due to a hack, if there are multiple definitions - * of fd->ident, NULL is returned. - */ - return false; - } - s = s->toAlias(); - OverloadSet *os = s->isOverloadSet(); - if (os) - { - for (size_t i = 0; i < os->a.dim; i++) - { - Dsymbol *s2 = os->a[i]; - FuncDeclaration *f2 = s2->isFuncDeclaration(); - if (f2 && overloadApply(f2, (void *)fd, &isf)) - return false; - } - return true; - } - else - { - FuncDeclaration *fdstart = s->isFuncDeclaration(); - //printf("%s fdstart = %p\n", s->kind(), fdstart); - if (overloadApply(fdstart, (void *)fd, &isf)) - return false; - - return !fd->parent->isTemplateMixin(); - } -} - -/**************** - * Find virtual function matching identifier and type. - * Used to build virtual function tables for interface implementations. - */ - -FuncDeclaration *ClassDeclaration::findFunc(Identifier *ident, TypeFunction *tf) -{ - //printf("ClassDeclaration::findFunc(%s, %s) %s\n", ident->toChars(), tf->toChars(), toChars()); - FuncDeclaration *fdmatch = NULL; - FuncDeclaration *fdambig = NULL; - - ClassDeclaration *cd = this; - Dsymbols *vtbl = &cd->vtbl; - while (1) - { - for (size_t i = 0; i < vtbl->dim; i++) - { - FuncDeclaration *fd = (*vtbl)[i]->isFuncDeclaration(); - if (!fd) - continue; // the first entry might be a ClassInfo - - //printf("\t[%d] = %s\n", i, fd->toChars()); - if (ident == fd->ident && - fd->type->covariant(tf) == 1) - { - //printf("fd->parent->isClassDeclaration() = %p\n", fd->parent->isClassDeclaration()); - if (!fdmatch) - goto Lfd; - if (fd == fdmatch) - goto Lfdmatch; - - { - // Function type matcing: exact > covariant - MATCH m1 = tf->equals(fd ->type) ? MATCHexact : MATCHnomatch; - MATCH m2 = tf->equals(fdmatch->type) ? MATCHexact : MATCHnomatch; - if (m1 > m2) - goto Lfd; - else if (m1 < m2) - goto Lfdmatch; - } - - { - MATCH m1 = (tf->mod == fd ->type->mod) ? MATCHexact : MATCHnomatch; - MATCH m2 = (tf->mod == fdmatch->type->mod) ? MATCHexact : MATCHnomatch; - if (m1 > m2) - goto Lfd; - else if (m1 < m2) - goto Lfdmatch; - } - - { - // The way of definition: non-mixin > mixin - MATCH m1 = fd ->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch; - MATCH m2 = fdmatch->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch; - if (m1 > m2) - goto Lfd; - else if (m1 < m2) - goto Lfdmatch; - } - - fdambig = fd; - //printf("Lambig fdambig = %s %s [%s]\n", fdambig->toChars(), fdambig->type->toChars(), fdambig->loc.toChars()); - continue; - - Lfd: - fdmatch = fd, fdambig = NULL; - //printf("Lfd fdmatch = %s %s [%s]\n", fdmatch->toChars(), fdmatch->type->toChars(), fdmatch->loc.toChars()); - continue; - - Lfdmatch: - continue; - } - //else printf("\t\t%d\n", fd->type->covariant(tf)); - } - if (!cd) - break; - vtbl = &cd->vtblFinal; - cd = cd->baseClass; - } - - if (fdambig) - error("ambiguous virtual function %s", fdambig->toChars()); - return fdmatch; -} - -void ClassDeclaration::interfaceSemantic(Scope *sc) -{ - vtblInterfaces = new BaseClasses(); - vtblInterfaces->reserve(interfaces_dim); - - for (size_t i = 0; i < interfaces_dim; i++) - { - BaseClass *b = interfaces[i]; - vtblInterfaces->push(b); - b->copyBaseInterfaces(vtblInterfaces); - } -} - -unsigned ClassDeclaration::setBaseInterfaceOffsets(unsigned baseOffset) -{ - assert(vtblInterfaces); // Bugzilla 12984 - - // set the offset of base interfaces from this (most derived) class/interface. - unsigned offset = baseOffset; - - //if (vtblInterfaces->dim) printf("\n%s->finalizeSize()\n", toChars()); - for (size_t i = 0; i < vtblInterfaces->dim; i++) - { - BaseClass *b = (*vtblInterfaces)[i]; - unsigned thissize = Target::ptrsize; - - alignmember(STRUCTALIGN_DEFAULT, thissize, &offset); - b->offset = offset; - //printf("\tvtblInterfaces[%d] b->sym = %s, offset = %d\n", i, b->sym->toChars(), b->offset); - - // Take care of single inheritance offsets - while (b->baseInterfaces_dim) - { - b = &b->baseInterfaces[0]; - b->offset = offset; - //printf("\tvtblInterfaces[%d] + sym = %s, offset = %d\n", i, b->sym->toChars(), b->offset); - } - - offset += thissize; - if (alignsize < thissize) - alignsize = thissize; - } - - return offset - baseOffset; -} - -/**************************************** - */ - -bool ClassDeclaration::isCOMclass() -{ - return com; -} - -bool ClassDeclaration::isCOMinterface() -{ - return false; -} - -bool ClassDeclaration::isCPPclass() -{ - return cpp; -} - -bool ClassDeclaration::isCPPinterface() -{ - return false; -} - - -/**************************************** - */ - -bool ClassDeclaration::isAbstract() -{ - if (isabstract) - return true; - for (size_t i = 1; i < vtbl.dim; i++) - { - FuncDeclaration *fd = vtbl[i]->isFuncDeclaration(); - - //printf("\tvtbl[%d] = %p\n", i, fd); - if (!fd || fd->isAbstract()) - { - isabstract = true; - return true; - } - } - return false; -} - - -/**************************************** - * Determine if slot 0 of the vtbl[] is reserved for something else. - * For class objects, yes, this is where the classinfo ptr goes. - * For COM interfaces, no. - * For non-COM interfaces, yes, this is where the Interface ptr goes. - * Returns: - * 0 vtbl[0] is first virtual function pointer - * 1 vtbl[0] is classinfo/interfaceinfo pointer - */ - -int ClassDeclaration::vtblOffset() -{ - return cpp ? 0 : 1; -} - -/**************************************** - */ - -const char *ClassDeclaration::kind() -{ - return "class"; -} - -/**************************************** - */ - -void ClassDeclaration::addLocalClass(ClassDeclarations *aclasses) -{ - aclasses->push(this); -} - -/********************************* InterfaceDeclaration ****************************/ - -InterfaceDeclaration::InterfaceDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses) - : ClassDeclaration(loc, id, baseclasses) -{ - if (id == Id::IUnknown) // IUnknown is the root of all COM interfaces - { - com = true; - cpp = true; // IUnknown is also a C++ interface - } -} - -Dsymbol *InterfaceDeclaration::syntaxCopy(Dsymbol *s) -{ - InterfaceDeclaration *id = - s ? (InterfaceDeclaration *)s - : new InterfaceDeclaration(loc, ident, NULL); - return ClassDeclaration::syntaxCopy(id); -} - -void InterfaceDeclaration::semantic(Scope *sc) -{ - //printf("InterfaceDeclaration::semantic(%s), type = %p\n", toChars(), type); - if (semanticRun >= PASSsemanticdone) - return; - int errors = global.errors; - - Scope *scx = NULL; - if (scope) - { - sc = scope; - scx = scope; // save so we don't make redundant copies - scope = NULL; - } - - if (!parent) - { - assert(sc->parent && sc->func); - parent = sc->parent; - } - assert(parent && !isAnonymous()); - type = type->semantic(loc, sc); - - if (type->ty == Tclass && ((TypeClass *)type)->sym != this) - { - TemplateInstance *ti = ((TypeClass *)type)->sym->isInstantiated(); - if (ti && isError(ti)) - ((TypeClass *)type)->sym = this; - } - - // Ungag errors when not speculative - Ungag ungag = ungagSpeculative(); - - if (semanticRun == PASSinit) - { - protection = sc->protection; - - storage_class |= sc->stc; - if (storage_class & STCdeprecated) - isdeprecated = true; - - userAttribDecl = sc->userAttribDecl; - } - else if (symtab) - { - if (sizeok == SIZEOKdone || !scx) - { - semanticRun = PASSsemanticdone; - return; - } - } - semanticRun = PASSsemantic; - - if (baseok < BASEOKdone) - { - baseok = BASEOKin; - - // Expand any tuples in baseclasses[] - for (size_t i = 0; i < baseclasses->dim; ) - { - scope = scx ? scx : sc->copy(); - scope->setNoFree(); - - BaseClass *b = (*baseclasses)[i]; - b->type = b->type->semantic(loc, sc); - - scope = NULL; - - Type *tb = b->type->toBasetype(); - if (tb->ty == Ttuple) - { - TypeTuple *tup = (TypeTuple *)tb; - Prot protection = b->protection; - baseclasses->remove(i); - size_t dim = Parameter::dim(tup->arguments); - for (size_t j = 0; j < dim; j++) - { - Parameter *arg = Parameter::getNth(tup->arguments, j); - b = new BaseClass(arg->type, protection); - baseclasses->insert(i + j, b); - } - } - else - i++; - } - - if (baseok >= BASEOKdone) - { - //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun); - if (semanticRun >= PASSsemanticdone) - return; - goto Lancestorsdone; - } - - if (!baseclasses->dim && sc->linkage == LINKcpp) - cpp = true; - objc_InterfaceDeclaration_semantic_objcExtern(this, sc); - - // Check for errors, handle forward references - for (size_t i = 0; i < baseclasses->dim; ) - { - BaseClass *b = (*baseclasses)[i]; - Type *tb = b->type->toBasetype(); - TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; - if (!tc || !tc->sym->isInterfaceDeclaration()) - { - if (b->type != Type::terror) - error("base type must be interface, not %s", b->type->toChars()); - baseclasses->remove(i); - continue; - } - - // Check for duplicate interfaces - for (size_t j = 0; j < i; j++) - { - BaseClass *b2 = (*baseclasses)[j]; - if (b2->sym == tc->sym) - { - error("inherits from duplicate interface %s", b2->sym->toChars()); - baseclasses->remove(i); - continue; - } - } - - if (tc->sym == this || isBaseOf2(tc->sym)) - { - error("circular inheritance of interface"); - baseclasses->remove(i); - continue; - } - - if (tc->sym->isDeprecated()) - { - if (!isDeprecated()) - { - // Deriving from deprecated class makes this one deprecated too - isdeprecated = true; - - tc->checkDeprecated(loc, sc); - } - } - - b->sym = tc->sym; - - if (tc->sym->scope && tc->sym->baseok < BASEOKdone) - tc->sym->semantic(NULL); // Try to resolve forward reference - if (tc->sym->baseok < BASEOKdone) - { - //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars()); - if (tc->sym->scope) - tc->sym->scope->module->addDeferredSemantic(tc->sym); - baseok = BASEOKnone; - } - i++; - } - if (baseok == BASEOKnone) - { - // Forward referencee of one or more bases, try again later - scope = scx ? scx : sc->copy(); - scope->setNoFree(); - scope->module->addDeferredSemantic(this); - return; - } - baseok = BASEOKdone; - - interfaces_dim = baseclasses->dim; - interfaces = baseclasses->tdata(); - - for (size_t i = 0; i < interfaces_dim; i++) - { - BaseClass *b = interfaces[i]; - // If this is an interface, and it derives from a COM interface, - // then this is a COM interface too. - if (b->sym->isCOMinterface()) - com = true; - if (b->sym->isCPPinterface()) - cpp = true; - } - - interfaceSemantic(sc); - } -Lancestorsdone: - - if (!members) // if opaque declaration - { - semanticRun = PASSsemanticdone; - return; - } - if (!symtab) - symtab = new DsymbolTable(); - - for (size_t i = 0; i < baseclasses->dim; i++) - { - BaseClass *b = (*baseclasses)[i]; - Type *tb = b->type->toBasetype(); - assert(tb->ty == Tclass); - TypeClass *tc = (TypeClass *)tb; - - if (tc->sym->semanticRun < PASSsemanticdone) - { - // Forward referencee of one or more bases, try again later - scope = scx ? scx : sc->copy(); - scope->setNoFree(); - if (tc->sym->scope) - tc->sym->scope->module->addDeferredSemantic(tc->sym); - scope->module->addDeferredSemantic(this); - return; - } - } - - if (baseok == BASEOKdone) - { - baseok = BASEOKsemanticdone; - - // initialize vtbl - if (vtblOffset()) - vtbl.push(this); // leave room at vtbl[0] for classinfo - - // Cat together the vtbl[]'s from base interfaces - for (size_t i = 0; i < interfaces_dim; i++) - { - BaseClass *b = interfaces[i]; - - // Skip if b has already appeared - for (size_t k = 0; k < i; k++) - { - if (b == interfaces[k]) - goto Lcontinue; - } - - // Copy vtbl[] from base class - if (b->sym->vtblOffset()) - { - size_t d = b->sym->vtbl.dim; - if (d > 1) - { - vtbl.reserve(d - 1); - for (size_t j = 1; j < d; j++) - vtbl.push(b->sym->vtbl[j]); - } - } - else - { - vtbl.append(&b->sym->vtbl); - } - - Lcontinue: - ; - } - } - - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - s->addMember(sc, this); - } - - Scope *sc2 = sc->push(this); - sc2->stc &= STCsafe | STCtrusted | STCsystem; - sc2->parent = this; - sc2->inunion = 0; - if (com) - sc2->linkage = LINKwindows; - else if (cpp) - sc2->linkage = LINKcpp; - else if (this->objc.isInterface()) - sc2->linkage = LINKobjc; - sc2->protection = Prot(PROTpublic); - sc2->explicitProtection = 0; - sc2->structalign = STRUCTALIGN_DEFAULT; - sc2->userAttribDecl = NULL; - - finalizeSize(sc2); - - /* Set scope so if there are forward references, we still might be able to - * resolve individual members like enums. - */ - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - //printf("setScope %s %s\n", s->kind(), s->toChars()); - s->setScope(sc2); - } - - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - s->importAll(sc2); - } - - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - s->semantic(sc2); - } - - semanticRun = PASSsemanticdone; - - if (global.errors != errors) - { - // The type is no good. - type = Type::terror; - } - - //members->print(); - sc2->pop(); - //printf("-InterfaceDeclaration::semantic(%s), type = %p\n", toChars(), type); - -#if 0 - if (type->ty == Tclass && ((TypeClass *)type)->sym != this) - { - printf("this = %p %s\n", this, this->toChars()); - printf("type = %d sym = %p\n", type->ty, ((TypeClass *)type)->sym); - } -#endif - assert(type->ty != Tclass || ((TypeClass *)type)->sym == this); -} - -void InterfaceDeclaration::finalizeSize(Scope *sc) -{ - structsize = Target::ptrsize * 2; - sizeok = SIZEOKdone; - - // set the offset of base interfaces - setBaseInterfaceOffsets(0); -} - -/******************************************* - * Determine if 'this' is a base class of cd. - * (Actually, if it is an interface supported by cd) - * Output: - * *poffset offset to start of class - * OFFSET_RUNTIME must determine offset at runtime - * Returns: - * false not a base - * true is a base - */ - -bool InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) -{ - //printf("%s.InterfaceDeclaration::isBaseOf(cd = '%s')\n", toChars(), cd->toChars()); - assert(!baseClass); - for (size_t j = 0; j < cd->interfaces_dim; j++) - { - BaseClass *b = cd->interfaces[j]; - - //printf("\tX base %s\n", b->sym->toChars()); - if (this == b->sym) - { - //printf("\tfound at offset %d\n", b->offset); - if (poffset) - { - *poffset = b->offset; - if (j && cd->isInterfaceDeclaration()) - *poffset = OFFSET_RUNTIME; - - /* TODO: Even though it's an interface to base interface upcast, - * I think we can avoid runtime offset determination ultimately. - * (I doubt that it was just a workaround for the bug in the - * inferface to Object downcast) - */ - } - return true; - } - if (isBaseOf(b, poffset)) - { - if (poffset) - { - if (j && cd->isInterfaceDeclaration()) - *poffset = OFFSET_RUNTIME; - } - return true; - } - } - - if (cd->baseClass && isBaseOf(cd->baseClass, poffset)) - return true; - - if (poffset) - *poffset = 0; - return false; -} - -bool InterfaceDeclaration::isBaseOf(BaseClass *bc, int *poffset) -{ - //printf("%s.InterfaceDeclaration::isBaseOf(bc = '%s')\n", toChars(), bc->sym->toChars()); - for (size_t j = 0; j < bc->baseInterfaces_dim; j++) - { - BaseClass *b = &bc->baseInterfaces[j]; - - //printf("\tY base %s\n", b->sym->toChars()); - if (this == b->sym) - { - //printf("\tfound at offset %d\n", b->offset); - if (poffset) - { - *poffset = b->offset; - } - return true; - } - if (isBaseOf(b, poffset)) - { - return true; - } - } - if (poffset) - *poffset = 0; - return false; -} - -/**************************************** - * Determine if slot 0 of the vtbl[] is reserved for something else. - * For class objects, yes, this is where the ClassInfo ptr goes. - * For COM interfaces, no. - * For non-COM interfaces, yes, this is where the Interface ptr goes. - */ - -int InterfaceDeclaration::vtblOffset() -{ - if (isCOMinterface() || isCPPinterface()) - return 0; - return 1; -} - -bool InterfaceDeclaration::isCOMinterface() -{ - return com; -} - -bool InterfaceDeclaration::isCPPinterface() -{ - return cpp; -} - -/******************************************* - */ - -const char *InterfaceDeclaration::kind() -{ - return "interface"; -} - - -/******************************** BaseClass *****************************/ - -BaseClass::BaseClass() -{ - memset(this, 0, sizeof(BaseClass)); -} - -BaseClass::BaseClass(Type *type, Prot protection) -{ - //printf("BaseClass(this = %p, '%s')\n", this, type->toChars()); - this->type = type; - this->protection = protection; - this->sym = NULL; - this->offset = 0; - - this->baseInterfaces_dim = 0; - this->baseInterfaces = NULL; -} - -/**************************************** - * Fill in vtbl[] for base class based on member functions of class cd. - * Input: - * vtbl if !=NULL, fill it in - * newinstance !=0 means all entries must be filled in by members - * of cd, not members of any base classes of cd. - * Returns: - * true if any entries were filled in by members of cd (not exclusively - * by base classes) - */ - -bool BaseClass::fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance) -{ - bool result = false; - - //printf("BaseClass::fillVtbl(this='%s', cd='%s')\n", sym->toChars(), cd->toChars()); - if (vtbl) - vtbl->setDim(sym->vtbl.dim); - - // first entry is ClassInfo reference - for (size_t j = sym->vtblOffset(); j < sym->vtbl.dim; j++) - { - FuncDeclaration *ifd = sym->vtbl[j]->isFuncDeclaration(); - FuncDeclaration *fd; - TypeFunction *tf; - - //printf(" vtbl[%d] is '%s'\n", j, ifd ? ifd->toChars() : "null"); - - assert(ifd); - // Find corresponding function in this class - tf = (ifd->type->ty == Tfunction) ? (TypeFunction *)(ifd->type) : NULL; - assert(tf); // should always be non-null - fd = cd->findFunc(ifd->ident, tf); - if (fd && !fd->isAbstract()) - { - //printf(" found\n"); - // Check that calling conventions match - if (fd->linkage != ifd->linkage) - fd->error("linkage doesn't match interface function"); - - // Check that it is current - if (newinstance && - fd->toParent() != cd && - ifd->toParent() == sym) - cd->error("interface function '%s' is not implemented", ifd->toFullSignature()); - - if (fd->toParent() == cd) - result = true; - } - else - { - //printf(" not found\n"); - // BUG: should mark this class as abstract? - if (!cd->isAbstract()) - cd->error("interface function '%s' is not implemented", ifd->toFullSignature()); - - fd = NULL; - } - if (vtbl) - (*vtbl)[j] = fd; - } - - return result; -} - -void BaseClass::copyBaseInterfaces(BaseClasses *vtblInterfaces) -{ - //printf("+copyBaseInterfaces(), %s\n", sym->toChars()); -// if (baseInterfaces_dim) -// return; - - baseInterfaces_dim = sym->interfaces_dim; - baseInterfaces = (BaseClass *)mem.xcalloc(baseInterfaces_dim, sizeof(BaseClass)); - - //printf("%s.copyBaseInterfaces()\n", sym->toChars()); - for (size_t i = 0; i < baseInterfaces_dim; i++) - { - BaseClass *b = &baseInterfaces[i]; - BaseClass *b2 = sym->interfaces[i]; - - assert(b2->vtbl.dim == 0); // should not be filled yet - memcpy(b, b2, sizeof(BaseClass)); - - if (i) // single inheritance is i==0 - vtblInterfaces->push(b); // only need for M.I. - b->copyBaseInterfaces(vtblInterfaces); - } - //printf("-copyBaseInterfaces\n"); -} diff --git a/src/clone.c b/src/clone.c deleted file mode 100644 index 0c7551c4d55b..000000000000 --- a/src/clone.c +++ /dev/null @@ -1,1131 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/clone.c - */ - -#include -#include - -#include "root.h" -#include "aggregate.h" -#include "scope.h" -#include "mtype.h" -#include "declaration.h" -#include "module.h" -#include "id.h" -#include "expression.h" -#include "statement.h" -#include "init.h" -#include "template.h" -#include "tokens.h" - -/******************************************* - * Merge function attributes pure, nothrow, @safe, @nogc, and @disable - */ -StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration *f) -{ - if (!f) - return s1; - - StorageClass s2 = (f->storage_class & STCdisable); - TypeFunction *tf = (TypeFunction *)f->type; - if (tf->trust == TRUSTsafe) - s2 |= STCsafe; - else if (tf->trust == TRUSTsystem) - s2 |= STCsystem; - else if (tf->trust == TRUSTtrusted) - s2 |= STCtrusted; - if (tf->purity != PUREimpure) - s2 |= STCpure; - if (tf->isnothrow) - s2 |= STCnothrow; - if (tf->isnogc) - s2 |= STCnogc; - - StorageClass stc = 0; - StorageClass sa = s1 & s2; - StorageClass so = s1 | s2; - - if (so & STCsystem) - stc |= STCsystem; - else if (sa & STCtrusted) - stc |= STCtrusted; - else if ((so & (STCtrusted | STCsafe)) == (STCtrusted | STCsafe)) - stc |= STCtrusted; - else if (sa & STCsafe) - stc |= STCsafe; - - if (sa & STCpure) - stc |= STCpure; - - if (sa & STCnothrow) - stc |= STCnothrow; - - if (sa & STCnogc) - stc |= STCnogc; - - if (so & STCdisable) - stc |= STCdisable; - - return stc; -} - -/******************************************* - * Check given aggregate actually has an identity opAssign or not. - */ -FuncDeclaration *hasIdentityOpAssign(AggregateDeclaration *ad, Scope *sc) -{ - Dsymbol *assign = search_function(ad, Id::assign); - if (assign) - { - /* check identity opAssign exists - */ - Expression *er = new NullExp(ad->loc, ad->type); // dummy rvalue - Expression *el = new IdentifierExp(ad->loc, Id::p); // dummy lvalue - el->type = ad->type; - Expressions *a = new Expressions(); - a->setDim(1); - FuncDeclaration *f = NULL; - - unsigned errors = global.startGagging(); // Do not report errors, even if the - sc = sc->push(); - sc->tinst = NULL; - sc->minst = NULL; - - for (size_t i = 0; i < 2; i++) - { - (*a)[0] = (i == 0 ? er : el); - f = resolveFuncCall(ad->loc, sc, assign, NULL, ad->type, a, 1); - if (f) - break; - } - - sc = sc->pop(); - global.endGagging(errors); - - if (f) - { - if (f->errors) - return NULL; - int varargs; - Parameters *fparams = f->getParameters(&varargs); - if (fparams->dim >= 1) - { - Parameter *fparam0 = Parameter::getNth(fparams, 0); - if (fparam0->type->toDsymbol(NULL) != ad) - f = NULL; - } - } - // BUGS: This detection mechanism cannot find some opAssign-s like follows: - // struct S { void opAssign(ref immutable S) const; } - return f; - } - return NULL; -} - -/******************************************* - * We need an opAssign for the struct if - * it has a destructor or a postblit. - * We need to generate one if a user-specified one does not exist. - */ -bool needOpAssign(StructDeclaration *sd) -{ - //printf("StructDeclaration::needOpAssign() %s\n", sd->toChars()); - - if (sd->hasIdentityAssign) - goto Lneed; // because has identity==elaborate opAssign - - if (sd->dtor || sd->postblit) - goto Lneed; - - /* If any of the fields need an opAssign, then we - * need it too. - */ - for (size_t i = 0; i < sd->fields.dim; i++) - { - VarDeclaration *v = sd->fields[i]; - if (v->storage_class & STCref) - continue; - Type *tv = v->type->baseElemOf(); - if (tv->ty == Tstruct) - { - TypeStruct *ts = (TypeStruct *)tv; - if (needOpAssign(ts->sym)) - goto Lneed; - } - } - //printf("\tdontneed\n"); - return false; - -Lneed: - //printf("\tneed\n"); - return true; -} - -/****************************************** - * Build opAssign for struct. - * ref S opAssign(S s) { ... } - * - * Note that s will be constructed onto the stack, and probably - * copy-constructed in caller site. - * - * If S has copy copy construction and/or destructor, - * the body will make bit-wise object swap: - * S __swap = this; // bit copy - * this = s; // bit copy - * __swap.dtor(); - * Instead of running the destructor on s, run it on tmp instead. - * - * Otherwise, the body will make member-wise assignments: - * Then, the body is: - * this.field1 = s.field1; - * this.field2 = s.field2; - * ...; - */ -FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc) -{ - if (FuncDeclaration *f = hasIdentityOpAssign(sd, sc)) - { - sd->hasIdentityAssign = true; - return f; - } - // Even if non-identity opAssign is defined, built-in identity opAssign - // will be defined. - - if (!needOpAssign(sd)) - return NULL; - - //printf("StructDeclaration::buildOpAssign() %s\n", sd->toChars()); - StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; - Loc declLoc = sd->loc; - Loc loc = Loc(); // internal code should have no loc to prevent coverage - - if (sd->dtor || sd->postblit) - { - if (!sd->type->isAssignable()) // Bugzilla 13044 - return NULL; - stc = mergeFuncAttrs(stc, sd->dtor); - if (stc & STCsafe) - stc = (stc & ~STCsafe) | STCtrusted; - } - else - { - for (size_t i = 0; i < sd->fields.dim; i++) - { - VarDeclaration *v = sd->fields[i]; - if (v->storage_class & STCref) - continue; - Type *tv = v->type->baseElemOf(); - if (tv->ty != Tstruct) - continue; - - StructDeclaration *sdv = ((TypeStruct *)tv)->sym; - stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc)); - } - } - - Parameters *fparams = new Parameters; - fparams->push(new Parameter(STCnodtor, sd->type, Id::p, NULL)); - TypeFunction *tf = new TypeFunction(fparams, sd->handleType(), 0, LINKd, stc | STCref); - - FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), Id::assign, stc, tf); - - Expression *e = NULL; - if (stc & STCdisable) - { - } - else if (sd->dtor || sd->postblit) - { - /* Do swap this and rhs - * __swap = this; this = s; __swap.dtor(); - */ - //printf("\tswap copy\n"); - Identifier *idtmp = Identifier::generateId("__swap"); - VarDeclaration *tmp = NULL; - AssignExp *ec = NULL; - if (sd->dtor) - { - tmp = new VarDeclaration(loc, sd->type, idtmp, new VoidInitializer(loc)); - tmp->noscope = 1; - tmp->storage_class |= STCtemp | STCctfe; - e = new DeclarationExp(loc, tmp); - ec = new BlitExp(loc, new VarExp(loc, tmp), new ThisExp(loc)); - e = Expression::combine(e, ec); - } - ec = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id::p)); - e = Expression::combine(e, ec); - if (sd->dtor) - { - /* Instead of running the destructor on s, run it - * on tmp. This avoids needing to copy tmp back in to s. - */ - Expression *ec2 = new DotVarExp(loc, new VarExp(loc, tmp), sd->dtor, 0); - ec2 = new CallExp(loc, ec2); - e = Expression::combine(e, ec2); - } - } - else - { - /* Do memberwise copy - */ - //printf("\tmemberwise copy\n"); - for (size_t i = 0; i < sd->fields.dim; i++) - { - VarDeclaration *v = sd->fields[i]; - // this.v = s.v; - AssignExp *ec = new AssignExp(loc, - new DotVarExp(loc, new ThisExp(loc), v, 0), - new DotVarExp(loc, new IdentifierExp(loc, Id::p), v, 0)); - e = Expression::combine(e, ec); - } - } - if (e) - { - Statement *s1 = new ExpStatement(loc, e); - - /* Add: - * return this; - */ - e = new ThisExp(loc); - Statement *s2 = new ReturnStatement(loc, e); - - fop->fbody = new CompoundStatement(loc, s1, s2); - tf->isreturn = true; - } - - sd->members->push(fop); - fop->addMember(sc, sd); - sd->hasIdentityAssign = true; // temporary mark identity assignable - - unsigned errors = global.startGagging(); // Do not report errors, even if the - Scope *sc2 = sc->push(); - sc2->stc = 0; - sc2->linkage = LINKd; - - fop->semantic(sc2); - fop->semantic2(sc2); - fop->semantic3(sc2); - - sc2->pop(); - if (global.endGagging(errors)) // if errors happened - { - // Disable generated opAssign, because some members forbid identity assignment. - fop->storage_class |= STCdisable; - fop->fbody = NULL; // remove fbody which contains the error - } - - //printf("-StructDeclaration::buildOpAssign() %s, errors = %d\n", sd->toChars(), (fop->storage_class & STCdisable) != 0); - - return fop; -} - -/******************************************* - * We need an opEquals for the struct if - * any fields has an opEquals. - * Generate one if a user-specified one does not exist. - */ -bool needOpEquals(StructDeclaration *sd) -{ - //printf("StructDeclaration::needOpEquals() %s\n", sd->toChars()); - - if (sd->hasIdentityEquals) - goto Lneed; - - if (sd->isUnionDeclaration()) - goto Ldontneed; - - /* If any of the fields has an opEquals, then we - * need it too. - */ - for (size_t i = 0; i < sd->fields.dim; i++) - { - VarDeclaration *v = sd->fields[i]; - if (v->storage_class & STCref) - continue; - Type *tv = v->type->toBasetype(); - if (tv->isfloating()) - { - // This is necessray for: - // 1. comparison of +0.0 and -0.0 should be true. - // 2. comparison of NANs should be false always. - goto Lneed; - } - if (tv->ty == Tarray) - goto Lneed; - if (tv->ty == Taarray) - goto Lneed; - if (tv->ty == Tclass) - goto Lneed; - tv = tv->baseElemOf(); - if (tv->ty == Tstruct) - { - TypeStruct *ts = (TypeStruct *)tv; - if (needOpEquals(ts->sym)) - goto Lneed; - if (ts->sym->aliasthis) // Bugzilla 14806 - goto Lneed; - } - } -Ldontneed: - //printf("\tdontneed\n"); - return false; - -Lneed: - //printf("\tneed\n"); - return true; -} - -/******************************************* - * Check given aggregate actually has an identity opEquals or not. - */ -FuncDeclaration *hasIdentityOpEquals(AggregateDeclaration *ad, Scope *sc) -{ - Dsymbol *eq = search_function(ad, Id::eq); - if (eq) - { - /* check identity opEquals exists - */ - Expression *er = new NullExp(ad->loc, NULL); // dummy rvalue - Expression *el = new IdentifierExp(ad->loc, Id::p); // dummy lvalue - Expressions *a = new Expressions(); - a->setDim(1); - for (size_t i = 0; ; i++) - { - Type *tthis = NULL; // dead-store to prevent spurious warning - if (i == 0) tthis = ad->type; - if (i == 1) tthis = ad->type->constOf(); - if (i == 2) tthis = ad->type->immutableOf(); - if (i == 3) tthis = ad->type->sharedOf(); - if (i == 4) tthis = ad->type->sharedConstOf(); - if (i == 5) break; - FuncDeclaration *f = NULL; - - unsigned errors = global.startGagging(); // Do not report errors, even if the - sc = sc->push(); - sc->tinst = NULL; - sc->minst = NULL; - - for (size_t j = 0; j < 2; j++) - { - (*a)[0] = (j == 0 ? er : el); - (*a)[0]->type = tthis; - f = resolveFuncCall(ad->loc, sc, eq, NULL, tthis, a, 1); - if (f) - break; - } - - sc = sc->pop(); - global.endGagging(errors); - - if (f) - { - if (f->errors) - return NULL; - return f; - } - } - } - return NULL; -} - -/****************************************** - * Build opEquals for struct. - * const bool opEquals(const S s) { ... } - * - * By fixing bugzilla 3789, opEquals is changed to be never implicitly generated. - * Now, struct objects comparison s1 == s2 is translated to: - * s1.tupleof == s2.tupleof - * to calculate structural equality. See EqualExp::semantic. - */ -FuncDeclaration *buildOpEquals(StructDeclaration *sd, Scope *sc) -{ - if (hasIdentityOpEquals(sd, sc)) - { - sd->hasIdentityEquals = true; - } - return NULL; -} - -/****************************************** - * Build __xopEquals for TypeInfo_Struct - * static bool __xopEquals(ref const S p, ref const S q) - * { - * return p == q; - * } - * - * This is called by TypeInfo.equals(p1, p2). If the struct does not support - * const objects comparison, it will throw "not implemented" Error in runtime. - */ -FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc) -{ - if (!needOpEquals(sd)) - return NULL; // bitwise comparison would work - - //printf("StructDeclaration::buildXopEquals() %s\n", sd->toChars()); - if (Dsymbol *eq = search_function(sd, Id::eq)) - { - if (FuncDeclaration *fd = eq->isFuncDeclaration()) - { - TypeFunction *tfeqptr; - { - Scope scx; - - /* const bool opEquals(ref const S s); - */ - Parameters *parameters = new Parameters; - parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL)); - tfeqptr = new TypeFunction(parameters, Type::tbool, 0, LINKd); - tfeqptr->mod = MODconst; - tfeqptr = (TypeFunction *)tfeqptr->semantic(Loc(), &scx); - } - fd = fd->overloadExactMatch(tfeqptr); - if (fd) - return fd; - } - } - - if (!sd->xerreq) - { - // object._xopEquals - Identifier *id = Identifier::idPool("_xopEquals"); - Expression *e = new IdentifierExp(sd->loc, Id::empty); - e = new DotIdExp(sd->loc, e, Id::object); - e = new DotIdExp(sd->loc, e, id); - e = e->semantic(sc); - Dsymbol *s = getDsymbol(e); - assert(s); - sd->xerreq = s->isFuncDeclaration(); - } - - Loc declLoc = Loc(); // loc is unnecessary so __xopEquals is never called directly - Loc loc = Loc(); // loc is unnecessary so errors are gagged - - Parameters *parameters = new Parameters; - parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL)); - parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL)); - TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); - - Identifier *id = Id::xopEquals; - FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); - - Expression *e1 = new IdentifierExp(loc, Id::p); - Expression *e2 = new IdentifierExp(loc, Id::q); - Expression *e = new EqualExp(TOKequal, loc, e1, e2); - - fop->fbody = new ReturnStatement(loc, e); - - unsigned errors = global.startGagging(); // Do not report errors - Scope *sc2 = sc->push(); - sc2->stc = 0; - sc2->linkage = LINKd; - - fop->semantic(sc2); - fop->semantic2(sc2); - - sc2->pop(); - if (global.endGagging(errors)) // if errors happened - fop = sd->xerreq; - - return fop; -} - -/****************************************** - * Build __xopCmp for TypeInfo_Struct - * static bool __xopCmp(ref const S p, ref const S q) - * { - * return p.opCmp(q); - * } - * - * This is called by TypeInfo.compare(p1, p2). If the struct does not support - * const objects comparison, it will throw "not implemented" Error in runtime. - */ -FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc) -{ - //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); - if (Dsymbol *cmp = search_function(sd, Id::cmp)) - { - if (FuncDeclaration *fd = cmp->isFuncDeclaration()) - { - TypeFunction *tfcmpptr; - { - Scope scx; - - /* const int opCmp(ref const S s); - */ - Parameters *parameters = new Parameters; - parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL)); - tfcmpptr = new TypeFunction(parameters, Type::tint32, 0, LINKd); - tfcmpptr->mod = MODconst; - tfcmpptr = (TypeFunction *)tfcmpptr->semantic(Loc(), &scx); - } - fd = fd->overloadExactMatch(tfcmpptr); - if (fd) - return fd; - } - } - else - { -#if 0 // FIXME: doesn't work for recursive alias this - /* Check opCmp member exists. - * Consider 'alias this', but except opDispatch. - */ - Expression *e = new DsymbolExp(sd->loc, sd); - e = new DotIdExp(sd->loc, e, Id::cmp); - Scope *sc2 = sc->push(); - e = e->trySemantic(sc2); - sc2->pop(); - if (e) - { - Dsymbol *s = NULL; - switch (e->op) - { - case TOKoverloadset: s = ((OverExp *)e)->vars; break; - case TOKimport: s = ((ScopeExp *)e)->sds; break; - case TOKvar: s = ((VarExp *)e)->var; break; - default: break; - } - if (!s || s->ident != Id::cmp) - e = NULL; // there's no valid member 'opCmp' - } - if (!e) - return NULL; // bitwise comparison would work - /* Essentially, a struct which does not define opCmp is not comparable. - * At this time, typeid(S).compare might be correct that throwing "not implement" Error. - * But implementing it would break existing code, such as: - * - * struct S { int value; } // no opCmp - * int[S] aa; // Currently AA key uses bitwise comparison - * // (It's default behavior of TypeInfo_Strust.compare). - * - * Not sure we should fix this inconsistency, so just keep current behavior. - */ -#else - return NULL; -#endif - } - - if (!sd->xerrcmp) - { - // object._xopCmp - Identifier *id = Identifier::idPool("_xopCmp"); - Expression *e = new IdentifierExp(sd->loc, Id::empty); - e = new DotIdExp(sd->loc, e, Id::object); - e = new DotIdExp(sd->loc, e, id); - e = e->semantic(sc); - Dsymbol *s = getDsymbol(e); - assert(s); - sd->xerrcmp = s->isFuncDeclaration(); - } - - Loc declLoc = Loc(); // loc is unnecessary so __xopCmp is never called directly - Loc loc = Loc(); // loc is unnecessary so errors are gagged - - Parameters *parameters = new Parameters; - parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL)); - parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL)); - TypeFunction *tf = new TypeFunction(parameters, Type::tint32, 0, LINKd); - - Identifier *id = Id::xopCmp; - FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); - - Expression *e1 = new IdentifierExp(loc, Id::p); - Expression *e2 = new IdentifierExp(loc, Id::q); - Expression *e = new CallExp(loc, new DotIdExp(loc, e2, Id::cmp), e1); - - fop->fbody = new ReturnStatement(loc, e); - - unsigned errors = global.startGagging(); // Do not report errors - Scope *sc2 = sc->push(); - sc2->stc = 0; - sc2->linkage = LINKd; - - fop->semantic(sc2); - fop->semantic2(sc2); - - sc2->pop(); - if (global.endGagging(errors)) // if errors happened - fop = sd->xerrcmp; - - return fop; -} - -/******************************************* - * We need a toHash for the struct if - * any fields has a toHash. - * Generate one if a user-specified one does not exist. - */ -bool needToHash(StructDeclaration *sd) -{ - //printf("StructDeclaration::needToHash() %s\n", sd->toChars()); - - if (sd->xhash) - goto Lneed; - - if (sd->isUnionDeclaration()) - goto Ldontneed; - - /* If any of the fields has an opEquals, then we - * need it too. - */ - for (size_t i = 0; i < sd->fields.dim; i++) - { - VarDeclaration *v = sd->fields[i]; - if (v->storage_class & STCref) - continue; - Type *tv = v->type->toBasetype(); - if (tv->isfloating()) - { - // This is necessray for: - // 1. comparison of +0.0 and -0.0 should be true. - goto Lneed; - } - if (tv->ty == Tarray) - goto Lneed; - if (tv->ty == Taarray) - goto Lneed; - if (tv->ty == Tclass) - goto Lneed; - tv = tv->baseElemOf(); - if (tv->ty == Tstruct) - { - TypeStruct *ts = (TypeStruct *)tv; - if (needToHash(ts->sym)) - goto Lneed; - } - } -Ldontneed: - //printf("\tdontneed\n"); - return false; - -Lneed: - //printf("\tneed\n"); - return true; -} - -/****************************************** - * Build __xtoHash for non-bitwise hashing - * static hash_t xtoHash(ref const S p) nothrow @trusted; - */ -FuncDeclaration *buildXtoHash(StructDeclaration *sd, Scope *sc) -{ - if (Dsymbol *s = search_function(sd, Id::tohash)) - { - static TypeFunction *tftohash; - if (!tftohash) - { - tftohash = new TypeFunction(NULL, Type::thash_t, 0, LINKd); - tftohash->mod = MODconst; - tftohash = (TypeFunction *)tftohash->merge(); - } - - if (FuncDeclaration *fd = s->isFuncDeclaration()) - { - fd = fd->overloadExactMatch(tftohash); - if (fd) - return fd; - } - } - - if (!needToHash(sd)) - return NULL; - - //printf("StructDeclaration::buildXtoHash() %s\n", sd->toPrettyChars()); - Loc declLoc = Loc(); // loc is unnecessary so __xtoHash is never called directly - Loc loc = Loc(); // internal code should have no loc to prevent coverage - - Parameters *parameters = new Parameters(); - parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL)); - TypeFunction *tf = new TypeFunction(parameters, Type::thash_t, 0, LINKd, STCnothrow | STCtrusted); - - Identifier *id = Id::xtoHash; - FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); - - const char *code = - "size_t h = 0;" - "foreach (i, T; typeof(p.tupleof))" - " h += typeid(T).getHash(cast(const void*)&p.tupleof[i]);" - "return h;"; - fop->fbody = new CompileStatement(loc, new StringExp(loc, (char *)code)); - - Scope *sc2 = sc->push(); - sc2->stc = 0; - sc2->linkage = LINKd; - - fop->semantic(sc2); - fop->semantic2(sc2); - - sc2->pop(); - - //printf("%s fop = %s %s\n", sd->toChars(), fop->toChars(), fop->type->toChars()); - return fop; -} - -/***************************************** - * Create inclusive postblit for struct by aggregating - * all the postblits in postblits[] with the postblits for - * all the members. - * Note the close similarity with AggregateDeclaration::buildDtor(), - * and the ordering changes (runs forward instead of backwards). - */ -FuncDeclaration *buildPostBlit(StructDeclaration *sd, Scope *sc) -{ - //printf("StructDeclaration::buildPostBlit() %s\n", sd->toChars()); - StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; - Loc declLoc = sd->postblits.dim ? sd->postblits[0]->loc : sd->loc; - Loc loc = Loc(); // internal code should have no loc to prevent coverage - - for (size_t i = 0; i < sd->postblits.dim; i++) - { - stc |= sd->postblits[i]->storage_class & STCdisable; - } - - Statements *a = NULL; - for (size_t i = 0; i < sd->fields.dim && !(stc & STCdisable); i++) - { - VarDeclaration *v = sd->fields[i]; - if (v->storage_class & STCref) - continue; - Type *tv = v->type->baseElemOf(); - if (tv->ty != Tstruct || !v->type->size()) - continue; - StructDeclaration *sdv = ((TypeStruct *)tv)->sym; - if (!sdv->postblit) - continue; - - stc = mergeFuncAttrs(stc, sdv->postblit); - stc = mergeFuncAttrs(stc, sdv->dtor); - if (stc & STCdisable) - { - a = NULL; - break; - } - if (!a) - a = new Statements(); - - Expression *ex = new ThisExp(loc); - ex = new DotVarExp(loc, ex, v, 0); - if (v->type->toBasetype()->ty == Tstruct) - { - // this.v.__xpostblit() - - // This is a hack so we can call postblits on const/immutable objects. - ex = new AddrExp(loc, ex); - ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo()); - ex = new PtrExp(loc, ex); - if (stc & STCsafe) - stc = (stc & ~STCsafe) | STCtrusted; - - ex = new DotVarExp(loc, ex, sdv->postblit, 0); - ex = new CallExp(loc, ex); - } - else - { - // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n]) - - // This is a hack so we can call postblits on const/immutable objects. - ex = new DotIdExp(loc, ex, Id::ptr); - ex = new CastExp(loc, ex, sdv->type->pointerTo()); - if (stc & STCsafe) - stc = (stc & ~STCsafe) | STCtrusted; - - uinteger_t n = v->type->size() / sdv->type->size(); - ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t), - new IntegerExp(loc, n, Type::tsize_t)); - // Prevent redundant bounds check - ((SliceExp *)ex)->upperIsInBounds = true; - ((SliceExp *)ex)->lowerIsLessThanUpper = true; - - ex = new CallExp(loc, new IdentifierExp(loc, Id::_ArrayPostblit), ex); - } - a->push(new ExpStatement(loc, ex)); // combine in forward order - - /* Bugzilla 10972: When the following field postblit calls fail, - * this field should be destructed for Exception Safety. - */ - if (!sdv->dtor) - continue; - ex = new ThisExp(loc); - ex = new DotVarExp(loc, ex, v, 0); - if (v->type->toBasetype()->ty == Tstruct) - { - // this.v.__xdtor() - - // This is a hack so we can call destructors on const/immutable objects. - ex = new AddrExp(loc, ex); - ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo()); - ex = new PtrExp(loc, ex); - if (stc & STCsafe) - stc = (stc & ~STCsafe) | STCtrusted; - - ex = new DotVarExp(loc, ex, sdv->dtor, 0); - ex = new CallExp(loc, ex); - } - else - { - // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) - - // This is a hack so we can call destructors on const/immutable objects. - ex = new DotIdExp(loc, ex, Id::ptr); - ex = new CastExp(loc, ex, sdv->type->pointerTo()); - if (stc & STCsafe) - stc = (stc & ~STCsafe) | STCtrusted; - - uinteger_t n = v->type->size() / sdv->type->size(); - ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t), - new IntegerExp(loc, n, Type::tsize_t)); - // Prevent redundant bounds check - ((SliceExp *)ex)->upperIsInBounds = true; - ((SliceExp *)ex)->lowerIsLessThanUpper = true; - - ex = new CallExp(loc, new IdentifierExp(loc, Id::_ArrayDtor), ex); - } - a->push(new OnScopeStatement(loc, TOKon_scope_failure, new ExpStatement(loc, ex))); - } - - /* Build our own "postblit" which executes a - */ - if (a || (stc & STCdisable)) - { - //printf("Building __fieldPostBlit()\n"); - PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__fieldPostblit); - dd->fbody = a ? new CompoundStatement(loc, a) : NULL; - sd->postblits.shift(dd); - sd->members->push(dd); - dd->semantic(sc); - } - - FuncDeclaration *xpostblit = NULL; - switch (sd->postblits.dim) - { - case 0: - break; - - case 1: - xpostblit = sd->postblits[0]; - break; - - default: - Expression *e = NULL; - stc = STCsafe | STCnothrow | STCpure | STCnogc; - for (size_t i = 0; i < sd->postblits.dim; i++) - { - FuncDeclaration *fd = sd->postblits[i]; - stc = mergeFuncAttrs(stc, fd); - if (stc & STCdisable) - { - e = NULL; - break; - } - Expression *ex = new ThisExp(loc); - ex = new DotVarExp(loc, ex, fd, 0); - ex = new CallExp(loc, ex); - e = Expression::combine(e, ex); - } - PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__aggrPostblit); - dd->fbody = new ExpStatement(loc, e); - sd->members->push(dd); - dd->semantic(sc); - xpostblit = dd; - break; - } - // Add an __xpostblit alias to make the inclusive postblit accessible - if (xpostblit) - { - AliasDeclaration *alias = new AliasDeclaration(Loc(), Id::__xpostblit, xpostblit); - alias->semantic(sc); - sd->members->push(alias); - alias->addMember(sc, sd); // add to symbol table - } - return xpostblit; -} - -/***************************************** - * Create inclusive destructor for struct/class by aggregating - * all the destructors in dtors[] with the destructors for - * all the members. - * Note the close similarity with StructDeclaration::buildPostBlit(), - * and the ordering changes (runs backward instead of forwards). - */ -FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc) -{ - //printf("AggregateDeclaration::buildDtor() %s\n", ad->toChars()); - StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; - Loc declLoc = ad->dtors.dim ? ad->dtors[0]->loc : ad->loc; - Loc loc = Loc(); // internal code should have no loc to prevent coverage - - Expression *e = NULL; - for (size_t i = 0; i < ad->fields.dim; i++) - { - VarDeclaration *v = ad->fields[i]; - if (v->storage_class & STCref) - continue; - Type *tv = v->type->baseElemOf(); - if (tv->ty != Tstruct || !v->type->size()) - continue; - StructDeclaration *sdv = ((TypeStruct *)tv)->sym; - if (!sdv->dtor) - continue; - - stc = mergeFuncAttrs(stc, sdv->dtor); - if (stc & STCdisable) - { - e = NULL; - break; - } - - Expression *ex = new ThisExp(loc); - ex = new DotVarExp(loc, ex, v, 0); - if (v->type->toBasetype()->ty == Tstruct) - { - // this.v.__xdtor() - - // This is a hack so we can call destructors on const/immutable objects. - ex = new AddrExp(loc, ex); - ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo()); - ex = new PtrExp(loc, ex); - if (stc & STCsafe) - stc = (stc & ~STCsafe) | STCtrusted; - - ex = new DotVarExp(loc, ex, sdv->dtor, 0); - ex = new CallExp(loc, ex); - } - else - { - // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) - - // This is a hack so we can call destructors on const/immutable objects. - ex = new DotIdExp(loc, ex, Id::ptr); - ex = new CastExp(loc, ex, sdv->type->pointerTo()); - if (stc & STCsafe) - stc = (stc & ~STCsafe) | STCtrusted; - - uinteger_t n = v->type->size() / sdv->type->size(); - ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t), - new IntegerExp(loc, n, Type::tsize_t)); - // Prevent redundant bounds check - ((SliceExp *)ex)->upperIsInBounds = true; - ((SliceExp *)ex)->lowerIsLessThanUpper = true; - - ex = new CallExp(loc, new IdentifierExp(loc, Id::_ArrayDtor), ex); - } - e = Expression::combine(ex, e); // combine in reverse order - } - - /* Build our own "destructor" which executes e - */ - if (e || (stc & STCdisable)) - { - //printf("Building __fieldDtor()\n"); - DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Id::__fieldDtor); - dd->fbody = new ExpStatement(loc, e); - ad->dtors.shift(dd); - ad->members->push(dd); - dd->semantic(sc); - } - - FuncDeclaration *xdtor = NULL; - switch (ad->dtors.dim) - { - case 0: - break; - - case 1: - xdtor = ad->dtors[0]; - break; - - default: - e = NULL; - stc = STCsafe | STCnothrow | STCpure | STCnogc; - for (size_t i = 0; i < ad->dtors.dim; i++) - { - FuncDeclaration *fd = ad->dtors[i]; - stc = mergeFuncAttrs(stc, fd); - if (stc & STCdisable) - { - e = NULL; - break; - } - Expression *ex = new ThisExp(loc); - ex = new DotVarExp(loc, ex, fd, 0); - ex = new CallExp(loc, ex); - e = Expression::combine(ex, e); - } - DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Id::__aggrDtor); - dd->fbody = new ExpStatement(loc, e); - ad->members->push(dd); - dd->semantic(sc); - xdtor = dd; - break; - } - // Add an __xdtor alias to make the inclusive dtor accessible - if (xdtor) - { - AliasDeclaration *alias = new AliasDeclaration(Loc(), Id::__xdtor, xdtor); - alias->semantic(sc); - ad->members->push(alias); - alias->addMember(sc, ad); // add to symbol table - } - return xdtor; -} - -/****************************************** - * Create inclusive invariant for struct/class by aggregating - * all the invariants in invs[]. - * void __invariant() const [pure nothrow @trusted] - * { - * invs[0](), invs[1](), ...; - * } - */ -FuncDeclaration *buildInv(AggregateDeclaration *ad, Scope *sc) -{ - StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; - Loc declLoc = ad->loc; - Loc loc = Loc(); // internal code should have no loc to prevent coverage - - switch (ad->invs.dim) - { - case 0: - return NULL; - - case 1: - // Don't return invs[0] so it has uniquely generated name. - /* fall through */ - - default: - Expression *e = NULL; - StorageClass stcx = 0; - for (size_t i = 0; i < ad->invs.dim; i++) - { - stc = mergeFuncAttrs(stc, ad->invs[i]); - if (stc & STCdisable) - { - // What should do? - } - StorageClass stcy = (ad->invs[i]->storage_class & STCsynchronized) | - (ad->invs[i]->type->mod & MODshared ? STCshared : 0); - if (i == 0) - stcx = stcy; - else if (stcx ^ stcy) - { - #if 1 // currently rejects - ad->error(ad->invs[i]->loc, "mixing invariants with shared/synchronized differene is not supported"); - e = NULL; - break; - #endif - } - e = Expression::combine(e, new CallExp(loc, new VarExp(loc, ad->invs[i]))); - } - InvariantDeclaration *inv; - inv = new InvariantDeclaration(declLoc, Loc(), stc | stcx, Id::classInvariant); - inv->fbody = new ExpStatement(loc, e); - ad->members->push(inv); - inv->semantic(sc); - return inv; - } -} diff --git a/src/clone.d b/src/clone.d new file mode 100644 index 000000000000..6c1faffe6ebf --- /dev/null +++ b/src/clone.d @@ -0,0 +1,1016 @@ +// 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.clone; + +import ddmd.aggregate, ddmd.arraytypes, ddmd.declaration, ddmd.dscope, ddmd.dstruct, ddmd.dsymbol, ddmd.dtemplate, ddmd.errors, ddmd.expression, ddmd.func, ddmd.globals, ddmd.id, ddmd.identifier, ddmd.init, ddmd.mtype, ddmd.opover, ddmd.statement, ddmd.tokens; + +/******************************************* + * Merge function attributes pure, nothrow, @safe, @nogc, and @disable + */ +extern (C++) StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration f) +{ + if (!f) + return s1; + StorageClass s2 = (f.storage_class & STCdisable); + TypeFunction tf = cast(TypeFunction)f.type; + if (tf.trust == TRUSTsafe) + s2 |= STCsafe; + else if (tf.trust == TRUSTsystem) + s2 |= STCsystem; + else if (tf.trust == TRUSTtrusted) + s2 |= STCtrusted; + if (tf.purity != PUREimpure) + s2 |= STCpure; + if (tf.isnothrow) + s2 |= STCnothrow; + if (tf.isnogc) + s2 |= STCnogc; + StorageClass stc = 0; + StorageClass sa = s1 & s2; + StorageClass so = s1 | s2; + if (so & STCsystem) + stc |= STCsystem; + else if (sa & STCtrusted) + stc |= STCtrusted; + else if ((so & (STCtrusted | STCsafe)) == (STCtrusted | STCsafe)) + stc |= STCtrusted; + else if (sa & STCsafe) + stc |= STCsafe; + if (sa & STCpure) + stc |= STCpure; + if (sa & STCnothrow) + stc |= STCnothrow; + if (sa & STCnogc) + stc |= STCnogc; + if (so & STCdisable) + stc |= STCdisable; + return stc; +} + +/******************************************* + * Check given aggregate actually has an identity opAssign or not. + */ +extern (C++) FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc) +{ + Dsymbol assign = search_function(ad, Id.assign); + if (assign) + { + /* check identity opAssign exists + */ + Expression er = new NullExp(ad.loc, ad.type); // dummy rvalue + Expression el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue + el.type = ad.type; + auto a = new Expressions(); + a.setDim(1); + FuncDeclaration f = null; + uint errors = global.startGagging(); // Do not report errors, even if the + sc = sc.push(); + sc.tinst = null; + sc.minst = null; + for (size_t i = 0; i < 2; i++) + { + (*a)[0] = (i == 0 ? er : el); + f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, a, 1); + if (f) + break; + } + sc = sc.pop(); + global.endGagging(errors); + if (f) + { + if (f.errors) + return null; + int varargs; + Parameters* fparams = f.getParameters(&varargs); + if (fparams.dim >= 1) + { + Parameter fparam0 = Parameter.getNth(fparams, 0); + if (fparam0.type.toDsymbol(null) != ad) + f = null; + } + } + // BUGS: This detection mechanism cannot find some opAssign-s like follows: + // struct S { void opAssign(ref immutable S) const; } + return f; + } + return null; +} + +/******************************************* + * We need an opAssign for the struct if + * it has a destructor or a postblit. + * We need to generate one if a user-specified one does not exist. + */ +extern (C++) bool needOpAssign(StructDeclaration sd) +{ + //printf("StructDeclaration::needOpAssign() %s\n", sd->toChars()); + if (sd.hasIdentityAssign) + goto Lneed; + // because has identity==elaborate opAssign + if (sd.dtor || sd.postblit) + goto Lneed; + /* If any of the fields need an opAssign, then we + * need it too. + */ + for (size_t i = 0; i < sd.fields.dim; i++) + { + VarDeclaration v = sd.fields[i]; + if (v.storage_class & STCref) + continue; + Type tv = v.type.baseElemOf(); + if (tv.ty == Tstruct) + { + TypeStruct ts = cast(TypeStruct)tv; + if (needOpAssign(ts.sym)) + goto Lneed; + } + } + //printf("\tdontneed\n"); + return false; +Lneed: + //printf("\tneed\n"); + return true; +} + +/****************************************** + * Build opAssign for struct. + * ref S opAssign(S s) { ... } + * + * Note that s will be constructed onto the stack, and probably + * copy-constructed in caller site. + * + * If S has copy copy construction and/or destructor, + * the body will make bit-wise object swap: + * S __swap = this; // bit copy + * this = s; // bit copy + * __swap.dtor(); + * Instead of running the destructor on s, run it on tmp instead. + * + * Otherwise, the body will make member-wise assignments: + * Then, the body is: + * this.field1 = s.field1; + * this.field2 = s.field2; + * ...; + */ +extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) +{ + if (FuncDeclaration f = hasIdentityOpAssign(sd, sc)) + { + sd.hasIdentityAssign = true; + return f; + } + // Even if non-identity opAssign is defined, built-in identity opAssign + // will be defined. + if (!needOpAssign(sd)) + return null; + //printf("StructDeclaration::buildOpAssign() %s\n", sd->toChars()); + StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; + Loc declLoc = sd.loc; + Loc loc = Loc(); // internal code should have no loc to prevent coverage + if (sd.dtor || sd.postblit) + { + if (!sd.type.isAssignable()) // Bugzilla 13044 + return null; + stc = mergeFuncAttrs(stc, sd.dtor); + if (stc & STCsafe) + stc = (stc & ~STCsafe) | STCtrusted; + } + else + { + for (size_t i = 0; i < sd.fields.dim; i++) + { + VarDeclaration v = sd.fields[i]; + if (v.storage_class & STCref) + continue; + Type tv = v.type.baseElemOf(); + if (tv.ty != Tstruct) + continue; + StructDeclaration sdv = (cast(TypeStruct)tv).sym; + stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc)); + } + } + auto fparams = new Parameters(); + fparams.push(new Parameter(STCnodtor, sd.type, Id.p, null)); + auto tf = new TypeFunction(fparams, sd.handleType(), 0, LINKd, stc | STCref); + auto fop = new FuncDeclaration(declLoc, Loc(), Id.assign, stc, tf); + Expression e = null; + if (stc & STCdisable) + { + } + else if (sd.dtor || sd.postblit) + { + /* Do swap this and rhs + * __swap = this; this = s; __swap.dtor(); + */ + //printf("\tswap copy\n"); + Identifier idtmp = Identifier.generateId("__swap"); + VarDeclaration tmp = null; + AssignExp ec = null; + if (sd.dtor) + { + tmp = new VarDeclaration(loc, sd.type, idtmp, new VoidInitializer(loc)); + tmp.noscope = 1; + tmp.storage_class |= STCtemp | STCctfe; + e = new DeclarationExp(loc, tmp); + ec = new BlitExp(loc, new VarExp(loc, tmp), new ThisExp(loc)); + e = Expression.combine(e, ec); + } + ec = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p)); + e = Expression.combine(e, ec); + if (sd.dtor) + { + /* Instead of running the destructor on s, run it + * on tmp. This avoids needing to copy tmp back in to s. + */ + Expression ec2 = new DotVarExp(loc, new VarExp(loc, tmp), sd.dtor, 0); + ec2 = new CallExp(loc, ec2); + e = Expression.combine(e, ec2); + } + } + else + { + /* Do memberwise copy + */ + //printf("\tmemberwise copy\n"); + for (size_t i = 0; i < sd.fields.dim; i++) + { + VarDeclaration v = sd.fields[i]; + // this.v = s.v; + auto ec = new AssignExp(loc, new DotVarExp(loc, new ThisExp(loc), v, 0), new DotVarExp(loc, new IdentifierExp(loc, Id.p), v, 0)); + e = Expression.combine(e, ec); + } + } + if (e) + { + Statement s1 = new ExpStatement(loc, e); + /* Add: + * return this; + */ + e = new ThisExp(loc); + Statement s2 = new ReturnStatement(loc, e); + fop.fbody = new CompoundStatement(loc, s1, s2); + tf.isreturn = true; + } + sd.members.push(fop); + fop.addMember(sc, sd); + sd.hasIdentityAssign = true; // temporary mark identity assignable + uint errors = global.startGagging(); // Do not report errors, even if the + Scope* sc2 = sc.push(); + sc2.stc = 0; + sc2.linkage = LINKd; + fop.semantic(sc2); + fop.semantic2(sc2); + fop.semantic3(sc2); + sc2.pop(); + if (global.endGagging(errors)) // if errors happened + { + // Disable generated opAssign, because some members forbid identity assignment. + fop.storage_class |= STCdisable; + fop.fbody = null; // remove fbody which contains the error + } + //printf("-StructDeclaration::buildOpAssign() %s, errors = %d\n", sd->toChars(), (fop->storage_class & STCdisable) != 0); + return fop; +} + +/******************************************* + * We need an opEquals for the struct if + * any fields has an opEquals. + * Generate one if a user-specified one does not exist. + */ +extern (C++) bool needOpEquals(StructDeclaration sd) +{ + //printf("StructDeclaration::needOpEquals() %s\n", sd->toChars()); + if (sd.hasIdentityEquals) + goto Lneed; + if (sd.isUnionDeclaration()) + goto Ldontneed; + /* If any of the fields has an opEquals, then we + * need it too. + */ + for (size_t i = 0; i < sd.fields.dim; i++) + { + VarDeclaration v = sd.fields[i]; + if (v.storage_class & STCref) + continue; + Type tv = v.type.toBasetype(); + if (tv.isfloating()) + { + // This is necessray for: + // 1. comparison of +0.0 and -0.0 should be true. + // 2. comparison of NANs should be false always. + goto Lneed; + } + if (tv.ty == Tarray) + goto Lneed; + if (tv.ty == Taarray) + goto Lneed; + if (tv.ty == Tclass) + goto Lneed; + tv = tv.baseElemOf(); + if (tv.ty == Tstruct) + { + TypeStruct ts = cast(TypeStruct)tv; + if (needOpEquals(ts.sym)) + goto Lneed; + if (ts.sym.aliasthis) // Bugzilla 14806 + goto Lneed; + } + } +Ldontneed: + //printf("\tdontneed\n"); + return false; +Lneed: + //printf("\tneed\n"); + return true; +} + +/******************************************* + * Check given aggregate actually has an identity opEquals or not. + */ +extern (C++) FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc) +{ + Dsymbol eq = search_function(ad, Id.eq); + if (eq) + { + /* check identity opEquals exists + */ + Expression er = new NullExp(ad.loc, null); // dummy rvalue + Expression el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue + auto a = new Expressions(); + a.setDim(1); + for (size_t i = 0;; i++) + { + Type tthis = null; // dead-store to prevent spurious warning + if (i == 0) + tthis = ad.type; + if (i == 1) + tthis = ad.type.constOf(); + if (i == 2) + tthis = ad.type.immutableOf(); + if (i == 3) + tthis = ad.type.sharedOf(); + if (i == 4) + tthis = ad.type.sharedConstOf(); + if (i == 5) + break; + FuncDeclaration f = null; + uint errors = global.startGagging(); // Do not report errors, even if the + sc = sc.push(); + sc.tinst = null; + sc.minst = null; + for (size_t j = 0; j < 2; j++) + { + (*a)[0] = (j == 0 ? er : el); + (*a)[0].type = tthis; + f = resolveFuncCall(ad.loc, sc, eq, null, tthis, a, 1); + if (f) + break; + } + sc = sc.pop(); + global.endGagging(errors); + if (f) + { + if (f.errors) + return null; + return f; + } + } + } + return null; +} + +/****************************************** + * Build opEquals for struct. + * const bool opEquals(const S s) { ... } + * + * By fixing bugzilla 3789, opEquals is changed to be never implicitly generated. + * Now, struct objects comparison s1 == s2 is translated to: + * s1.tupleof == s2.tupleof + * to calculate structural equality. See EqualExp::semantic. + */ +extern (C++) FuncDeclaration buildOpEquals(StructDeclaration sd, Scope* sc) +{ + if (hasIdentityOpEquals(sd, sc)) + { + sd.hasIdentityEquals = true; + } + return null; +} + +/****************************************** + * Build __xopEquals for TypeInfo_Struct + * static bool __xopEquals(ref const S p, ref const S q) + * { + * return p == q; + * } + * + * This is called by TypeInfo.equals(p1, p2). If the struct does not support + * const objects comparison, it will throw "not implemented" Error in runtime. + */ +extern (C++) FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) +{ + if (!needOpEquals(sd)) + return null; // bitwise comparison would work + //printf("StructDeclaration::buildXopEquals() %s\n", sd->toChars()); + if (Dsymbol eq = search_function(sd, Id.eq)) + { + if (FuncDeclaration fd = eq.isFuncDeclaration()) + { + TypeFunction tfeqptr; + { + Scope scx; + /* const bool opEquals(ref const S s); + */ + auto parameters = new Parameters(); + parameters.push(new Parameter(STCref | STCconst, sd.type, null, null)); + tfeqptr = new TypeFunction(parameters, Type.tbool, 0, LINKd); + tfeqptr.mod = MODconst; + tfeqptr = cast(TypeFunction)tfeqptr.semantic(Loc(), &scx); + } + fd = fd.overloadExactMatch(tfeqptr); + if (fd) + return fd; + } + } + if (!sd.xerreq) + { + // object._xopEquals + Identifier id = Identifier.idPool("_xopEquals"); + Expression e = new IdentifierExp(sd.loc, Id.empty); + e = new DotIdExp(sd.loc, e, Id.object); + e = new DotIdExp(sd.loc, e, id); + e = e.semantic(sc); + Dsymbol s = getDsymbol(e); + assert(s); + sd.xerreq = s.isFuncDeclaration(); + } + Loc declLoc = Loc(); // loc is unnecessary so __xopEquals is never called directly + Loc loc = Loc(); // loc is unnecessary so errors are gagged + auto parameters = new Parameters(); + parameters.push(new Parameter(STCref | STCconst, sd.type, Id.p, null)); + parameters.push(new Parameter(STCref | STCconst, sd.type, Id.q, null)); + auto tf = new TypeFunction(parameters, Type.tbool, 0, LINKd); + Identifier id = Id.xopEquals; + auto fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); + Expression e1 = new IdentifierExp(loc, Id.p); + Expression e2 = new IdentifierExp(loc, Id.q); + Expression e = new EqualExp(TOKequal, loc, e1, e2); + fop.fbody = new ReturnStatement(loc, e); + uint errors = global.startGagging(); // Do not report errors + Scope* sc2 = sc.push(); + sc2.stc = 0; + sc2.linkage = LINKd; + fop.semantic(sc2); + fop.semantic2(sc2); + sc2.pop(); + if (global.endGagging(errors)) // if errors happened + fop = sd.xerreq; + return fop; +} + +/****************************************** + * Build __xopCmp for TypeInfo_Struct + * static bool __xopCmp(ref const S p, ref const S q) + * { + * return p.opCmp(q); + * } + * + * This is called by TypeInfo.compare(p1, p2). If the struct does not support + * const objects comparison, it will throw "not implemented" Error in runtime. + */ +extern (C++) FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) +{ + //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); + if (Dsymbol cmp = search_function(sd, Id.cmp)) + { + if (FuncDeclaration fd = cmp.isFuncDeclaration()) + { + TypeFunction tfcmpptr; + { + Scope scx; + /* const int opCmp(ref const S s); + */ + auto parameters = new Parameters(); + parameters.push(new Parameter(STCref | STCconst, sd.type, null, null)); + tfcmpptr = new TypeFunction(parameters, Type.tint32, 0, LINKd); + tfcmpptr.mod = MODconst; + tfcmpptr = cast(TypeFunction)tfcmpptr.semantic(Loc(), &scx); + } + fd = fd.overloadExactMatch(tfcmpptr); + if (fd) + return fd; + } + } + else + { + version (none) + { + // FIXME: doesn't work for recursive alias this + /* Check opCmp member exists. + * Consider 'alias this', but except opDispatch. + */ + Expression e = new DsymbolExp(sd.loc, sd); + e = new DotIdExp(sd.loc, e, Id.cmp); + Scope* sc2 = sc.push(); + e = e.trySemantic(sc2); + sc2.pop(); + if (e) + { + Dsymbol s = null; + switch (e.op) + { + case TOKoverloadset: + s = (cast(OverExp)e).vars; + break; + case TOKimport: + s = (cast(ScopeExp)e).sds; + break; + case TOKvar: + s = (cast(VarExp)e).var; + break; + default: + break; + } + if (!s || s.ident != Id.cmp) + e = null; // there's no valid member 'opCmp' + } + if (!e) + return null; // bitwise comparison would work + /* Essentially, a struct which does not define opCmp is not comparable. + * At this time, typeid(S).compare might be correct that throwing "not implement" Error. + * But implementing it would break existing code, such as: + * + * struct S { int value; } // no opCmp + * int[S] aa; // Currently AA key uses bitwise comparison + * // (It's default behavior of TypeInfo_Strust.compare). + * + * Not sure we should fix this inconsistency, so just keep current behavior. + */ + } + else + { + return null; + } + } + if (!sd.xerrcmp) + { + // object._xopCmp + Identifier id = Identifier.idPool("_xopCmp"); + Expression e = new IdentifierExp(sd.loc, Id.empty); + e = new DotIdExp(sd.loc, e, Id.object); + e = new DotIdExp(sd.loc, e, id); + e = e.semantic(sc); + Dsymbol s = getDsymbol(e); + assert(s); + sd.xerrcmp = s.isFuncDeclaration(); + } + Loc declLoc = Loc(); // loc is unnecessary so __xopCmp is never called directly + Loc loc = Loc(); // loc is unnecessary so errors are gagged + auto parameters = new Parameters(); + parameters.push(new Parameter(STCref | STCconst, sd.type, Id.p, null)); + parameters.push(new Parameter(STCref | STCconst, sd.type, Id.q, null)); + auto tf = new TypeFunction(parameters, Type.tint32, 0, LINKd); + Identifier id = Id.xopCmp; + auto fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); + Expression e1 = new IdentifierExp(loc, Id.p); + Expression e2 = new IdentifierExp(loc, Id.q); + Expression e = new CallExp(loc, new DotIdExp(loc, e2, Id.cmp), e1); + fop.fbody = new ReturnStatement(loc, e); + uint errors = global.startGagging(); // Do not report errors + Scope* sc2 = sc.push(); + sc2.stc = 0; + sc2.linkage = LINKd; + fop.semantic(sc2); + fop.semantic2(sc2); + sc2.pop(); + if (global.endGagging(errors)) // if errors happened + fop = sd.xerrcmp; + return fop; +} + +/******************************************* + * We need a toHash for the struct if + * any fields has a toHash. + * Generate one if a user-specified one does not exist. + */ +extern (C++) bool needToHash(StructDeclaration sd) +{ + //printf("StructDeclaration::needToHash() %s\n", sd->toChars()); + if (sd.xhash) + goto Lneed; + if (sd.isUnionDeclaration()) + goto Ldontneed; + /* If any of the fields has an opEquals, then we + * need it too. + */ + for (size_t i = 0; i < sd.fields.dim; i++) + { + VarDeclaration v = sd.fields[i]; + if (v.storage_class & STCref) + continue; + Type tv = v.type.toBasetype(); + if (tv.isfloating()) + { + // This is necessray for: + // 1. comparison of +0.0 and -0.0 should be true. + goto Lneed; + } + if (tv.ty == Tarray) + goto Lneed; + if (tv.ty == Taarray) + goto Lneed; + if (tv.ty == Tclass) + goto Lneed; + tv = tv.baseElemOf(); + if (tv.ty == Tstruct) + { + TypeStruct ts = cast(TypeStruct)tv; + if (needToHash(ts.sym)) + goto Lneed; + } + } +Ldontneed: + //printf("\tdontneed\n"); + return false; +Lneed: + //printf("\tneed\n"); + return true; +} + +/****************************************** + * Build __xtoHash for non-bitwise hashing + * static hash_t xtoHash(ref const S p) nothrow @trusted; + */ +extern (C++) FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) +{ + if (Dsymbol s = search_function(sd, Id.tohash)) + { + static __gshared TypeFunction tftohash; + if (!tftohash) + { + tftohash = new TypeFunction(null, Type.thash_t, 0, LINKd); + tftohash.mod = MODconst; + tftohash = cast(TypeFunction)tftohash.merge(); + } + if (FuncDeclaration fd = s.isFuncDeclaration()) + { + fd = fd.overloadExactMatch(tftohash); + if (fd) + return fd; + } + } + if (!needToHash(sd)) + return null; + //printf("StructDeclaration::buildXtoHash() %s\n", sd->toPrettyChars()); + Loc declLoc = Loc(); // loc is unnecessary so __xtoHash is never called directly + Loc loc = Loc(); // internal code should have no loc to prevent coverage + auto parameters = new Parameters(); + parameters.push(new Parameter(STCref | STCconst, sd.type, Id.p, null)); + auto tf = new TypeFunction(parameters, Type.thash_t, 0, LINKd, STCnothrow | STCtrusted); + Identifier id = Id.xtoHash; + auto fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); + const(char)* code = "size_t h = 0;foreach (i, T; typeof(p.tupleof)) h += typeid(T).getHash(cast(const void*)&p.tupleof[i]);return h;"; + fop.fbody = new CompileStatement(loc, new StringExp(loc, cast(char*)code)); + Scope* sc2 = sc.push(); + sc2.stc = 0; + sc2.linkage = LINKd; + fop.semantic(sc2); + fop.semantic2(sc2); + sc2.pop(); + //printf("%s fop = %s %s\n", sd->toChars(), fop->toChars(), fop->type->toChars()); + return fop; +} + +/***************************************** + * Create inclusive postblit for struct by aggregating + * all the postblits in postblits[] with the postblits for + * all the members. + * Note the close similarity with AggregateDeclaration::buildDtor(), + * and the ordering changes (runs forward instead of backwards). + */ +extern (C++) FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) +{ + //printf("StructDeclaration::buildPostBlit() %s\n", sd->toChars()); + StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; + Loc declLoc = sd.postblits.dim ? sd.postblits[0].loc : sd.loc; + Loc loc = Loc(); // internal code should have no loc to prevent coverage + for (size_t i = 0; i < sd.postblits.dim; i++) + { + stc |= sd.postblits[i].storage_class & STCdisable; + } + Statements* a = null; + for (size_t i = 0; i < sd.fields.dim && !(stc & STCdisable); i++) + { + VarDeclaration v = sd.fields[i]; + if (v.storage_class & STCref) + continue; + Type tv = v.type.baseElemOf(); + if (tv.ty != Tstruct || !v.type.size()) + continue; + StructDeclaration sdv = (cast(TypeStruct)tv).sym; + if (!sdv.postblit) + continue; + stc = mergeFuncAttrs(stc, sdv.postblit); + stc = mergeFuncAttrs(stc, sdv.dtor); + if (stc & STCdisable) + { + a = null; + break; + } + if (!a) + a = new Statements(); + Expression ex = new ThisExp(loc); + ex = new DotVarExp(loc, ex, v, 0); + if (v.type.toBasetype().ty == Tstruct) + { + // this.v.__xpostblit() + // This is a hack so we can call postblits on const/immutable objects. + ex = new AddrExp(loc, ex); + ex = new CastExp(loc, ex, v.type.mutableOf().pointerTo()); + ex = new PtrExp(loc, ex); + if (stc & STCsafe) + stc = (stc & ~STCsafe) | STCtrusted; + ex = new DotVarExp(loc, ex, sdv.postblit, 0); + ex = new CallExp(loc, ex); + } + else + { + // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n]) + // This is a hack so we can call postblits on const/immutable objects. + ex = new DotIdExp(loc, ex, Id.ptr); + ex = new CastExp(loc, ex, sdv.type.pointerTo()); + if (stc & STCsafe) + stc = (stc & ~STCsafe) | STCtrusted; + uinteger_t n = v.type.size() / sdv.type.size(); + ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), new IntegerExp(loc, n, Type.tsize_t)); + // Prevent redundant bounds check + (cast(SliceExp)ex).upperIsInBounds = true; + (cast(SliceExp)ex).lowerIsLessThanUpper = true; + ex = new CallExp(loc, new IdentifierExp(loc, Id._ArrayPostblit), ex); + } + a.push(new ExpStatement(loc, ex)); // combine in forward order + /* Bugzilla 10972: When the following field postblit calls fail, + * this field should be destructed for Exception Safety. + */ + if (!sdv.dtor) + continue; + ex = new ThisExp(loc); + ex = new DotVarExp(loc, ex, v, 0); + if (v.type.toBasetype().ty == Tstruct) + { + // this.v.__xdtor() + // This is a hack so we can call destructors on const/immutable objects. + ex = new AddrExp(loc, ex); + ex = new CastExp(loc, ex, v.type.mutableOf().pointerTo()); + ex = new PtrExp(loc, ex); + if (stc & STCsafe) + stc = (stc & ~STCsafe) | STCtrusted; + ex = new DotVarExp(loc, ex, sdv.dtor, 0); + ex = new CallExp(loc, ex); + } + else + { + // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) + // This is a hack so we can call destructors on const/immutable objects. + ex = new DotIdExp(loc, ex, Id.ptr); + ex = new CastExp(loc, ex, sdv.type.pointerTo()); + if (stc & STCsafe) + stc = (stc & ~STCsafe) | STCtrusted; + uinteger_t n = v.type.size() / sdv.type.size(); + ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), new IntegerExp(loc, n, Type.tsize_t)); + // Prevent redundant bounds check + (cast(SliceExp)ex).upperIsInBounds = true; + (cast(SliceExp)ex).lowerIsLessThanUpper = true; + ex = new CallExp(loc, new IdentifierExp(loc, Id._ArrayDtor), ex); + } + a.push(new OnScopeStatement(loc, TOKon_scope_failure, new ExpStatement(loc, ex))); + } + /* Build our own "postblit" which executes a + */ + if (a || (stc & STCdisable)) + { + //printf("Building __fieldPostBlit()\n"); + auto dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id.__fieldPostblit); + dd.fbody = a ? new CompoundStatement(loc, a) : null; + sd.postblits.shift(dd); + sd.members.push(dd); + dd.semantic(sc); + } + FuncDeclaration xpostblit = null; + switch (sd.postblits.dim) + { + case 0: + break; + case 1: + xpostblit = sd.postblits[0]; + break; + default: + Expression e = null; + stc = STCsafe | STCnothrow | STCpure | STCnogc; + for (size_t i = 0; i < sd.postblits.dim; i++) + { + FuncDeclaration fd = sd.postblits[i]; + stc = mergeFuncAttrs(stc, fd); + if (stc & STCdisable) + { + e = null; + break; + } + Expression ex = new ThisExp(loc); + ex = new DotVarExp(loc, ex, fd, 0); + ex = new CallExp(loc, ex); + e = Expression.combine(e, ex); + } + auto dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id.__aggrPostblit); + dd.fbody = new ExpStatement(loc, e); + sd.members.push(dd); + dd.semantic(sc); + xpostblit = dd; + break; + } + // Add an __xpostblit alias to make the inclusive postblit accessible + if (xpostblit) + { + auto _alias = new AliasDeclaration(Loc(), Id.__xpostblit, xpostblit); + _alias.semantic(sc); + sd.members.push(_alias); + _alias.addMember(sc, sd); // add to symbol table + } + return xpostblit; +} + +/***************************************** + * Create inclusive destructor for struct/class by aggregating + * all the destructors in dtors[] with the destructors for + * all the members. + * Note the close similarity with StructDeclaration::buildPostBlit(), + * and the ordering changes (runs backward instead of forwards). + */ +extern (C++) FuncDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) +{ + //printf("AggregateDeclaration::buildDtor() %s\n", ad->toChars()); + StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; + Loc declLoc = ad.dtors.dim ? ad.dtors[0].loc : ad.loc; + Loc loc = Loc(); // internal code should have no loc to prevent coverage + Expression e = null; + for (size_t i = 0; i < ad.fields.dim; i++) + { + VarDeclaration v = ad.fields[i]; + if (v.storage_class & STCref) + continue; + Type tv = v.type.baseElemOf(); + if (tv.ty != Tstruct || !v.type.size()) + continue; + StructDeclaration sdv = (cast(TypeStruct)tv).sym; + if (!sdv.dtor) + continue; + stc = mergeFuncAttrs(stc, sdv.dtor); + if (stc & STCdisable) + { + e = null; + break; + } + Expression ex = new ThisExp(loc); + ex = new DotVarExp(loc, ex, v, 0); + if (v.type.toBasetype().ty == Tstruct) + { + // this.v.__xdtor() + // This is a hack so we can call destructors on const/immutable objects. + ex = new AddrExp(loc, ex); + ex = new CastExp(loc, ex, v.type.mutableOf().pointerTo()); + ex = new PtrExp(loc, ex); + if (stc & STCsafe) + stc = (stc & ~STCsafe) | STCtrusted; + ex = new DotVarExp(loc, ex, sdv.dtor, 0); + ex = new CallExp(loc, ex); + } + else + { + // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) + // This is a hack so we can call destructors on const/immutable objects. + ex = new DotIdExp(loc, ex, Id.ptr); + ex = new CastExp(loc, ex, sdv.type.pointerTo()); + if (stc & STCsafe) + stc = (stc & ~STCsafe) | STCtrusted; + uinteger_t n = v.type.size() / sdv.type.size(); + ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), new IntegerExp(loc, n, Type.tsize_t)); + // Prevent redundant bounds check + (cast(SliceExp)ex).upperIsInBounds = true; + (cast(SliceExp)ex).lowerIsLessThanUpper = true; + ex = new CallExp(loc, new IdentifierExp(loc, Id._ArrayDtor), ex); + } + e = Expression.combine(ex, e); // combine in reverse order + } + /* Build our own "destructor" which executes e + */ + if (e || (stc & STCdisable)) + { + //printf("Building __fieldDtor()\n"); + auto dd = new DtorDeclaration(declLoc, Loc(), stc, Id.__fieldDtor); + dd.fbody = new ExpStatement(loc, e); + ad.dtors.shift(dd); + ad.members.push(dd); + dd.semantic(sc); + } + FuncDeclaration xdtor = null; + switch (ad.dtors.dim) + { + case 0: + break; + case 1: + xdtor = ad.dtors[0]; + break; + default: + e = null; + stc = STCsafe | STCnothrow | STCpure | STCnogc; + for (size_t i = 0; i < ad.dtors.dim; i++) + { + FuncDeclaration fd = ad.dtors[i]; + stc = mergeFuncAttrs(stc, fd); + if (stc & STCdisable) + { + e = null; + break; + } + Expression ex = new ThisExp(loc); + ex = new DotVarExp(loc, ex, fd, 0); + ex = new CallExp(loc, ex); + e = Expression.combine(ex, e); + } + auto dd = new DtorDeclaration(declLoc, Loc(), stc, Id.__aggrDtor); + dd.fbody = new ExpStatement(loc, e); + ad.members.push(dd); + dd.semantic(sc); + xdtor = dd; + break; + } + // Add an __xdtor alias to make the inclusive dtor accessible + if (xdtor) + { + auto _alias = new AliasDeclaration(Loc(), Id.__xdtor, xdtor); + _alias.semantic(sc); + ad.members.push(_alias); + _alias.addMember(sc, ad); // add to symbol table + } + return xdtor; +} + +/****************************************** + * Create inclusive invariant for struct/class by aggregating + * all the invariants in invs[]. + * void __invariant() const [pure nothrow @trusted] + * { + * invs[0](), invs[1](), ...; + * } + */ +extern (C++) FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) +{ + StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; + Loc declLoc = ad.loc; + Loc loc = Loc(); // internal code should have no loc to prevent coverage + switch (ad.invs.dim) + { + case 0: + return null; + case 1: + // Don't return invs[0] so it has uniquely generated name. + /* fall through */ + default: + Expression e = null; + StorageClass stcx = 0; + for (size_t i = 0; i < ad.invs.dim; i++) + { + stc = mergeFuncAttrs(stc, ad.invs[i]); + if (stc & STCdisable) + { + // What should do? + } + StorageClass stcy = (ad.invs[i].storage_class & STCsynchronized) | (ad.invs[i].type.mod & MODshared ? STCshared : 0); + if (i == 0) + stcx = stcy; + else if (stcx ^ stcy) + { + version (all) + { + // currently rejects + ad.error(ad.invs[i].loc, "mixing invariants with shared/synchronized differene is not supported"); + e = null; + break; + } + } + e = Expression.combine(e, new CallExp(loc, new VarExp(loc, ad.invs[i]))); + } + InvariantDeclaration inv; + inv = new InvariantDeclaration(declLoc, Loc(), stc | stcx, Id.classInvariant); + inv.fbody = new ExpStatement(loc, e); + ad.members.push(inv); + inv.semantic(sc); + return inv; + } +} diff --git a/src/cond.c b/src/cond.c deleted file mode 100644 index 4f8a4ad2f6bf..000000000000 --- a/src/cond.c +++ /dev/null @@ -1,388 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/cond.c - */ - -#include -#include -#include // strcmp() - -#include "id.h" -#include "init.h" -#include "declaration.h" -#include "identifier.h" -#include "expression.h" -#include "cond.h" -#include "module.h" -#include "template.h" -#include "mtype.h" -#include "scope.h" -#include "arraytypes.h" -#include "tokens.h" - -int findCondition(Strings *ids, Identifier *ident) -{ - if (ids) - { - for (size_t i = 0; i < ids->dim; i++) - { - const char *id = (*ids)[i]; - - if (strcmp(id, ident->toChars()) == 0) - return true; - } - } - - return false; -} - -/* ============================================================ */ - -Condition::Condition(Loc loc) -{ - this->loc = loc; - inc = 0; -} - -/* ============================================================ */ - -DVCondition::DVCondition(Module *mod, unsigned level, Identifier *ident) - : Condition(Loc()) -{ - this->mod = mod; - this->level = level; - this->ident = ident; -} - -Condition *DVCondition::syntaxCopy() -{ - return this; // don't need to copy -} - -/* ============================================================ */ - -void DebugCondition::setGlobalLevel(unsigned level) -{ - global.params.debuglevel = level; -} - -void DebugCondition::addGlobalIdent(const char *ident) -{ - if (!global.params.debugids) - global.params.debugids = new Strings(); - global.params.debugids->push((char *)ident); -} - - -DebugCondition::DebugCondition(Module *mod, unsigned level, Identifier *ident) - : DVCondition(mod, level, ident) -{ -} - -// Helper for printing dependency information -void printDepsConditional(Scope *sc, DVCondition* condition, const char* depType) -{ - if (!global.params.moduleDeps || global.params.moduleDepsFile) - return; - OutBuffer *ob = global.params.moduleDeps; - Module* imod = sc ? sc->instantiatingModule() : condition->mod; - if (!imod) - return; - ob->writestring(depType); - ob->writestring(imod->toPrettyChars()); - ob->writestring(" ("); - escapePath(ob, imod->srcfile->toChars()); - ob->writestring(") : "); - if (condition->ident) - ob->printf("%s\n", condition->ident->toChars()); - else - ob->printf("%d\n", condition->level); -} - - -int DebugCondition::include(Scope *sc, ScopeDsymbol *sds) -{ - //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); - if (inc == 0) - { - inc = 2; - bool definedInModule = false; - if (ident) - { - if (findCondition(mod->debugids, ident)) - { - inc = 1; - definedInModule = true; - } - else if (findCondition(global.params.debugids, ident)) - inc = 1; - else - { if (!mod->debugidsNot) - mod->debugidsNot = new Strings(); - mod->debugidsNot->push(ident->toChars()); - } - } - else if (level <= global.params.debuglevel || level <= mod->debuglevel) - inc = 1; - if (!definedInModule) - printDepsConditional(sc, this, "depsDebug "); - } - return (inc == 1); -} - -/* ============================================================ */ - -void VersionCondition::setGlobalLevel(unsigned level) -{ - global.params.versionlevel = level; -} - -bool VersionCondition::isPredefined(const char *ident) -{ - static const char* reserved[] = - { - "DigitalMars", - "GNU", - "LDC", - "SDC", - "Windows", - "Win32", - "Win64", - "linux", - "OSX", - "FreeBSD", - "OpenBSD", - "NetBSD", - "DragonFlyBSD", - "BSD", - "Solaris", - "Posix", - "AIX", - "Haiku", - "SkyOS", - "SysV3", - "SysV4", - "Hurd", - "Android", - "Cygwin", - "MinGW", - "FreeStanding", - "X86", - "X86_64", - "ARM", - "ARM_Thumb", - "ARM_SoftFloat", - "ARM_SoftFP", - "ARM_HardFloat", - "AArch64", - "Epiphany", - "PPC", - "PPC_SoftFloat", - "PPC_HardFloat", - "PPC64", - "IA64", - "MIPS32", - "MIPS64", - "MIPS_O32", - "MIPS_N32", - "MIPS_O64", - "MIPS_N64", - "MIPS_EABI", - "MIPS_SoftFloat", - "MIPS_HardFloat", - "NVPTX", - "NVPTX64", - "SPARC", - "SPARC_V8Plus", - "SPARC_SoftFloat", - "SPARC_HardFloat", - "SPARC64", - "S390", - "S390X", - "HPPA", - "HPPA64", - "SH", - "SH64", - "Alpha", - "Alpha_SoftFloat", - "Alpha_HardFloat", - "LittleEndian", - "BigEndian", - "ELFv1", - "ELFv2", - "CRuntime_Digitalmars", - "CRuntime_Glibc", - "CRuntime_Microsoft", - "D_Coverage", - "D_Ddoc", - "D_InlineAsm_X86", - "D_InlineAsm_X86_64", - "D_LP64", - "D_X32", - "D_HardFloat", - "D_SoftFloat", - "D_PIC", - "D_SIMD", - "D_Version2", - "D_NoBoundsChecks", - "unittest", - "assert", - "all", - "none", - NULL - }; - - for (unsigned i = 0; reserved[i]; i++) - { - if (strcmp(ident, reserved[i]) == 0) - return true; - } - - if (ident[0] == 'D' && ident[1] == '_') - return true; - return false; -} - -void VersionCondition::addGlobalIdent(const char *ident) -{ - checkPredefined(Loc(), ident); - addPredefinedGlobalIdent(ident); -} - -void VersionCondition::addPredefinedGlobalIdent(const char *ident) -{ - if (!global.params.versionids) - global.params.versionids = new Strings(); - global.params.versionids->push((char *)ident); -} - - -VersionCondition::VersionCondition(Module *mod, unsigned level, Identifier *ident) - : DVCondition(mod, level, ident) -{ -} - -int VersionCondition::include(Scope *sc, ScopeDsymbol *sds) -{ - //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel); - //if (ident) printf("\tident = '%s'\n", ident->toChars()); - if (inc == 0) - { - inc = 2; - bool definedInModule=false; - if (ident) - { - if (findCondition(mod->versionids, ident)) - { - inc = 1; - definedInModule = true; - } - else if (findCondition(global.params.versionids, ident)) - inc = 1; - else - { - if (!mod->versionidsNot) - mod->versionidsNot = new Strings(); - mod->versionidsNot->push(ident->toChars()); - } - } - else if (level <= global.params.versionlevel || level <= mod->versionlevel) - inc = 1; - if (!definedInModule && (!ident || (!isPredefined(ident->toChars()) && ident != Identifier::idPool(Token::toChars(TOKunittest)) && ident != Identifier::idPool(Token::toChars(TOKassert))))) - printDepsConditional(sc, this, "depsVersion "); - } - return (inc == 1); -} - -/**************************** StaticIfCondition *******************************/ - -StaticIfCondition::StaticIfCondition(Loc loc, Expression *exp) - : Condition(loc) -{ - this->exp = exp; - this->nest = 0; -} - -Condition *StaticIfCondition::syntaxCopy() -{ - return new StaticIfCondition(loc, exp->syntaxCopy()); -} - -int StaticIfCondition::include(Scope *sc, ScopeDsymbol *sds) -{ -#if 0 - printf("StaticIfCondition::include(sc = %p, sds = %p) this=%p inc = %d\n", sc, sds, this, inc); - if (sds) - { - printf("\ts = '%s', kind = %s\n", sds->toChars(), sds->kind()); - } -#endif - if (inc == 0) - { - if (exp->op == TOKerror || nest > 100) - { - error(loc, (nest > 1000) ? "unresolvable circular static if expression" - : "error evaluating static if expression"); - goto Lerror; - } - - if (!sc) - { - error(loc, "static if conditional cannot be at global scope"); - inc = 2; - return 0; - } - - ++nest; - sc = sc->push(sc->scopesym); - sc->sds = sds; // sds gets any addMember() - //sc->speculative = true; // TODO: static if (is(T U)) { /* U is available */ } - sc->flags |= SCOPEcondition; - - sc = sc->startCTFE(); - Expression *e = exp->semantic(sc); - e = resolveProperties(sc, e); - sc = sc->endCTFE(); - - sc->pop(); - --nest; - - // Prevent repeated condition evaluation. - // See: fail_compilation/fail7815.d - if (inc != 0) - return (inc == 1); - - 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()); - goto Lerror; - } - e = e->ctfeInterpret(); - if (e->op == TOKerror) - { - goto Lerror; - } - else if (e->isBool(true)) - inc = 1; - else if (e->isBool(false)) - inc = 2; - else - { - e->error("expression %s is not constant or does not evaluate to a bool", e->toChars()); - goto Lerror; - } - } - return (inc == 1); - -Lerror: - if (!global.gag) - inc = 2; // so we don't see the error message again - return 0; -} diff --git a/src/cond.d b/src/cond.d new file mode 100644 index 000000000000..4b18344adb59 --- /dev/null +++ b/src/cond.d @@ -0,0 +1,433 @@ +// 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.cond; + +import core.stdc.string; +import ddmd.arraytypes, ddmd.dmodule, ddmd.dmodule, ddmd.dscope, ddmd.dsymbol, ddmd.errors, ddmd.expression, ddmd.globals, ddmd.hdrgen, ddmd.identifier, ddmd.mars, ddmd.mtype, ddmd.root.outbuffer, ddmd.tokens, ddmd.visitor; + +extern (C++) class Condition +{ +public: + Loc loc; + // 0: not computed yet + // 1: include + // 2: do not include + int inc; + + /* ============================================================ */ + final extern (D) this(Loc loc) + { + this.loc = loc; + inc = 0; + } + + abstract Condition syntaxCopy(); + + abstract int include(Scope* sc, ScopeDsymbol sds); + + DebugCondition isDebugCondition() + { + return null; + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) class DVCondition : Condition +{ +public: + uint level; + Identifier ident; + Module mod; + + /* ============================================================ */ + final extern (D) this(Module mod, uint level, Identifier ident) + { + super(Loc()); + this.mod = mod; + this.level = level; + this.ident = ident; + } + + final Condition syntaxCopy() + { + return this; // don't need to copy + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) final class DebugCondition : DVCondition +{ +public: + /* ============================================================ */ + static void setGlobalLevel(uint level) + { + global.params.debuglevel = level; + } + + static void addGlobalIdent(const(char)* ident) + { + if (!global.params.debugids) + global.params.debugids = new Strings(); + global.params.debugids.push(cast(char*)ident); + } + + extern (D) this(Module mod, uint level, Identifier ident) + { + super(mod, level, ident); + } + + int include(Scope* sc, ScopeDsymbol sds) + { + //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); + if (inc == 0) + { + inc = 2; + bool definedInModule = false; + if (ident) + { + if (findCondition(mod.debugids, ident)) + { + inc = 1; + definedInModule = true; + } + else if (findCondition(global.params.debugids, ident)) + inc = 1; + else + { + if (!mod.debugidsNot) + mod.debugidsNot = new Strings(); + mod.debugidsNot.push(ident.toChars()); + } + } + else if (level <= global.params.debuglevel || level <= mod.debuglevel) + inc = 1; + if (!definedInModule) + printDepsConditional(sc, this, "depsDebug "); + } + return (inc == 1); + } + + DebugCondition isDebugCondition() + { + return this; + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) final class VersionCondition : DVCondition +{ +public: + /* ============================================================ */ + static void setGlobalLevel(uint level) + { + global.params.versionlevel = level; + } + + static bool isPredefined(const(char)* ident) + { + static __gshared const(char)** reserved = + [ + "DigitalMars", + "GNU", + "LDC", + "SDC", + "Windows", + "Win32", + "Win64", + "linux", + "OSX", + "FreeBSD", + "OpenBSD", + "NetBSD", + "DragonFlyBSD", + "BSD", + "Solaris", + "Posix", + "AIX", + "Haiku", + "SkyOS", + "SysV3", + "SysV4", + "Hurd", + "Android", + "Cygwin", + "MinGW", + "FreeStanding", + "X86", + "X86_64", + "ARM", + "ARM_Thumb", + "ARM_SoftFloat", + "ARM_SoftFP", + "ARM_HardFloat", + "AArch64", + "Epiphany", + "PPC", + "PPC_SoftFloat", + "PPC_HardFloat", + "PPC64", + "IA64", + "MIPS32", + "MIPS64", + "MIPS_O32", + "MIPS_N32", + "MIPS_O64", + "MIPS_N64", + "MIPS_EABI", + "MIPS_SoftFloat", + "MIPS_HardFloat", + "NVPTX", + "NVPTX64", + "SPARC", + "SPARC_V8Plus", + "SPARC_SoftFloat", + "SPARC_HardFloat", + "SPARC64", + "S390", + "S390X", + "HPPA", + "HPPA64", + "SH", + "SH64", + "Alpha", + "Alpha_SoftFloat", + "Alpha_HardFloat", + "LittleEndian", + "BigEndian", + "ELFv1", + "ELFv2", + "CRuntime_Digitalmars", + "CRuntime_Glibc", + "CRuntime_Microsoft", + "D_Coverage", + "D_Ddoc", + "D_InlineAsm_X86", + "D_InlineAsm_X86_64", + "D_LP64", + "D_X32", + "D_HardFloat", + "D_SoftFloat", + "D_PIC", + "D_SIMD", + "D_Version2", + "D_NoBoundsChecks", + "unittest", + "assert", + "all", + "none", + null + ]; + for (uint i = 0; reserved[i]; i++) + { + if (strcmp(ident, reserved[i]) == 0) + return true; + } + if (ident[0] == 'D' && ident[1] == '_') + return true; + return false; + } + + static void checkPredefined(Loc loc, const(char)* ident) + { + if (isPredefined(ident)) + error(loc, "version identifier '%s' is reserved and cannot be set", ident); + } + + static void addGlobalIdent(const(char)* ident) + { + checkPredefined(Loc(), ident); + addPredefinedGlobalIdent(ident); + } + + static void addPredefinedGlobalIdent(const(char)* ident) + { + if (!global.params.versionids) + global.params.versionids = new Strings(); + global.params.versionids.push(cast(char*)ident); + } + + extern (D) this(Module mod, uint level, Identifier ident) + { + super(mod, level, ident); + } + + int include(Scope* sc, ScopeDsymbol sds) + { + //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel); + //if (ident) printf("\tident = '%s'\n", ident->toChars()); + if (inc == 0) + { + inc = 2; + bool definedInModule = false; + if (ident) + { + if (findCondition(mod.versionids, ident)) + { + inc = 1; + definedInModule = true; + } + else if (findCondition(global.params.versionids, ident)) + inc = 1; + else + { + if (!mod.versionidsNot) + mod.versionidsNot = new Strings(); + mod.versionidsNot.push(ident.toChars()); + } + } + else if (level <= global.params.versionlevel || level <= mod.versionlevel) + inc = 1; + if (!definedInModule && (!ident || (!isPredefined(ident.toChars()) && ident != Identifier.idPool(Token.toChars(TOKunittest)) && ident != Identifier.idPool(Token.toChars(TOKassert))))) + printDepsConditional(sc, this, "depsVersion "); + } + return (inc == 1); + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) final class StaticIfCondition : Condition +{ +public: + Expression exp; + int nest; // limit circular dependencies + + /**************************** StaticIfCondition *******************************/ + extern (D) this(Loc loc, Expression exp) + { + super(loc); + this.exp = exp; + this.nest = 0; + } + + Condition syntaxCopy() + { + return new StaticIfCondition(loc, exp.syntaxCopy()); + } + + int include(Scope* sc, ScopeDsymbol sds) + { + version (none) + { + printf("StaticIfCondition::include(sc = %p, sds = %p) this=%p inc = %d\n", sc, sds, this, inc); + if (sds) + { + printf("\ts = '%s', kind = %s\n", sds.toChars(), sds.kind()); + } + } + if (inc == 0) + { + if (exp.op == TOKerror || nest > 100) + { + error(loc, (nest > 1000) ? "unresolvable circular static if expression" : "error evaluating static if expression"); + goto Lerror; + } + if (!sc) + { + error(loc, "static if conditional cannot be at global scope"); + inc = 2; + return 0; + } + ++nest; + sc = sc.push(sc.scopesym); + sc.sds = sds; // sds gets any addMember() + //sc->speculative = true; // TODO: static if (is(T U)) { /* U is available */ } + sc.flags |= SCOPEcondition; + sc = sc.startCTFE(); + Expression e = exp.semantic(sc); + e = resolveProperties(sc, e); + sc = sc.endCTFE(); + sc.pop(); + --nest; + // Prevent repeated condition evaluation. + // See: fail_compilation/fail7815.d + if (inc != 0) + return (inc == 1); + 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()); + goto Lerror; + } + e = e.ctfeInterpret(); + if (e.op == TOKerror) + { + goto Lerror; + } + else if (e.isBool(true)) + inc = 1; + else if (e.isBool(false)) + inc = 2; + else + { + e.error("expression %s is not constant or does not evaluate to a bool", e.toChars()); + goto Lerror; + } + } + return (inc == 1); + Lerror: + if (!global.gag) + inc = 2; // so we don't see the error message again + return 0; + } + + void accept(Visitor v) + { + v.visit(this); + } +} + +extern (C++) int findCondition(Strings* ids, Identifier ident) +{ + if (ids) + { + for (size_t i = 0; i < ids.dim; i++) + { + const(char)* id = (*ids)[i]; + if (strcmp(id, ident.toChars()) == 0) + return true; + } + } + return false; +} + +// Helper for printing dependency information +extern (C++) void printDepsConditional(Scope* sc, DVCondition condition, const(char)* depType) +{ + if (!global.params.moduleDeps || global.params.moduleDepsFile) + return; + OutBuffer* ob = global.params.moduleDeps; + Module imod = sc ? sc.instantiatingModule() : condition.mod; + if (!imod) + return; + ob.writestring(depType); + ob.writestring(imod.toPrettyChars()); + ob.writestring(" ("); + escapePath(ob, imod.srcfile.toChars()); + ob.writestring(") : "); + if (condition.ident) + ob.printf("%s\n", condition.ident.toChars()); + else + ob.printf("%d\n", condition.level); +} diff --git a/src/constfold.c b/src/constfold.c deleted file mode 100644 index 2a0c81ecac07..000000000000 --- a/src/constfold.c +++ /dev/null @@ -1,1999 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/constfold.c - */ - -#include -#include -#include -#include // mem{cpy|set|cmp}() -#include -#include - -#include "rmem.h" -#include "root.h" -#include "port.h" - -#include "mtype.h" -#include "expression.h" -#include "aggregate.h" -#include "declaration.h" -#include "utf.h" -#include "ctfe.h" - -#define LOG 0 - -int RealEquals(real_t x1, real_t x2); - -Expression *expType(Type *type, Expression *e) -{ - if (type != e->type) - { - e = e->copy(); - e->type = type; - } - return e; -} - -/* ================================== isConst() ============================== */ - -int isConst(Expression *e) -{ - //printf("Expression::isConst(): %s\n", e->toChars()); - switch (e->op) - { - case TOKint64: - case TOKfloat64: - case TOKcomplex80: - return 1; - case TOKnull: - return 0; - case TOKsymoff: - return 2; - default: - return 0; - } - assert(0); - return 0; -} - -/* =============================== constFold() ============================== */ - -/* The constFold() functions were redundant with the optimize() ones, - * and so have been folded in with them. - */ - -/* ========================================================================== */ - -UnionExp Neg(Type *type, Expression *e1) -{ - UnionExp ue; - Loc loc = e1->loc; - - if (e1->type->isreal()) - { - new(&ue) RealExp(loc, -e1->toReal(), type); - } - else if (e1->type->isimaginary()) - { - new(&ue) RealExp(loc, -e1->toImaginary(), type); - } - else if (e1->type->iscomplex()) - { - new(&ue) ComplexExp(loc, -e1->toComplex(), type); - } - else - { - new(&ue) IntegerExp(loc, -e1->toInteger(), type); - } - return ue; -} - -UnionExp Com(Type *type, Expression *e1) -{ - UnionExp ue; - Loc loc = e1->loc; - - new(&ue) IntegerExp(loc, ~e1->toInteger(), type); - return ue; -} - -UnionExp Not(Type *type, Expression *e1) -{ - UnionExp ue; - Loc loc = e1->loc; - - new(&ue) IntegerExp(loc, e1->isBool(false) ? 1 : 0, type); - return ue; -} - -UnionExp Bool(Type *type, Expression *e1) -{ - UnionExp ue; - Loc loc = e1->loc; - - new(&ue) IntegerExp(loc, e1->isBool(true) ? 1 : 0, type); - return ue; -} - -UnionExp Add(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - -#if LOG - printf("Add(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); -#endif - if (type->isreal()) - { - new(&ue) RealExp(loc, e1->toReal() + e2->toReal(), type); - } - else if (type->isimaginary()) - { - new(&ue) RealExp(loc, e1->toImaginary() + e2->toImaginary(), type); - } - else if (type->iscomplex()) - { - // This rigamarole is necessary so that -0.0 doesn't get - // converted to +0.0 by doing an extraneous add with +0.0 - complex_t c1; - real_t r1 = ldouble (0.0); - real_t i1 = ldouble (0.0); - - complex_t c2; - real_t r2 = ldouble (0.0); - real_t i2 = ldouble (0.0); - - complex_t v; - int x; - - if (e1->type->isreal()) - { - r1 = e1->toReal(); - x = 0; - } - else if (e1->type->isimaginary()) - { - i1 = e1->toImaginary(); - x = 3; - } - else - { - c1 = e1->toComplex(); - x = 6; - } - - if (e2->type->isreal()) - { - r2 = e2->toReal(); - } - else if (e2->type->isimaginary()) - { - i2 = e2->toImaginary(); - x += 1; - } - else - { - c2 = e2->toComplex(); - x += 2; - } - - switch (x) - { - case 0+0: v = complex_t(r1 + r2, 0); break; - case 0+1: v = complex_t(r1, i2); break; - case 0+2: v = complex_t(r1 + creall(c2), cimagl(c2)); break; - case 3+0: v = complex_t(r2, i1); break; - case 3+1: v = complex_t(0, i1 + i2); break; - case 3+2: v = complex_t(creall(c2), i1 + cimagl(c2)); break; - case 6+0: v = complex_t(creall(c1) + r2, cimagl(c2)); break; - case 6+1: v = complex_t(creall(c1), cimagl(c1) + i2); break; - case 6+2: v = c1 + c2; break; - default: assert(0); - } - new(&ue) ComplexExp(loc, v, type); - } - else if (e1->op == TOKsymoff) - { - SymOffExp *soe = (SymOffExp *)e1; - new(&ue) SymOffExp(loc, soe->var, soe->offset + e2->toInteger()); - ue.exp()->type = type; - } - else if (e2->op == TOKsymoff) - { - SymOffExp *soe = (SymOffExp *)e2; - new(&ue) SymOffExp(loc, soe->var, soe->offset + e1->toInteger()); - ue.exp()->type = type; - } - else - new(&ue) IntegerExp(loc, e1->toInteger() + e2->toInteger(), type); - return ue; -} - - -UnionExp Min(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - - if (type->isreal()) - { - new(&ue) RealExp(loc, e1->toReal() - e2->toReal(), type); - } - else if (type->isimaginary()) - { - new(&ue) RealExp(loc, e1->toImaginary() - e2->toImaginary(), type); - } - else if (type->iscomplex()) - { - // This rigamarole is necessary so that -0.0 doesn't get - // converted to +0.0 by doing an extraneous add with +0.0 - complex_t c1; - real_t r1 = ldouble (0.0); - real_t i1 = ldouble (0.0); - - complex_t c2; - real_t r2 = ldouble (0.0); - real_t i2 = ldouble (0.0); - - complex_t v; - int x; - - if (e1->type->isreal()) - { - r1 = e1->toReal(); - x = 0; - } - else if (e1->type->isimaginary()) - { - i1 = e1->toImaginary(); - x = 3; - } - else - { - c1 = e1->toComplex(); - x = 6; - } - - if (e2->type->isreal()) - { - r2 = e2->toReal(); - } - else if (e2->type->isimaginary()) - { - i2 = e2->toImaginary(); - x += 1; - } - else - { - c2 = e2->toComplex(); - x += 2; - } - - switch (x) - { - case 0+0: v = complex_t(r1 - r2, 0); break; - case 0+1: v = complex_t(r1, -i2); break; - case 0+2: v = complex_t(r1 - creall(c2), -cimagl(c2)); break; - case 3+0: v = complex_t(-r2, i1); break; - case 3+1: v = complex_t(0, i1 - i2); break; - case 3+2: v = complex_t(-creall(c2), i1 - cimagl(c2)); break; - case 6+0: v = complex_t(creall(c1) - r2, cimagl(c1)); break; - case 6+1: v = complex_t(creall(c1), cimagl(c1) - i2); break; - case 6+2: v = c1 - c2; break; - default: assert(0); - } - new(&ue) ComplexExp(loc, v, type); - } - else if (e1->op == TOKsymoff) - { - SymOffExp *soe = (SymOffExp *)e1; - new(&ue) SymOffExp(loc, soe->var, soe->offset - e2->toInteger()); - ue.exp()->type = type; - } - else - { - new(&ue) IntegerExp(loc, e1->toInteger() - e2->toInteger(), type); - } - return ue; -} - -UnionExp Mul(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - - if (type->isfloating()) - { - complex_t c; - d_float80 r; - - if (e1->type->isreal()) - { - r = e1->toReal(); - c = e2->toComplex(); - c = complex_t(r * creall(c), r * cimagl(c)); - } - else if (e1->type->isimaginary()) - { - r = e1->toImaginary(); - c = e2->toComplex(); - c = complex_t(-r * cimagl(c), r * creall(c)); - } - else if (e2->type->isreal()) - { - r = e2->toReal(); - c = e1->toComplex(); - c = complex_t(r * creall(c), r * cimagl(c)); - } - else if (e2->type->isimaginary()) - { - r = e2->toImaginary(); - c = e1->toComplex(); - c = complex_t(-r * cimagl(c), r * creall(c)); - } - else - c = e1->toComplex() * e2->toComplex(); - - if (type->isreal()) - new(&ue) RealExp(loc, creall(c), type); - else if (type->isimaginary()) - new(&ue) RealExp(loc, cimagl(c), type); - else if (type->iscomplex()) - new(&ue) ComplexExp(loc, c, type); - else - assert(0); - } - else - { - new(&ue) IntegerExp(loc, e1->toInteger() * e2->toInteger(), type); - } - return ue; -} - -UnionExp Div(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - - if (type->isfloating()) - { - complex_t c; - d_float80 r; - - //e1->type->print(); - //e2->type->print(); - if (e2->type->isreal()) - { - if (e1->type->isreal()) - { - new(&ue) RealExp(loc, e1->toReal() / e2->toReal(), type); - return ue; - } - r = e2->toReal(); - c = e1->toComplex(); - c = complex_t(creall(c) / r, cimagl(c) / r); - } - else if (e2->type->isimaginary()) - { - r = e2->toImaginary(); - c = e1->toComplex(); - c = complex_t(cimagl(c) / r, -creall(c) / r); - } - else - { - c = e1->toComplex() / e2->toComplex(); - } - - if (type->isreal()) - new(&ue) RealExp(loc, creall(c), type); - else if (type->isimaginary()) - new(&ue) RealExp(loc, cimagl(c), type); - else if (type->iscomplex()) - new(&ue) ComplexExp(loc, c, type); - else - assert(0); - } - else - { - sinteger_t n1; - sinteger_t n2; - sinteger_t n; - - n1 = e1->toInteger(); - n2 = e2->toInteger(); - if (n2 == 0) - { - e2->error("divide by 0"); - n2 = 1; - } - if (e1->type->isunsigned() || e2->type->isunsigned()) - n = ((dinteger_t) n1) / ((dinteger_t) n2); - else - n = n1 / n2; - new(&ue) IntegerExp(loc, n, type); - } - return ue; -} - -UnionExp Mod(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - - if (type->isfloating()) - { - complex_t c; - - if (e2->type->isreal()) - { - real_t r2 = e2->toReal(); - - c = complex_t(Port::fmodl(e1->toReal(), r2), Port::fmodl(e1->toImaginary(), r2)); - } - else if (e2->type->isimaginary()) - { - real_t i2 = e2->toImaginary(); - - c = complex_t(Port::fmodl(e1->toReal(), i2), Port::fmodl(e1->toImaginary(), i2)); - } - else - assert(0); - - if (type->isreal()) - new(&ue) RealExp(loc, creall(c), type); - else if (type->isimaginary()) - new(&ue) RealExp(loc, cimagl(c), type); - else if (type->iscomplex()) - new(&ue) ComplexExp(loc, c, type); - else - assert(0); - } - else - { - sinteger_t n1; - sinteger_t n2; - sinteger_t n; - - n1 = e1->toInteger(); - n2 = e2->toInteger(); - if (n2 == 0) - { - e2->error("divide by 0"); - n2 = 1; - } - if (n2 == -1 && !type->isunsigned()) - { - // Check for int.min % -1 - if (n1 == 0xFFFFFFFF80000000ULL && type->toBasetype()->ty != Tint64) - { - e2->error("integer overflow: int.min % -1"); - n2 = 1; - } - else if (n1 == 0x8000000000000000LL) // long.min % -1 - { - e2->error("integer overflow: long.min % -1"); - n2 = 1; - } - } - if (e1->type->isunsigned() || e2->type->isunsigned()) - n = ((dinteger_t) n1) % ((dinteger_t) n2); - else - n = n1 % n2; - new(&ue) IntegerExp(loc, n, type); - } - return ue; -} - -UnionExp Pow(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - - // Handle integer power operations. - if (e2->type->isintegral()) - { - dinteger_t n = e2->toInteger(); - bool neg; - - if (!e2->type->isunsigned() && (sinteger_t)n < 0) - { - if (e1->type->isintegral()) - { - new(&ue) CTFEExp(TOKcantexp); - return ue; - } - - // Don't worry about overflow, from now on n is unsigned. - neg = true; - n = -n; - } - else - neg = false; - - UnionExp ur, uv; - if (e1->type->iscomplex()) - { - new(&ur) ComplexExp(loc, e1->toComplex(), e1->type); - new(&uv) ComplexExp(loc, complex_t(1.0, 0.0), e1->type); - } - else if (e1->type->isfloating()) - { - new(&ur) RealExp(loc, e1->toReal(), e1->type); - new(&uv) RealExp(loc, ldouble(1.0), e1->type); - } - else - { - new(&ur) IntegerExp(loc, e1->toInteger(), e1->type); - new(&uv) IntegerExp(loc, 1, e1->type); - } - - Expression* r = ur.exp(); - Expression* v = uv.exp(); - while (n != 0) - { - if (n & 1) - { - // v = v * r; - uv = Mul(v->type, v, r); - } - n >>= 1; - // r = r * r - ur = Mul(r->type, r, r); - } - - if (neg) - { - // ue = 1.0 / v - UnionExp one; - new(&one) RealExp(loc, ldouble(1.0), v->type); - uv = Div(v->type, one.exp(), v); - } - - if (type->iscomplex()) - new(&ue) ComplexExp(loc, v->toComplex(), type); - else if (type->isintegral()) - new(&ue) IntegerExp(loc, v->toInteger(), type); - else - new(&ue) RealExp(loc, v->toReal(), type); - } - else if (e2->type->isfloating()) - { - // x ^^ y for x < 0 and y not an integer is not defined; so set result as NaN - if (e1->toReal() < 0.0) - { - new(&ue) RealExp(loc, Port::ldbl_nan, type); - } - else - new(&ue) CTFEExp(TOKcantexp); - } - else - new(&ue) CTFEExp(TOKcantexp); - - return ue; -} - -UnionExp Shl(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - - new(&ue) IntegerExp(loc, e1->toInteger() << e2->toInteger(), type); - return ue; -} - -UnionExp Shr(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - - dinteger_t value = e1->toInteger(); - dinteger_t dcount = e2->toInteger(); - assert(dcount <= 0xFFFFFFFF); - unsigned count = (unsigned)dcount; - switch (e1->type->toBasetype()->ty) - { - case Tint8: - value = (d_int8)(value) >> count; - break; - - case Tuns8: - case Tchar: - value = (d_uns8)(value) >> count; - break; - - case Tint16: - value = (d_int16)(value) >> count; - break; - - case Tuns16: - case Twchar: - value = (d_uns16)(value) >> count; - break; - - case Tint32: - value = (d_int32)(value) >> count; - break; - - case Tuns32: - case Tdchar: - value = (d_uns32)(value) >> count; - break; - - case Tint64: - value = (d_int64)(value) >> count; - break; - - case Tuns64: - value = (d_uns64)(value) >> count; - break; - - case Terror: - new(&ue) ErrorExp(); - return ue; - - default: - assert(0); - } - new(&ue) IntegerExp(loc, value, type); - return ue; -} - -UnionExp Ushr(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - - dinteger_t value = e1->toInteger(); - dinteger_t dcount = e2->toInteger(); - assert(dcount <= 0xFFFFFFFF); - unsigned count = (unsigned)dcount; - switch (e1->type->toBasetype()->ty) - { - case Tint8: - case Tuns8: - case Tchar: - // Possible only with >>>=. >>> always gets promoted to int. - value = (value & 0xFF) >> count; - break; - - case Tint16: - case Tuns16: - case Twchar: - // Possible only with >>>=. >>> always gets promoted to int. - value = (value & 0xFFFF) >> count; - break; - - case Tint32: - case Tuns32: - case Tdchar: - value = (value & 0xFFFFFFFF) >> count; - break; - - case Tint64: - case Tuns64: - value = (d_uns64)(value) >> count; - break; - - case Terror: - new(&ue) ErrorExp(); - return ue; - - default: - assert(0); - } - new(&ue) IntegerExp(loc, value, type); - return ue; -} - -UnionExp And(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - new(&ue) IntegerExp(e1->loc, e1->toInteger() & e2->toInteger(), type); - return ue; -} - -UnionExp Or(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - new(&ue) IntegerExp(e1->loc, e1->toInteger() | e2->toInteger(), type); - return ue; -} - -UnionExp Xor(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - new(&ue) IntegerExp(e1->loc, e1->toInteger() ^ e2->toInteger(), type); - return ue; -} - -/* Also returns TOKcantexp if cannot be computed. - */ -UnionExp Equal(TOK op, Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - int cmp = 0; - real_t r1; - real_t r2; - - //printf("Equal(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); - - assert(op == TOKequal || op == TOKnotequal); - - if (e1->op == TOKnull) - { - if (e2->op == TOKnull) - cmp = 1; - else if (e2->op == TOKstring) - { - StringExp *es2 = (StringExp *)e2; - cmp = (0 == es2->len); - } - else if (e2->op == TOKarrayliteral) - { - ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; - cmp = !es2->elements || (0 == es2->elements->dim); - } - else - { - new(&ue) CTFEExp(TOKcantexp); - return ue; - } - } - else if (e2->op == TOKnull) - { - if (e1->op == TOKstring) - { - StringExp *es1 = (StringExp *)e1; - cmp = (0 == es1->len); - } - else if (e1->op == TOKarrayliteral) - { - ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; - cmp = !es1->elements || (0 == es1->elements->dim); - } - else - { - new(&ue) CTFEExp(TOKcantexp); - return ue; - } - } - else if (e1->op == TOKstring && e2->op == TOKstring) - { - StringExp *es1 = (StringExp *)e1; - StringExp *es2 = (StringExp *)e2; - - if (es1->sz != es2->sz) - { - assert(global.errors); - new(&ue) CTFEExp(TOKcantexp); - return ue; - } - if (es1->len == es2->len && - memcmp(es1->string, es2->string, es1->sz * es1->len) == 0) - cmp = 1; - else - cmp = 0; - } - else if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral) - { - ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; - ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; - - if ((!es1->elements || !es1->elements->dim) && - (!es2->elements || !es2->elements->dim)) - cmp = 1; // both arrays are empty - else if (!es1->elements || !es2->elements) - cmp = 0; - else if (es1->elements->dim != es2->elements->dim) - cmp = 0; - else - { - for (size_t i = 0; i < es1->elements->dim; i++) - { - Expression *ee1 = (*es1->elements)[i]; - Expression *ee2 = (*es2->elements)[i]; - - ue = Equal(TOKequal, Type::tint32, ee1, ee2); - if (CTFEExp::isCantExp(ue.exp())) - return ue; - cmp = (int)ue.exp()->toInteger(); - if (cmp == 0) - break; - } - } - } - else if (e1->op == TOKarrayliteral && e2->op == TOKstring) - { - // Swap operands and use common code - Expression *etmp = e1; - e1 = e2; - e2 = etmp; - goto Lsa; - } - else if (e1->op == TOKstring && e2->op == TOKarrayliteral) - { - Lsa: - StringExp *es1 = (StringExp *)e1; - ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; - size_t dim1 = es1->len; - size_t dim2 = es2->elements ? es2->elements->dim : 0; - if (dim1 != dim2) - cmp = 0; - else - { - cmp = 1; // if dim1 winds up being 0 - for (size_t i = 0; i < dim1; i++) - { - uinteger_t c = es1->charAt(i); - Expression *ee2 = (*es2->elements)[i]; - if (ee2->isConst() != 1) - { - new(&ue) CTFEExp(TOKcantexp); - return ue; - } - cmp = (c == ee2->toInteger()); - if (cmp == 0) - break; - } - } - } - else if (e1->op == TOKstructliteral && e2->op == TOKstructliteral) - { - StructLiteralExp *es1 = (StructLiteralExp *)e1; - StructLiteralExp *es2 = (StructLiteralExp *)e2; - - if (es1->sd != es2->sd) - cmp = 0; - else if ((!es1->elements || !es1->elements->dim) && - (!es2->elements || !es2->elements->dim)) - cmp = 1; // both arrays are empty - else if (!es1->elements || !es2->elements) - cmp = 0; - else if (es1->elements->dim != es2->elements->dim) - cmp = 0; - else - { - cmp = 1; - for (size_t i = 0; i < es1->elements->dim; i++) - { - Expression *ee1 = (*es1->elements)[i]; - Expression *ee2 = (*es2->elements)[i]; - - if (ee1 == ee2) - continue; - if (!ee1 || !ee2) - { - cmp = 0; - break; - } - ue = Equal(TOKequal, Type::tint32, ee1, ee2); - if (ue.exp()->op == TOKcantexp) - return ue; - cmp = (int)ue.exp()->toInteger(); - if (cmp == 0) - break; - } - } - if (cmp && es1->type->needsNested()) - { - if ((es1->sinit != NULL) != (es2->sinit != NULL)) - cmp = 0; - } - } - else if (e1->isConst() != 1 || e2->isConst() != 1) - { - new(&ue) CTFEExp(TOKcantexp); - return ue; - } - else if (e1->type->isreal()) - { - r1 = e1->toReal(); - r2 = e2->toReal(); - goto L1; - } - else if (e1->type->isimaginary()) - { - r1 = e1->toImaginary(); - r2 = e2->toImaginary(); - L1: - if (Port::isNan(r1) || Port::isNan(r2)) // if unordered - { - cmp = 0; - } - else - { - cmp = (r1 == r2); - } - } - else if (e1->type->iscomplex()) - { - cmp = e1->toComplex() == e2->toComplex(); - } - else if (e1->type->isintegral() || e1->type->toBasetype()->ty == Tpointer) - { - cmp = (e1->toInteger() == e2->toInteger()); - } - else - { - new(&ue) CTFEExp(TOKcantexp); - return ue; - } - - if (op == TOKnotequal) - cmp ^= 1; - new(&ue) IntegerExp(loc, cmp, type); - return ue; -} - -UnionExp Identity(TOK op, Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - int cmp; - - if (e1->op == TOKnull) - { - cmp = (e2->op == TOKnull); - } - else if (e2->op == TOKnull) - { - cmp = 0; - } - else if (e1->op == TOKsymoff && e2->op == TOKsymoff) - { - SymOffExp *es1 = (SymOffExp *)e1; - SymOffExp *es2 = (SymOffExp *)e2; - - cmp = (es1->var == es2->var && es1->offset == es2->offset); - } - else - { - if (e1->type->isreal()) - { - cmp = RealEquals(e1->toReal(), e2->toReal()); - } - else if (e1->type->isimaginary()) - { - cmp = RealEquals(e1->toImaginary(), e2->toImaginary()); - } - else if (e1->type->iscomplex()) - { - complex_t v1 = e1->toComplex(); - complex_t v2 = e2->toComplex(); - cmp = RealEquals(creall(v1), creall(v2)) && - RealEquals(cimagl(v1), cimagl(v1)); - } - else - { - ue = Equal((op == TOKidentity) ? TOKequal : TOKnotequal, - type, e1, e2); - return ue; - } - } - if (op == TOKnotidentity) - cmp ^= 1; - new(&ue) IntegerExp(loc, cmp, type); - return ue; -} - - -UnionExp Cmp(TOK op, Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - dinteger_t n; - real_t r1; - real_t r2; - - //printf("Cmp(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); - - if (e1->op == TOKstring && e2->op == TOKstring) - { - StringExp *es1 = (StringExp *)e1; - StringExp *es2 = (StringExp *)e2; - size_t sz = es1->sz; - assert(sz == es2->sz); - - size_t len = es1->len; - if (es2->len < len) - len = es2->len; - - int cmp = memcmp(es1->string, es2->string, sz * len); - if (cmp == 0) - cmp = (int)(es1->len - es2->len); - - switch (op) - { - case TOKlt: n = cmp < 0; break; - case TOKle: n = cmp <= 0; break; - case TOKgt: n = cmp > 0; break; - case TOKge: n = cmp >= 0; break; - - case TOKleg: n = 1; break; - case TOKlg: n = cmp != 0; break; - case TOKunord: n = 0; break; - case TOKue: n = cmp == 0; break; - case TOKug: n = cmp > 0; break; - case TOKuge: n = cmp >= 0; break; - case TOKul: n = cmp < 0; break; - case TOKule: n = cmp <= 0; break; - - default: - assert(0); - } - } - else if (e1->isConst() != 1 || e2->isConst() != 1) - { - new(&ue) CTFEExp(TOKcantexp); - return ue; - } - else if (e1->type->isreal()) - { - r1 = e1->toReal(); - r2 = e2->toReal(); - goto L1; - } - else if (e1->type->isimaginary()) - { - r1 = e1->toImaginary(); - r2 = e2->toImaginary(); - L1: - // Don't rely on compiler, handle NAN arguments separately - // (DMC does do it correctly) - if (Port::isNan(r1) || Port::isNan(r2)) // if unordered - { - switch (op) - { - case TOKlt: n = 0; break; - case TOKle: n = 0; break; - case TOKgt: n = 0; break; - case TOKge: n = 0; break; - - case TOKleg: n = 0; break; - case TOKlg: n = 0; break; - case TOKunord: n = 1; break; - case TOKue: n = 1; break; - case TOKug: n = 1; break; - case TOKuge: n = 1; break; - case TOKul: n = 1; break; - case TOKule: n = 1; break; - - default: - assert(0); - } - } - else - { - switch (op) - { - case TOKlt: n = r1 < r2; break; - case TOKle: n = r1 <= r2; break; - case TOKgt: n = r1 > r2; break; - case TOKge: n = r1 >= r2; break; - - case TOKleg: n = 1; break; - case TOKlg: n = r1 != r2; break; - case TOKunord: n = 0; break; - case TOKue: n = r1 == r2; break; - case TOKug: n = r1 > r2; break; - case TOKuge: n = r1 >= r2; break; - case TOKul: n = r1 < r2; break; - case TOKule: n = r1 <= r2; break; - - default: - assert(0); - } - } - } - else if (e1->type->iscomplex()) - { - assert(0); - } - else - { - sinteger_t n1; - sinteger_t n2; - - n1 = e1->toInteger(); - n2 = e2->toInteger(); - if (e1->type->isunsigned() || e2->type->isunsigned()) - { - switch (op) - { - case TOKlt: n = ((dinteger_t) n1) < ((dinteger_t) n2); break; - case TOKle: n = ((dinteger_t) n1) <= ((dinteger_t) n2); break; - case TOKgt: n = ((dinteger_t) n1) > ((dinteger_t) n2); break; - case TOKge: n = ((dinteger_t) n1) >= ((dinteger_t) n2); break; - - case TOKleg: n = 1; break; - case TOKlg: n = ((dinteger_t) n1) != ((dinteger_t) n2); break; - case TOKunord: n = 0; break; - case TOKue: n = ((dinteger_t) n1) == ((dinteger_t) n2); break; - case TOKug: n = ((dinteger_t) n1) > ((dinteger_t) n2); break; - case TOKuge: n = ((dinteger_t) n1) >= ((dinteger_t) n2); break; - case TOKul: n = ((dinteger_t) n1) < ((dinteger_t) n2); break; - case TOKule: n = ((dinteger_t) n1) <= ((dinteger_t) n2); break; - - default: - assert(0); - } - } - else - { - switch (op) - { - case TOKlt: n = n1 < n2; break; - case TOKle: n = n1 <= n2; break; - case TOKgt: n = n1 > n2; break; - case TOKge: n = n1 >= n2; break; - - case TOKleg: n = 1; break; - case TOKlg: n = n1 != n2; break; - case TOKunord: n = 0; break; - case TOKue: n = n1 == n2; break; - case TOKug: n = n1 > n2; break; - case TOKuge: n = n1 >= n2; break; - case TOKul: n = n1 < n2; break; - case TOKule: n = n1 <= n2; break; - - default: - assert(0); - } - } - } - new(&ue) IntegerExp(loc, n, type); - return ue; -} - -/* Also returns TOKcantexp if cannot be computed. - * to: type to cast to - * type: type to paint the result - */ - -UnionExp Cast(Type *type, Type *to, Expression *e1) -{ - UnionExp ue; - Loc loc = e1->loc; - - Type *tb = to->toBasetype(); - Type *typeb = type->toBasetype(); - - //printf("Cast(type = %s, to = %s, e1 = %s)\n", type->toChars(), to->toChars(), e1->toChars()); - //printf("\te1->type = %s\n", e1->type->toChars()); - if (e1->type->equals(type) && type->equals(to)) - { - new(&ue) UnionExp(e1); - return ue; - } - - if (e1->op == TOKvector && ((TypeVector *)e1->type)->basetype->equals(type) && type->equals(to)) - { - Expression *ex = ((VectorExp *)e1)->e1; - new(&ue) UnionExp(ex); - return ue; - } - - if (e1->type->implicitConvTo(to) >= MATCHconst || - to->implicitConvTo(e1->type) >= MATCHconst) - { - goto L1; - } - - // Allow covariant converions of delegates - // (Perhaps implicit conversion from pure to impure should be a MATCHconst, - // then we wouldn't need this extra check.) - if (e1->type->toBasetype()->ty == Tdelegate && - e1->type->implicitConvTo(to) == MATCHconvert) - { - goto L1; - } - - /* Allow casting from one string type to another - */ - if (e1->op == TOKstring) - { - if (tb->ty == Tarray && typeb->ty == Tarray && - tb->nextOf()->size() == typeb->nextOf()->size()) - { - goto L1; - } - } - - if (e1->op == TOKarrayliteral && typeb == tb) - { -L1: - Expression *ex = expType(to, e1); - new(&ue) UnionExp(ex); - return ue; - } - - if (e1->isConst() != 1) - { - new(&ue) CTFEExp(TOKcantexp); - } - else if (tb->ty == Tbool) - { - new(&ue) IntegerExp(loc, e1->toInteger() != 0, type); - } - else if (type->isintegral()) - { - if (e1->type->isfloating()) - { - dinteger_t result; - real_t r = e1->toReal(); - - switch (typeb->ty) - { - case Tint8: result = (d_int8)r; break; - case Tchar: - case Tuns8: result = (d_uns8)r; break; - case Tint16: result = (d_int16)r; break; - case Twchar: - case Tuns16: result = (d_uns16)r; break; - case Tint32: result = (d_int32)r; break; - case Tdchar: - case Tuns32: result = (d_uns32)r; break; - case Tint64: result = (d_int64)r; break; - case Tuns64: result = (d_uns64)r; break; - default: - assert(0); - } - - new(&ue) IntegerExp(loc, result, type); - } - else if (type->isunsigned()) - new(&ue) IntegerExp(loc, e1->toUInteger(), type); - else - new(&ue) IntegerExp(loc, e1->toInteger(), type); - } - else if (tb->isreal()) - { - real_t value = e1->toReal(); - - new(&ue) RealExp(loc, value, type); - } - else if (tb->isimaginary()) - { - real_t value = e1->toImaginary(); - - new(&ue) RealExp(loc, value, type); - } - else if (tb->iscomplex()) - { - complex_t value = e1->toComplex(); - - new(&ue) ComplexExp(loc, value, type); - } - else if (tb->isscalar()) - { - new(&ue) IntegerExp(loc, e1->toInteger(), type); - } - else if (tb->ty == Tvoid) - { - new(&ue) CTFEExp(TOKcantexp); - } - else if (tb->ty == Tstruct && e1->op == TOKint64) - { - // Struct = 0; - StructDeclaration *sd = tb->toDsymbol(NULL)->isStructDeclaration(); - assert(sd); - Expressions *elements = new Expressions; - for (size_t i = 0; i < sd->fields.dim; i++) - { - VarDeclaration *v = sd->fields[i]; - UnionExp zero; - new(&zero) IntegerExp(0); - ue = Cast(v->type, v->type, zero.exp()); - if (ue.exp()->op == TOKcantexp) - return ue; - elements->push(ue.exp()->copy()); - } - new(&ue) StructLiteralExp(loc, sd, elements); - ue.exp()->type = type; - } - else - { - if (type != Type::terror) - { - // have to change to Internal Compiler Error - // all invalid casts should be handled already in Expression::castTo(). - error(loc, "cannot cast %s to %s", e1->type->toChars(), type->toChars()); - } - new(&ue) ErrorExp(); - } - return ue; -} - - -UnionExp ArrayLength(Type *type, Expression *e1) -{ - UnionExp ue; - Loc loc = e1->loc; - - if (e1->op == TOKstring) - { - StringExp *es1 = (StringExp *)e1; - - new(&ue) IntegerExp(loc, es1->len, type); - } - else if (e1->op == TOKarrayliteral) - { - ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; - size_t dim = ale->elements ? ale->elements->dim : 0; - - new(&ue) IntegerExp(loc, dim, type); - } - else if (e1->op == TOKassocarrayliteral) - { - AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e1; - size_t dim = ale->keys->dim; - - new(&ue) IntegerExp(loc, dim, type); - } - else if (e1->type->toBasetype()->ty == Tsarray) - { - Expression *e = ((TypeSArray *)e1->type->toBasetype())->dim; - new(&ue) UnionExp(e); - } - else - new(&ue) CTFEExp(TOKcantexp); - return ue; -} - -/* Also return TOKcantexp if this fails - */ -UnionExp Index(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Loc loc = e1->loc; - - //printf("Index(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); - assert(e1->type); - if (e1->op == TOKstring && e2->op == TOKint64) - { - StringExp *es1 = (StringExp *)e1; - uinteger_t i = e2->toInteger(); - - if (i >= es1->len) - { - e1->error("string index %llu is out of bounds [0 .. %llu]", i, (ulonglong)es1->len); - new(&ue) ErrorExp(); - } - else - { - new(&ue) IntegerExp(loc, es1->charAt(i), type); - } - } - else if (e1->type->toBasetype()->ty == Tsarray && e2->op == TOKint64) - { - TypeSArray *tsa = (TypeSArray *)e1->type->toBasetype(); - uinteger_t length = tsa->dim->toInteger(); - uinteger_t i = e2->toInteger(); - - if (i >= length) - { - e1->error("array index %llu is out of bounds %s[0 .. %llu]", i, e1->toChars(), length); - new(&ue) ErrorExp(); - } - else if (e1->op == TOKarrayliteral) - { - ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; - Expression *e = (*ale->elements)[(size_t)i]; - e->type = type; - e->loc = loc; - if (hasSideEffect(e)) - new(&ue) CTFEExp(TOKcantexp); - else - new(&ue) UnionExp(e); - } - else - new(&ue) CTFEExp(TOKcantexp); - } - else if (e1->type->toBasetype()->ty == Tarray && e2->op == TOKint64) - { - uinteger_t i = e2->toInteger(); - - if (e1->op == TOKarrayliteral) - { - ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; - if (i >= ale->elements->dim) - { - e1->error("array index %llu is out of bounds %s[0 .. %u]", i, e1->toChars(), ale->elements->dim); - new(&ue) ErrorExp(); - } - else - { - Expression *e = (*ale->elements)[(size_t)i]; - e->type = type; - e->loc = loc; - if (hasSideEffect(e)) - new(&ue) CTFEExp(TOKcantexp); - else - new(&ue) UnionExp(e); - } - } - else - new(&ue) CTFEExp(TOKcantexp); - } - else if (e1->op == TOKassocarrayliteral) - { - AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e1; - /* Search the keys backwards, in case there are duplicate keys - */ - for (size_t i = ae->keys->dim; i;) - { - i--; - Expression *ekey = (*ae->keys)[i]; - ue = Equal(TOKequal, Type::tbool, ekey, e2); - if (CTFEExp::isCantExp(ue.exp())) - return ue; - if (ue.exp()->isBool(true)) - { - Expression *e = (*ae->values)[i]; - e->type = type; - e->loc = loc; - if (hasSideEffect(e)) - new(&ue) CTFEExp(TOKcantexp); - else - new(&ue) UnionExp(e); - return ue; - } - } - new(&ue) CTFEExp(TOKcantexp); - } - else - new(&ue) CTFEExp(TOKcantexp); - return ue; -} - -/* Also return TOKcantexp if this fails - */ -UnionExp Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr) -{ - UnionExp ue; - Loc loc = e1->loc; - -#if LOG - printf("Slice()\n"); - if (lwr) - { - printf("\te1 = %s\n", e1->toChars()); - printf("\tlwr = %s\n", lwr->toChars()); - printf("\tupr = %s\n", upr->toChars()); - } -#endif - if (e1->op == TOKstring && lwr->op == TOKint64 && upr->op == TOKint64) - { - StringExp *es1 = (StringExp *)e1; - uinteger_t ilwr = lwr->toInteger(); - uinteger_t iupr = upr->toInteger(); - - if (iupr > es1->len || ilwr > iupr) - { - e1->error("string slice [%llu .. %llu] is out of bounds", ilwr, iupr); - new(&ue) ErrorExp(); - } - else - { - size_t len = (size_t)(iupr - ilwr); - unsigned char sz = es1->sz; - - void *s = mem.xmalloc((len + 1) * sz); - memcpy((char *)s, (char *)es1->string + ilwr * sz, len * sz); - memset((char *)s + len * sz, 0, sz); - - new(&ue) StringExp(loc, s, len, es1->postfix); - StringExp *es = (StringExp *)ue.exp(); - es->sz = sz; - es->committed = es1->committed; - es->type = type; - } - } - else if (e1->op == TOKarrayliteral && - lwr->op == TOKint64 && upr->op == TOKint64 && - !hasSideEffect(e1)) - { - ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; - uinteger_t ilwr = lwr->toInteger(); - uinteger_t iupr = upr->toInteger(); - - if (iupr > es1->elements->dim || ilwr > iupr) - { - e1->error("array slice [%llu .. %llu] is out of bounds", ilwr, iupr); - new(&ue) ErrorExp(); - } - else - { - Expressions *elements = new Expressions(); - elements->setDim((size_t)(iupr - ilwr)); - memcpy(elements->tdata(), - es1->elements->tdata() + ilwr, - (size_t)(iupr - ilwr) * sizeof((*es1->elements)[0])); - new(&ue) ArrayLiteralExp(e1->loc, elements); - ue.exp()->type = type; - } - } - else - new(&ue) CTFEExp(TOKcantexp); - assert(ue.exp()->type); - return ue; -} - -/* Set a slice of char/integer array literal 'existingAE' from a string 'newval'. - * existingAE[firstIndex..firstIndex+newval.length] = newval. - */ -void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, size_t firstIndex) -{ - size_t newlen = newval->len; - size_t sz = newval->sz; - void *s = newval->string; - Type *elemType = existingAE->type->nextOf(); - for (size_t j = 0; j < newlen; j++) - { - dinteger_t val; - switch (sz) - { - case 1: val = (( utf8_t *)s)[j]; break; - case 2: val = ((utf16_t *)s)[j]; break; - case 4: val = ((utf32_t *)s)[j]; break; - default: assert(0); break; - } - (*existingAE->elements)[j + firstIndex] - = new IntegerExp(newval->loc, val, elemType); - } -} - -/* Set a slice of string 'existingSE' from a char array literal 'newae'. - * existingSE[firstIndex..firstIndex+newae.length] = newae. - */ -void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, size_t firstIndex) -{ - void *s = existingSE->string; - for (size_t j = 0; j < newae->elements->dim; j++) - { - unsigned val = (unsigned)((*newae->elements)[j]->toInteger()); - switch (existingSE->sz) - { - case 1: (( utf8_t *)s)[j + firstIndex] = ( utf8_t)val; break; - case 2: ((utf16_t *)s)[j + firstIndex] = (utf16_t)val; break; - case 4: ((utf32_t *)s)[j + firstIndex] = (utf32_t)val; break; - default: assert(0); break; - } - } -} - -/* Set a slice of string 'existingSE' from a string 'newstr'. - * existingSE[firstIndex..firstIndex+newstr.length] = newstr. - */ -void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, size_t firstIndex) -{ - void *s = existingSE->string; - size_t sz = existingSE->sz; - assert(sz == newstr->sz); - memcpy((char *)s + firstIndex * sz, newstr->string, sz * newstr->len); -} - -/* Compare a string slice with another string slice. - * Conceptually equivalent to memcmp( se1[lo1..lo1+len], se2[lo2..lo2+len]) - */ -int sliceCmpStringWithString(StringExp *se1, StringExp *se2, size_t lo1, size_t lo2, size_t len) -{ - void *s1 = se1->string; - void *s2 = se2->string; - size_t sz = se1->sz; - assert(sz == se2->sz); - return memcmp((char *)s1 + sz * lo1, (char *)s2 + sz * lo2, sz * len); -} - -/* Compare a string slice with an array literal slice - * Conceptually equivalent to memcmp( se1[lo1..lo1+len], ae2[lo2..lo2+len]) - */ -int sliceCmpStringWithArray(StringExp *se1, ArrayLiteralExp *ae2, size_t lo1, size_t lo2, size_t len) -{ - void *s = se1->string; - size_t sz = se1->sz; - - for (size_t j = 0; j < len; j++) - { - unsigned val2 = (unsigned)((*ae2->elements)[j + lo2]->toInteger()); - unsigned val1; - switch (sz) - { - case 1: val1 = (( utf8_t *)s)[j + lo1]; break; - case 2: val1 = ((utf16_t *)s)[j + lo1]; break; - case 4: val1 = ((utf32_t *)s)[j + lo1]; break; - default: assert(0); break; - } - int c = val1 - val2; - if (c) - return c; - } - return 0; -} - -/* Also return TOKcantexp if this fails - */ -UnionExp Cat(Type *type, Expression *e1, Expression *e2) -{ - UnionExp ue; - Expression *e = CTFEExp::cantexp; - Loc loc = e1->loc; - Type *t; - Type *t1 = e1->type->toBasetype(); - Type *t2 = e2->type->toBasetype(); - - //printf("Cat(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); - //printf("\tt1 = %s, t2 = %s, type = %s\n", t1->toChars(), t2->toChars(), type->toChars()); - - if (e1->op == TOKnull && (e2->op == TOKint64 || e2->op == TOKstructliteral)) - { - e = e2; - t = t1; - goto L2; - } - else if ((e1->op == TOKint64 || e1->op == TOKstructliteral) && e2->op == TOKnull) - { - e = e1; - t = t2; - L2: - Type *tn = e->type->toBasetype(); - if (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar) - { - // Create a StringExp - if (t->nextOf()) - t = t->nextOf()->toBasetype(); - unsigned char sz = (unsigned char)t->size(); - - dinteger_t v = e->toInteger(); - - size_t len = (t->ty == tn->ty) ? 1 : utf_codeLength(sz, (dchar_t)v); - void *s = mem.xmalloc((len + 1) * sz); - if (t->ty == tn->ty) - memcpy(s, &v, sz); - else - utf_encode(sz, s, (dchar_t)v); - - // Add terminating 0 - memset((char *)s + len * sz, 0, sz); - - new(&ue) StringExp(loc, s, len); - StringExp *es = (StringExp *)ue.exp(); - es->sz = sz; - es->committed = 1; - } - else - { - // Create an ArrayLiteralExp - Expressions *elements = new Expressions(); - elements->push(e); - new(&ue) ArrayLiteralExp(e->loc, elements); - } - ue.exp()->type = type; - assert(ue.exp()->type); - return ue; - } - else if (e1->op == TOKnull && e2->op == TOKnull) - { - if (type == e1->type) - { - // Handle null ~= null - if (t1->ty == Tarray && t2 == t1->nextOf()) - { - new(&ue) ArrayLiteralExp(e1->loc, e2); - ue.exp()->type = type; - assert(ue.exp()->type); - return ue; - } - else - { - new(&ue) UnionExp(e1); - assert(ue.exp()->type); - return ue; - } - } - if (type == e2->type) - { - new(&ue) UnionExp(e2); - assert(ue.exp()->type); - return ue; - } - new(&ue) NullExp(e1->loc, type); - assert(ue.exp()->type); - return ue; - } - else if (e1->op == TOKstring && e2->op == TOKstring) - { - // Concatenate the strings - StringExp *es1 = (StringExp *)e1; - StringExp *es2 = (StringExp *)e2; - size_t len = es1->len + es2->len; - unsigned char sz = es1->sz; - - if (sz != es2->sz) - { - /* Can happen with: - * auto s = "foo"d ~ "bar"c; - */ - assert(global.errors); - new(&ue) CTFEExp(TOKcantexp); - assert(ue.exp()->type); - return ue; - } - void *s = mem.xmalloc((len + 1) * sz); - memcpy((char *)s, es1->string, es1->len * sz); - memcpy((char *)s + es1->len * sz, es2->string, es2->len * sz); - - // Add terminating 0 - memset((char *)s + len * sz, 0, sz); - - new(&ue) StringExp(loc, s, len); - StringExp *es = (StringExp *)ue.exp(); - es->sz = sz; - es->committed = es1->committed | es2->committed; - es->type = type; - assert(ue.exp()->type); - return ue; - } - else if (e2->op == TOKstring && e1->op == TOKarrayliteral && - t1->nextOf()->isintegral()) - { - // [chars] ~ string --> [chars] - StringExp *es = (StringExp *)e2; - ArrayLiteralExp *ea = (ArrayLiteralExp *)e1; - size_t len = es->len + ea->elements->dim; - Expressions * elems = new Expressions; - elems->setDim(len); - for (size_t i= 0; i < ea->elements->dim; ++i) - { - (*elems)[i] = (*ea->elements)[i]; - } - new(&ue) ArrayLiteralExp(e1->loc, elems); - ArrayLiteralExp *dest = (ArrayLiteralExp *)ue.exp(); - dest->type = type; - sliceAssignArrayLiteralFromString(dest, es, ea->elements->dim); - assert(ue.exp()->type); - return ue; - } - else if (e1->op == TOKstring && e2->op == TOKarrayliteral && - t2->nextOf()->isintegral()) - { - // string ~ [chars] --> [chars] - StringExp *es = (StringExp *)e1; - ArrayLiteralExp *ea = (ArrayLiteralExp *)e2; - size_t len = es->len + ea->elements->dim; - Expressions * elems = new Expressions; - elems->setDim(len); - for (size_t i= 0; i < ea->elements->dim; ++i) - { - (*elems)[es->len + i] = (*ea->elements)[i]; - } - new(&ue) ArrayLiteralExp(e1->loc, elems); - ArrayLiteralExp *dest = (ArrayLiteralExp *)ue.exp(); - dest->type = type; - sliceAssignArrayLiteralFromString(dest, es, 0); - assert(ue.exp()->type); - return ue; - } - else if (e1->op == TOKstring && e2->op == TOKint64) - { - // string ~ char --> string - StringExp *es1 = (StringExp *)e1; - StringExp *es; - unsigned char sz = es1->sz; - dinteger_t v = e2->toInteger(); - - // Is it a concatentation of homogenous types? - // (char[] ~ char, wchar[]~wchar, or dchar[]~dchar) - bool homoConcat = (sz == t2->size()); - size_t len = es1->len; - len += homoConcat ? 1 : utf_codeLength(sz, (dchar_t)v); - - void *s = mem.xmalloc((len + 1) * sz); - memcpy(s, es1->string, es1->len * sz); - if (homoConcat) - memcpy((char *)s + (sz * es1->len), &v, sz); - else - utf_encode(sz, (char *)s + (sz * es1->len), (dchar_t)v); - - // Add terminating 0 - memset((char *)s + len * sz, 0, sz); - - new(&ue) StringExp(loc, s, len); - es = (StringExp *)ue.exp(); - es->sz = sz; - es->committed = es1->committed; - es->type = type; - assert(ue.exp()->type); - return ue; - } - else if (e1->op == TOKint64 && e2->op == TOKstring) - { - // Concatenate the strings - StringExp *es2 = (StringExp *)e2; - size_t len = 1 + es2->len; - unsigned char sz = es2->sz; - dinteger_t v = e1->toInteger(); - - void *s = mem.xmalloc((len + 1) * sz); - memcpy((char *)s, &v, sz); - memcpy((char *)s + sz, es2->string, es2->len * sz); - - // Add terminating 0 - memset((char *)s + len * sz, 0, sz); - - new(&ue) StringExp(loc, s, len); - StringExp *es = (StringExp *)ue.exp(); - es->sz = sz; - es->committed = es2->committed; - es->type = type; - assert(ue.exp()->type); - return ue; - } - else if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral && - t1->nextOf()->equals(t2->nextOf())) - { - // Concatenate the arrays - ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; - ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; - - new(&ue) ArrayLiteralExp(es1->loc, (Expressions *)es1->elements->copy()); - es1 = (ArrayLiteralExp *)ue.exp(); - es1->elements->insert(es1->elements->dim, es2->elements); - e = es1; - - if (type->toBasetype()->ty == Tsarray) - { - e->type = t1->nextOf()->sarrayOf(es1->elements->dim); - } - else - e->type = type; - assert(ue.exp()->type); - return ue; - } - else if (e1->op == TOKarrayliteral && e2->op == TOKnull && - t1->nextOf()->equals(t2->nextOf())) - { - e = e1; - goto L3; - } - else if (e1->op == TOKnull && e2->op == TOKarrayliteral && - t1->nextOf()->equals(t2->nextOf())) - { - e = e2; - L3: - // Concatenate the array with null - ArrayLiteralExp *es = (ArrayLiteralExp *)e; - - new(&ue) ArrayLiteralExp(es->loc, (Expressions *)es->elements->copy()); - es = (ArrayLiteralExp *)ue.exp(); - e = es; - - if (type->toBasetype()->ty == Tsarray) - { - e->type = t1->nextOf()->sarrayOf(es->elements->dim); - } - else - e->type = type; - assert(ue.exp()->type); - return ue; - } - else if ((e1->op == TOKarrayliteral || e1->op == TOKnull) && - e1->type->toBasetype()->nextOf() && - e1->type->toBasetype()->nextOf()->equals(e2->type)) - { - ArrayLiteralExp *es1; - if (e1->op == TOKarrayliteral) - { - es1 = (ArrayLiteralExp *)e1; - new(&ue) ArrayLiteralExp(es1->loc, (Expressions *)es1->elements->copy()); - es1 = (ArrayLiteralExp *)ue.exp(); - es1->elements->push(e2); - } - else - { - new(&ue) ArrayLiteralExp(e1->loc, e2); - es1 = (ArrayLiteralExp *)ue.exp(); - } - e = es1; - - if (type->toBasetype()->ty == Tsarray) - { - e->type = e2->type->sarrayOf(es1->elements->dim); - } - else - e->type = type; - assert(ue.exp()->type); - return ue; - } - else if (e2->op == TOKarrayliteral && - e2->type->toBasetype()->nextOf()->equals(e1->type)) - { - ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; - - new(&ue) ArrayLiteralExp(es2->loc, (Expressions *)es2->elements->copy()); - es2 = (ArrayLiteralExp *)ue.exp(); - es2->elements->shift(e1); - e = es2; - - if (type->toBasetype()->ty == Tsarray) - { - e->type = e1->type->sarrayOf(es2->elements->dim); - } - else - e->type = type; - assert(ue.exp()->type); - return ue; - } - else if (e1->op == TOKnull && e2->op == TOKstring) - { - t = e1->type; - e = e2; - goto L1; - } - else if (e1->op == TOKstring && e2->op == TOKnull) - { - e = e1; - t = e2->type; - L1: - Type *tb = t->toBasetype(); - if (tb->ty == Tarray && tb->nextOf()->equivalent(e->type)) - { - Expressions *expressions = new Expressions(); - expressions->push(e); - new(&ue) ArrayLiteralExp(loc, expressions); - e = ue.exp(); - e->type = t; - } - else - { - new(&ue) UnionExp(e); - e = ue.exp(); - } - if (!e->type->equals(type)) - { - StringExp *se = (StringExp *)e->copy(); - e = se->castTo(NULL, type); - new(&ue) UnionExp(e); - e = ue.exp(); - } - } - else - new(&ue) CTFEExp(TOKcantexp); - assert(ue.exp()->type); - return ue; -} - -UnionExp Ptr(Type *type, Expression *e1) -{ - //printf("Ptr(e1 = %s)\n", e1->toChars()); - UnionExp ue; - if (e1->op == TOKadd) - { - AddExp *ae = (AddExp *)e1; - if (ae->e1->op == TOKaddress && ae->e2->op == TOKint64) - { - AddrExp *ade = (AddrExp *)ae->e1; - if (ade->e1->op == TOKstructliteral) - { - StructLiteralExp *se = (StructLiteralExp *)ade->e1; - unsigned offset = (unsigned)ae->e2->toInteger(); - Expression *e = se->getField(type, offset); - if (e) - { - new(&ue) UnionExp(e); - return ue; - } - } - } - } - new(&ue) CTFEExp(TOKcantexp); - return ue; -} diff --git a/src/constfold.d b/src/constfold.d new file mode 100644 index 000000000000..80346b5ad46a --- /dev/null +++ b/src/constfold.d @@ -0,0 +1,2021 @@ +// 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.constfold; + +import core.stdc.string; +import ddmd.arraytypes, ddmd.builtin, ddmd.complex, ddmd.ctfeexpr, ddmd.declaration, ddmd.dstruct, ddmd.errors, ddmd.expression, ddmd.func, ddmd.globals, ddmd.mtype, ddmd.root.longdouble, ddmd.root.port, ddmd.root.rmem, ddmd.sideeffect, ddmd.tokens, ddmd.utf; + +private enum LOG = false; + +extern (C++) Expression expType(Type type, Expression e) +{ + if (type != e.type) + { + e = e.copy(); + e.type = type; + } + return e; +} + +/* ================================== isConst() ============================== */ +extern (C++) int isConst(Expression e) +{ + //printf("Expression::isConst(): %s\n", e->toChars()); + switch (e.op) + { + case TOKint64: + case TOKfloat64: + case TOKcomplex80: + return 1; + case TOKnull: + return 0; + case TOKsymoff: + return 2; + default: + return 0; + } + assert(0); + return 0; +} + +/* =============================== constFold() ============================== */ +/* The constFold() functions were redundant with the optimize() ones, + * and so have been folded in with them. + */ +/* ========================================================================== */ +extern (C++) UnionExp Neg(Type type, Expression e1) +{ + UnionExp ue; + Loc loc = e1.loc; + if (e1.type.isreal()) + { + emplaceExp!(RealExp)(&ue, loc, -e1.toReal(), type); + } + else if (e1.type.isimaginary()) + { + emplaceExp!(RealExp)(&ue, loc, -e1.toImaginary(), type); + } + else if (e1.type.iscomplex()) + { + emplaceExp!(ComplexExp)(&ue, loc, -e1.toComplex(), type); + } + else + { + emplaceExp!(IntegerExp)(&ue, loc, -e1.toInteger(), type); + } + return ue; +} + +extern (C++) UnionExp Com(Type type, Expression e1) +{ + UnionExp ue; + Loc loc = e1.loc; + emplaceExp!(IntegerExp)(&ue, loc, ~e1.toInteger(), type); + return ue; +} + +extern (C++) UnionExp Not(Type type, Expression e1) +{ + UnionExp ue; + Loc loc = e1.loc; + emplaceExp!(IntegerExp)(&ue, loc, e1.isBool(false) ? 1 : 0, type); + return ue; +} + +extern (C++) UnionExp Bool(Type type, Expression e1) +{ + UnionExp ue; + Loc loc = e1.loc; + emplaceExp!(IntegerExp)(&ue, loc, e1.isBool(true) ? 1 : 0, type); + return ue; +} + +extern (C++) UnionExp Add(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + static if (LOG) + { + printf("Add(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars()); + } + if (type.isreal()) + { + emplaceExp!(RealExp)(&ue, loc, e1.toReal() + e2.toReal(), type); + } + else if (type.isimaginary()) + { + emplaceExp!(RealExp)(&ue, loc, e1.toImaginary() + e2.toImaginary(), type); + } + else if (type.iscomplex()) + { + // This rigamarole is necessary so that -0.0 doesn't get + // converted to +0.0 by doing an extraneous add with +0.0 + complex_t c1; + real_t r1 = ldouble(0.0); + real_t i1 = ldouble(0.0); + complex_t c2; + real_t r2 = ldouble(0.0); + real_t i2 = ldouble(0.0); + complex_t v; + int x; + if (e1.type.isreal()) + { + r1 = e1.toReal(); + x = 0; + } + else if (e1.type.isimaginary()) + { + i1 = e1.toImaginary(); + x = 3; + } + else + { + c1 = e1.toComplex(); + x = 6; + } + if (e2.type.isreal()) + { + r2 = e2.toReal(); + } + else if (e2.type.isimaginary()) + { + i2 = e2.toImaginary(); + x += 1; + } + else + { + c2 = e2.toComplex(); + x += 2; + } + switch (x) + { + case 0 + 0: + v = complex_t(r1 + r2, 0); + break; + case 0 + 1: + v = complex_t(r1, i2); + break; + case 0 + 2: + v = complex_t(r1 + creall(c2), cimagl(c2)); + break; + case 3 + 0: + v = complex_t(r2, i1); + break; + case 3 + 1: + v = complex_t(0, i1 + i2); + break; + case 3 + 2: + v = complex_t(creall(c2), i1 + cimagl(c2)); + break; + case 6 + 0: + v = complex_t(creall(c1) + r2, cimagl(c2)); + break; + case 6 + 1: + v = complex_t(creall(c1), cimagl(c1) + i2); + break; + case 6 + 2: + v = c1 + c2; + break; + default: + assert(0); + } + emplaceExp!(ComplexExp)(&ue, loc, v, type); + } + else if (e1.op == TOKsymoff) + { + SymOffExp soe = cast(SymOffExp)e1; + emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset + e2.toInteger()); + ue.exp().type = type; + } + else if (e2.op == TOKsymoff) + { + SymOffExp soe = cast(SymOffExp)e2; + emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset + e1.toInteger()); + ue.exp().type = type; + } + else + emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() + e2.toInteger(), type); + return ue; +} + +extern (C++) UnionExp Min(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + if (type.isreal()) + { + emplaceExp!(RealExp)(&ue, loc, e1.toReal() - e2.toReal(), type); + } + else if (type.isimaginary()) + { + emplaceExp!(RealExp)(&ue, loc, e1.toImaginary() - e2.toImaginary(), type); + } + else if (type.iscomplex()) + { + // This rigamarole is necessary so that -0.0 doesn't get + // converted to +0.0 by doing an extraneous add with +0.0 + complex_t c1; + real_t r1 = ldouble(0.0); + real_t i1 = ldouble(0.0); + complex_t c2; + real_t r2 = ldouble(0.0); + real_t i2 = ldouble(0.0); + complex_t v; + int x; + if (e1.type.isreal()) + { + r1 = e1.toReal(); + x = 0; + } + else if (e1.type.isimaginary()) + { + i1 = e1.toImaginary(); + x = 3; + } + else + { + c1 = e1.toComplex(); + x = 6; + } + if (e2.type.isreal()) + { + r2 = e2.toReal(); + } + else if (e2.type.isimaginary()) + { + i2 = e2.toImaginary(); + x += 1; + } + else + { + c2 = e2.toComplex(); + x += 2; + } + switch (x) + { + case 0 + 0: + v = complex_t(r1 - r2, 0); + break; + case 0 + 1: + v = complex_t(r1, -i2); + break; + case 0 + 2: + v = complex_t(r1 - creall(c2), -cimagl(c2)); + break; + case 3 + 0: + v = complex_t(-r2, i1); + break; + case 3 + 1: + v = complex_t(0, i1 - i2); + break; + case 3 + 2: + v = complex_t(-creall(c2), i1 - cimagl(c2)); + break; + case 6 + 0: + v = complex_t(creall(c1) - r2, cimagl(c1)); + break; + case 6 + 1: + v = complex_t(creall(c1), cimagl(c1) - i2); + break; + case 6 + 2: + v = c1 - c2; + break; + default: + assert(0); + } + emplaceExp!(ComplexExp)(&ue, loc, v, type); + } + else if (e1.op == TOKsymoff) + { + SymOffExp soe = cast(SymOffExp)e1; + emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset - e2.toInteger()); + ue.exp().type = type; + } + else + { + emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() - e2.toInteger(), type); + } + return ue; +} + +extern (C++) UnionExp Mul(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + if (type.isfloating()) + { + complex_t c; + d_float80 r; + if (e1.type.isreal()) + { + r = e1.toReal(); + c = e2.toComplex(); + c = complex_t(r * creall(c), r * cimagl(c)); + } + else if (e1.type.isimaginary()) + { + r = e1.toImaginary(); + c = e2.toComplex(); + c = complex_t(-r * cimagl(c), r * creall(c)); + } + else if (e2.type.isreal()) + { + r = e2.toReal(); + c = e1.toComplex(); + c = complex_t(r * creall(c), r * cimagl(c)); + } + else if (e2.type.isimaginary()) + { + r = e2.toImaginary(); + c = e1.toComplex(); + c = complex_t(-r * cimagl(c), r * creall(c)); + } + else + c = e1.toComplex() * e2.toComplex(); + if (type.isreal()) + emplaceExp!(RealExp)(&ue, loc, creall(c), type); + else if (type.isimaginary()) + emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); + else if (type.iscomplex()) + emplaceExp!(ComplexExp)(&ue, loc, c, type); + else + assert(0); + } + else + { + emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() * e2.toInteger(), type); + } + return ue; +} + +extern (C++) UnionExp Div(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + if (type.isfloating()) + { + complex_t c; + d_float80 r; + //e1->type->print(); + //e2->type->print(); + if (e2.type.isreal()) + { + if (e1.type.isreal()) + { + emplaceExp!(RealExp)(&ue, loc, e1.toReal() / e2.toReal(), type); + return ue; + } + r = e2.toReal(); + c = e1.toComplex(); + c = complex_t(creall(c) / r, cimagl(c) / r); + } + else if (e2.type.isimaginary()) + { + r = e2.toImaginary(); + c = e1.toComplex(); + c = complex_t(cimagl(c) / r, -creall(c) / r); + } + else + { + c = e1.toComplex() / e2.toComplex(); + } + if (type.isreal()) + emplaceExp!(RealExp)(&ue, loc, creall(c), type); + else if (type.isimaginary()) + emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); + else if (type.iscomplex()) + emplaceExp!(ComplexExp)(&ue, loc, c, type); + else + assert(0); + } + else + { + sinteger_t n1; + sinteger_t n2; + sinteger_t n; + n1 = e1.toInteger(); + n2 = e2.toInteger(); + if (n2 == 0) + { + e2.error("divide by 0"); + n2 = 1; + } + if (e1.type.isunsigned() || e2.type.isunsigned()) + n = (cast(dinteger_t)n1) / (cast(dinteger_t)n2); + else + n = n1 / n2; + emplaceExp!(IntegerExp)(&ue, loc, n, type); + } + return ue; +} + +extern (C++) UnionExp Mod(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + if (type.isfloating()) + { + complex_t c; + if (e2.type.isreal()) + { + real_t r2 = e2.toReal(); + c = complex_t(Port.fmodl(e1.toReal(), r2), Port.fmodl(e1.toImaginary(), r2)); + } + else if (e2.type.isimaginary()) + { + real_t i2 = e2.toImaginary(); + c = complex_t(Port.fmodl(e1.toReal(), i2), Port.fmodl(e1.toImaginary(), i2)); + } + else + assert(0); + if (type.isreal()) + emplaceExp!(RealExp)(&ue, loc, creall(c), type); + else if (type.isimaginary()) + emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); + else if (type.iscomplex()) + emplaceExp!(ComplexExp)(&ue, loc, c, type); + else + assert(0); + } + else + { + sinteger_t n1; + sinteger_t n2; + sinteger_t n; + n1 = e1.toInteger(); + n2 = e2.toInteger(); + if (n2 == 0) + { + e2.error("divide by 0"); + n2 = 1; + } + if (n2 == -1 && !type.isunsigned()) + { + // Check for int.min % -1 + if (n1 == 0xFFFFFFFF80000000UL && type.toBasetype().ty != Tint64) + { + e2.error("integer overflow: int.min % -1"); + n2 = 1; + } + else if (n1 == 0x8000000000000000L) // long.min % -1 + { + e2.error("integer overflow: long.min % -1"); + n2 = 1; + } + } + if (e1.type.isunsigned() || e2.type.isunsigned()) + n = (cast(dinteger_t)n1) % (cast(dinteger_t)n2); + else + n = n1 % n2; + emplaceExp!(IntegerExp)(&ue, loc, n, type); + } + return ue; +} + +extern (C++) UnionExp Pow(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + // Handle integer power operations. + if (e2.type.isintegral()) + { + dinteger_t n = e2.toInteger(); + bool neg; + if (!e2.type.isunsigned() && cast(sinteger_t)n < 0) + { + if (e1.type.isintegral()) + { + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; + } + // Don't worry about overflow, from now on n is unsigned. + neg = true; + n = -n; + } + else + neg = false; + UnionExp ur, uv; + if (e1.type.iscomplex()) + { + emplaceExp!(ComplexExp)(&ur, loc, e1.toComplex(), e1.type); + emplaceExp!(ComplexExp)(&uv, loc, complex_t(1.0, 0.0), e1.type); + } + else if (e1.type.isfloating()) + { + emplaceExp!(RealExp)(&ur, loc, e1.toReal(), e1.type); + emplaceExp!(RealExp)(&uv, loc, ldouble(1.0), e1.type); + } + else + { + emplaceExp!(IntegerExp)(&ur, loc, e1.toInteger(), e1.type); + emplaceExp!(IntegerExp)(&uv, loc, 1, e1.type); + } + Expression r = ur.exp(); + Expression v = uv.exp(); + while (n != 0) + { + if (n & 1) + { + // v = v * r; + uv = Mul(v.type, v, r); + } + n >>= 1; + // r = r * r + ur = Mul(r.type, r, r); + } + if (neg) + { + // ue = 1.0 / v + UnionExp one; + emplaceExp!(RealExp)(&one, loc, ldouble(1.0), v.type); + uv = Div(v.type, one.exp(), v); + } + if (type.iscomplex()) + emplaceExp!(ComplexExp)(&ue, loc, v.toComplex(), type); + else if (type.isintegral()) + emplaceExp!(IntegerExp)(&ue, loc, v.toInteger(), type); + else + emplaceExp!(RealExp)(&ue, loc, v.toReal(), type); + } + else if (e2.type.isfloating()) + { + // x ^^ y for x < 0 and y not an integer is not defined; so set result as NaN + if (e1.toReal() < 0.0) + { + emplaceExp!(RealExp)(&ue, loc, Port.ldbl_nan, type); + } + else + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + } + else + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; +} + +extern (C++) UnionExp Shl(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() << e2.toInteger(), type); + return ue; +} + +extern (C++) UnionExp Shr(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + dinteger_t value = e1.toInteger(); + dinteger_t dcount = e2.toInteger(); + assert(dcount <= 0xFFFFFFFF); + uint count = cast(uint)dcount; + switch (e1.type.toBasetype().ty) + { + case Tint8: + value = cast(d_int8)value >> count; + break; + case Tuns8: + case Tchar: + value = cast(d_uns8)value >> count; + break; + case Tint16: + value = cast(d_int16)value >> count; + break; + case Tuns16: + case Twchar: + value = cast(d_uns16)value >> count; + break; + case Tint32: + value = cast(d_int32)value >> count; + break; + case Tuns32: + case Tdchar: + value = cast(d_uns32)value >> count; + break; + case Tint64: + value = cast(d_int64)value >> count; + break; + case Tuns64: + value = cast(d_uns64)value >> count; + break; + case Terror: + emplaceExp!(ErrorExp)(&ue); + return ue; + default: + assert(0); + } + emplaceExp!(IntegerExp)(&ue, loc, value, type); + return ue; +} + +extern (C++) UnionExp Ushr(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + dinteger_t value = e1.toInteger(); + dinteger_t dcount = e2.toInteger(); + assert(dcount <= 0xFFFFFFFF); + uint count = cast(uint)dcount; + switch (e1.type.toBasetype().ty) + { + case Tint8: + case Tuns8: + case Tchar: + // Possible only with >>>=. >>> always gets promoted to int. + value = (value & 0xFF) >> count; + break; + case Tint16: + case Tuns16: + case Twchar: + // Possible only with >>>=. >>> always gets promoted to int. + value = (value & 0xFFFF) >> count; + break; + case Tint32: + case Tuns32: + case Tdchar: + value = (value & 0xFFFFFFFF) >> count; + break; + case Tint64: + case Tuns64: + value = cast(d_uns64)value >> count; + break; + case Terror: + emplaceExp!(ErrorExp)(&ue); + return ue; + default: + assert(0); + } + emplaceExp!(IntegerExp)(&ue, loc, value, type); + return ue; +} + +extern (C++) UnionExp And(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + emplaceExp!(IntegerExp)(&ue, e1.loc, e1.toInteger() & e2.toInteger(), type); + return ue; +} + +extern (C++) UnionExp Or(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + emplaceExp!(IntegerExp)(&ue, e1.loc, e1.toInteger() | e2.toInteger(), type); + return ue; +} + +extern (C++) UnionExp Xor(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + emplaceExp!(IntegerExp)(&ue, e1.loc, e1.toInteger() ^ e2.toInteger(), type); + return ue; +} + +/* Also returns TOKcantexp if cannot be computed. + */ +extern (C++) UnionExp Equal(TOK op, Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + int cmp = 0; + real_t r1; + real_t r2; + //printf("Equal(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); + assert(op == TOKequal || op == TOKnotequal); + if (e1.op == TOKnull) + { + if (e2.op == TOKnull) + cmp = 1; + else if (e2.op == TOKstring) + { + StringExp es2 = cast(StringExp)e2; + cmp = (0 == es2.len); + } + else if (e2.op == TOKarrayliteral) + { + ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; + cmp = !es2.elements || (0 == es2.elements.dim); + } + else + { + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; + } + } + else if (e2.op == TOKnull) + { + if (e1.op == TOKstring) + { + StringExp es1 = cast(StringExp)e1; + cmp = (0 == es1.len); + } + else if (e1.op == TOKarrayliteral) + { + ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1; + cmp = !es1.elements || (0 == es1.elements.dim); + } + else + { + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; + } + } + else if (e1.op == TOKstring && e2.op == TOKstring) + { + StringExp es1 = cast(StringExp)e1; + StringExp es2 = cast(StringExp)e2; + if (es1.sz != es2.sz) + { + assert(global.errors); + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; + } + if (es1.len == es2.len && memcmp(es1.string, es2.string, es1.sz * es1.len) == 0) + cmp = 1; + else + cmp = 0; + } + else if (e1.op == TOKarrayliteral && e2.op == TOKarrayliteral) + { + ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1; + ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; + if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim)) + cmp = 1; // both arrays are empty + else if (!es1.elements || !es2.elements) + cmp = 0; + else if (es1.elements.dim != es2.elements.dim) + cmp = 0; + else + { + for (size_t i = 0; i < es1.elements.dim; i++) + { + Expression ee1 = (*es1.elements)[i]; + Expression ee2 = (*es2.elements)[i]; + ue = Equal(TOKequal, Type.tint32, ee1, ee2); + if (CTFEExp.isCantExp(ue.exp())) + return ue; + cmp = cast(int)ue.exp().toInteger(); + if (cmp == 0) + break; + } + } + } + else if (e1.op == TOKarrayliteral && e2.op == TOKstring) + { + // Swap operands and use common code + Expression etmp = e1; + e1 = e2; + e2 = etmp; + goto Lsa; + } + else if (e1.op == TOKstring && e2.op == TOKarrayliteral) + { + Lsa: + StringExp es1 = cast(StringExp)e1; + ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; + size_t dim1 = es1.len; + size_t dim2 = es2.elements ? es2.elements.dim : 0; + if (dim1 != dim2) + cmp = 0; + else + { + cmp = 1; // if dim1 winds up being 0 + for (size_t i = 0; i < dim1; i++) + { + uinteger_t c = es1.charAt(i); + Expression ee2 = (*es2.elements)[i]; + if (ee2.isConst() != 1) + { + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; + } + cmp = (c == ee2.toInteger()); + if (cmp == 0) + break; + } + } + } + else if (e1.op == TOKstructliteral && e2.op == TOKstructliteral) + { + StructLiteralExp es1 = cast(StructLiteralExp)e1; + StructLiteralExp es2 = cast(StructLiteralExp)e2; + if (es1.sd != es2.sd) + cmp = 0; + else if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim)) + cmp = 1; // both arrays are empty + else if (!es1.elements || !es2.elements) + cmp = 0; + else if (es1.elements.dim != es2.elements.dim) + cmp = 0; + else + { + cmp = 1; + for (size_t i = 0; i < es1.elements.dim; i++) + { + Expression ee1 = (*es1.elements)[i]; + Expression ee2 = (*es2.elements)[i]; + if (ee1 == ee2) + continue; + if (!ee1 || !ee2) + { + cmp = 0; + break; + } + ue = Equal(TOKequal, Type.tint32, ee1, ee2); + if (ue.exp().op == TOKcantexp) + return ue; + cmp = cast(int)ue.exp().toInteger(); + if (cmp == 0) + break; + } + } + if (cmp && es1.type.needsNested()) + { + if ((es1.sinit !is null) != (es2.sinit !is null)) + cmp = 0; + } + } + else if (e1.isConst() != 1 || e2.isConst() != 1) + { + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; + } + else if (e1.type.isreal()) + { + r1 = e1.toReal(); + r2 = e2.toReal(); + goto L1; + } + else if (e1.type.isimaginary()) + { + r1 = e1.toImaginary(); + r2 = e2.toImaginary(); + L1: + if (Port.isNan(r1) || Port.isNan(r2)) // if unordered + { + cmp = 0; + } + else + { + cmp = (r1 == r2); + } + } + else if (e1.type.iscomplex()) + { + cmp = e1.toComplex() == e2.toComplex(); + } + else if (e1.type.isintegral() || e1.type.toBasetype().ty == Tpointer) + { + cmp = (e1.toInteger() == e2.toInteger()); + } + else + { + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; + } + if (op == TOKnotequal) + cmp ^= 1; + emplaceExp!(IntegerExp)(&ue, loc, cmp, type); + return ue; +} + +extern (C++) UnionExp Identity(TOK op, Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + int cmp; + if (e1.op == TOKnull) + { + cmp = (e2.op == TOKnull); + } + else if (e2.op == TOKnull) + { + cmp = 0; + } + else if (e1.op == TOKsymoff && e2.op == TOKsymoff) + { + SymOffExp es1 = cast(SymOffExp)e1; + SymOffExp es2 = cast(SymOffExp)e2; + cmp = (es1.var == es2.var && es1.offset == es2.offset); + } + else + { + if (e1.type.isreal()) + { + cmp = RealEquals(e1.toReal(), e2.toReal()); + } + else if (e1.type.isimaginary()) + { + cmp = RealEquals(e1.toImaginary(), e2.toImaginary()); + } + else if (e1.type.iscomplex()) + { + complex_t v1 = e1.toComplex(); + complex_t v2 = e2.toComplex(); + cmp = RealEquals(creall(v1), creall(v2)) && RealEquals(cimagl(v1), cimagl(v1)); + } + else + { + ue = Equal((op == TOKidentity) ? TOKequal : TOKnotequal, type, e1, e2); + return ue; + } + } + if (op == TOKnotidentity) + cmp ^= 1; + emplaceExp!(IntegerExp)(&ue, loc, cmp, type); + return ue; +} + +extern (C++) UnionExp Cmp(TOK op, Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + dinteger_t n; + real_t r1; + real_t r2; + //printf("Cmp(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); + if (e1.op == TOKstring && e2.op == TOKstring) + { + StringExp es1 = cast(StringExp)e1; + StringExp es2 = cast(StringExp)e2; + size_t sz = es1.sz; + assert(sz == es2.sz); + size_t len = es1.len; + if (es2.len < len) + len = es2.len; + int cmp = memcmp(es1.string, es2.string, sz * len); + if (cmp == 0) + cmp = cast(int)(es1.len - es2.len); + switch (op) + { + case TOKlt: + n = cmp < 0; + break; + case TOKle: + n = cmp <= 0; + break; + case TOKgt: + n = cmp > 0; + break; + case TOKge: + n = cmp >= 0; + break; + case TOKleg: + n = 1; + break; + case TOKlg: + n = cmp != 0; + break; + case TOKunord: + n = 0; + break; + case TOKue: + n = cmp == 0; + break; + case TOKug: + n = cmp > 0; + break; + case TOKuge: + n = cmp >= 0; + break; + case TOKul: + n = cmp < 0; + break; + case TOKule: + n = cmp <= 0; + break; + default: + assert(0); + } + } + else if (e1.isConst() != 1 || e2.isConst() != 1) + { + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; + } + else if (e1.type.isreal()) + { + r1 = e1.toReal(); + r2 = e2.toReal(); + goto L1; + } + else if (e1.type.isimaginary()) + { + r1 = e1.toImaginary(); + r2 = e2.toImaginary(); + L1: + // Don't rely on compiler, handle NAN arguments separately + // (DMC does do it correctly) + if (Port.isNan(r1) || Port.isNan(r2)) // if unordered + { + switch (op) + { + case TOKlt: + n = 0; + break; + case TOKle: + n = 0; + break; + case TOKgt: + n = 0; + break; + case TOKge: + n = 0; + break; + case TOKleg: + n = 0; + break; + case TOKlg: + n = 0; + break; + case TOKunord: + n = 1; + break; + case TOKue: + n = 1; + break; + case TOKug: + n = 1; + break; + case TOKuge: + n = 1; + break; + case TOKul: + n = 1; + break; + case TOKule: + n = 1; + break; + default: + assert(0); + } + } + else + { + switch (op) + { + case TOKlt: + n = r1 < r2; + break; + case TOKle: + n = r1 <= r2; + break; + case TOKgt: + n = r1 > r2; + break; + case TOKge: + n = r1 >= r2; + break; + case TOKleg: + n = 1; + break; + case TOKlg: + n = r1 != r2; + break; + case TOKunord: + n = 0; + break; + case TOKue: + n = r1 == r2; + break; + case TOKug: + n = r1 > r2; + break; + case TOKuge: + n = r1 >= r2; + break; + case TOKul: + n = r1 < r2; + break; + case TOKule: + n = r1 <= r2; + break; + default: + assert(0); + } + } + } + else if (e1.type.iscomplex()) + { + assert(0); + } + else + { + sinteger_t n1; + sinteger_t n2; + n1 = e1.toInteger(); + n2 = e2.toInteger(); + if (e1.type.isunsigned() || e2.type.isunsigned()) + { + switch (op) + { + case TOKlt: + n = (cast(dinteger_t)n1) < (cast(dinteger_t)n2); + break; + case TOKle: + n = (cast(dinteger_t)n1) <= (cast(dinteger_t)n2); + break; + case TOKgt: + n = (cast(dinteger_t)n1) > (cast(dinteger_t)n2); + break; + case TOKge: + n = (cast(dinteger_t)n1) >= (cast(dinteger_t)n2); + break; + case TOKleg: + n = 1; + break; + case TOKlg: + n = (cast(dinteger_t)n1) != (cast(dinteger_t)n2); + break; + case TOKunord: + n = 0; + break; + case TOKue: + n = (cast(dinteger_t)n1) == (cast(dinteger_t)n2); + break; + case TOKug: + n = (cast(dinteger_t)n1) > (cast(dinteger_t)n2); + break; + case TOKuge: + n = (cast(dinteger_t)n1) >= (cast(dinteger_t)n2); + break; + case TOKul: + n = (cast(dinteger_t)n1) < (cast(dinteger_t)n2); + break; + case TOKule: + n = (cast(dinteger_t)n1) <= (cast(dinteger_t)n2); + break; + default: + assert(0); + } + } + else + { + switch (op) + { + case TOKlt: + n = n1 < n2; + break; + case TOKle: + n = n1 <= n2; + break; + case TOKgt: + n = n1 > n2; + break; + case TOKge: + n = n1 >= n2; + break; + case TOKleg: + n = 1; + break; + case TOKlg: + n = n1 != n2; + break; + case TOKunord: + n = 0; + break; + case TOKue: + n = n1 == n2; + break; + case TOKug: + n = n1 > n2; + break; + case TOKuge: + n = n1 >= n2; + break; + case TOKul: + n = n1 < n2; + break; + case TOKule: + n = n1 <= n2; + break; + default: + assert(0); + } + } + } + emplaceExp!(IntegerExp)(&ue, loc, n, type); + return ue; +} + +/* Also returns TOKcantexp if cannot be computed. + * to: type to cast to + * type: type to paint the result + */ +extern (C++) UnionExp Cast(Type type, Type to, Expression e1) +{ + UnionExp ue; + Loc loc = e1.loc; + Type tb = to.toBasetype(); + Type typeb = type.toBasetype(); + //printf("Cast(type = %s, to = %s, e1 = %s)\n", type->toChars(), to->toChars(), e1->toChars()); + //printf("\te1->type = %s\n", e1->type->toChars()); + if (e1.type.equals(type) && type.equals(to)) + { + emplaceExp!(UnionExp)(&ue, e1); + return ue; + } + if (e1.op == TOKvector && (cast(TypeVector)e1.type).basetype.equals(type) && type.equals(to)) + { + Expression ex = (cast(VectorExp)e1).e1; + emplaceExp!(UnionExp)(&ue, ex); + return ue; + } + if (e1.type.implicitConvTo(to) >= MATCHconst || to.implicitConvTo(e1.type) >= MATCHconst) + { + goto L1; + } + // Allow covariant converions of delegates + // (Perhaps implicit conversion from pure to impure should be a MATCHconst, + // then we wouldn't need this extra check.) + if (e1.type.toBasetype().ty == Tdelegate && e1.type.implicitConvTo(to) == MATCHconvert) + { + goto L1; + } + /* Allow casting from one string type to another + */ + if (e1.op == TOKstring) + { + if (tb.ty == Tarray && typeb.ty == Tarray && tb.nextOf().size() == typeb.nextOf().size()) + { + goto L1; + } + } + if (e1.op == TOKarrayliteral && typeb == tb) + { + L1: + Expression ex = expType(to, e1); + emplaceExp!(UnionExp)(&ue, ex); + return ue; + } + if (e1.isConst() != 1) + { + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + } + else if (tb.ty == Tbool) + { + emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() != 0, type); + } + else if (type.isintegral()) + { + if (e1.type.isfloating()) + { + dinteger_t result; + real_t r = e1.toReal(); + switch (typeb.ty) + { + case Tint8: + result = cast(d_int8)r; + break; + case Tchar: + case Tuns8: + result = cast(d_uns8)r; + break; + case Tint16: + result = cast(d_int16)r; + break; + case Twchar: + case Tuns16: + result = cast(d_uns16)r; + break; + case Tint32: + result = cast(d_int32)r; + break; + case Tdchar: + case Tuns32: + result = cast(d_uns32)r; + break; + case Tint64: + result = cast(d_int64)r; + break; + case Tuns64: + result = cast(d_uns64)r; + break; + default: + assert(0); + } + emplaceExp!(IntegerExp)(&ue, loc, result, type); + } + else if (type.isunsigned()) + emplaceExp!(IntegerExp)(&ue, loc, e1.toUInteger(), type); + else + emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger(), type); + } + else if (tb.isreal()) + { + real_t value = e1.toReal(); + emplaceExp!(RealExp)(&ue, loc, value, type); + } + else if (tb.isimaginary()) + { + real_t value = e1.toImaginary(); + emplaceExp!(RealExp)(&ue, loc, value, type); + } + else if (tb.iscomplex()) + { + complex_t value = e1.toComplex(); + emplaceExp!(ComplexExp)(&ue, loc, value, type); + } + else if (tb.isscalar()) + { + emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger(), type); + } + else if (tb.ty == Tvoid) + { + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + } + else if (tb.ty == Tstruct && e1.op == TOKint64) + { + // Struct = 0; + StructDeclaration sd = tb.toDsymbol(null).isStructDeclaration(); + assert(sd); + auto elements = new Expressions(); + for (size_t i = 0; i < sd.fields.dim; i++) + { + VarDeclaration v = sd.fields[i]; + UnionExp zero; + emplaceExp!(IntegerExp)(&zero, 0); + ue = Cast(v.type, v.type, zero.exp()); + if (ue.exp().op == TOKcantexp) + return ue; + elements.push(ue.exp().copy()); + } + emplaceExp!(StructLiteralExp)(&ue, loc, sd, elements); + ue.exp().type = type; + } + else + { + if (type != Type.terror) + { + // have to change to Internal Compiler Error + // all invalid casts should be handled already in Expression::castTo(). + error(loc, "cannot cast %s to %s", e1.type.toChars(), type.toChars()); + } + emplaceExp!(ErrorExp)(&ue); + } + return ue; +} + +extern (C++) UnionExp ArrayLength(Type type, Expression e1) +{ + UnionExp ue; + Loc loc = e1.loc; + if (e1.op == TOKstring) + { + StringExp es1 = cast(StringExp)e1; + emplaceExp!(IntegerExp)(&ue, loc, es1.len, type); + } + else if (e1.op == TOKarrayliteral) + { + ArrayLiteralExp ale = cast(ArrayLiteralExp)e1; + size_t dim = ale.elements ? ale.elements.dim : 0; + emplaceExp!(IntegerExp)(&ue, loc, dim, type); + } + else if (e1.op == TOKassocarrayliteral) + { + AssocArrayLiteralExp ale = cast(AssocArrayLiteralExp)e1; + size_t dim = ale.keys.dim; + emplaceExp!(IntegerExp)(&ue, loc, dim, type); + } + else if (e1.type.toBasetype().ty == Tsarray) + { + Expression e = (cast(TypeSArray)e1.type.toBasetype()).dim; + emplaceExp!(UnionExp)(&ue, e); + } + else + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; +} + +/* Also return TOKcantexp if this fails + */ +extern (C++) UnionExp Index(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Loc loc = e1.loc; + //printf("Index(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); + assert(e1.type); + if (e1.op == TOKstring && e2.op == TOKint64) + { + StringExp es1 = cast(StringExp)e1; + uinteger_t i = e2.toInteger(); + if (i >= es1.len) + { + e1.error("string index %llu is out of bounds [0 .. %llu]", i, cast(ulong)es1.len); + emplaceExp!(ErrorExp)(&ue); + } + else + { + emplaceExp!(IntegerExp)(&ue, loc, es1.charAt(i), type); + } + } + else if (e1.type.toBasetype().ty == Tsarray && e2.op == TOKint64) + { + TypeSArray tsa = cast(TypeSArray)e1.type.toBasetype(); + uinteger_t length = tsa.dim.toInteger(); + uinteger_t i = e2.toInteger(); + if (i >= length) + { + e1.error("array index %llu is out of bounds %s[0 .. %llu]", i, e1.toChars(), length); + emplaceExp!(ErrorExp)(&ue); + } + else if (e1.op == TOKarrayliteral) + { + ArrayLiteralExp ale = cast(ArrayLiteralExp)e1; + Expression e = (*ale.elements)[cast(size_t)i]; + e.type = type; + e.loc = loc; + if (hasSideEffect(e)) + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + else + emplaceExp!(UnionExp)(&ue, e); + } + else + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + } + else if (e1.type.toBasetype().ty == Tarray && e2.op == TOKint64) + { + uinteger_t i = e2.toInteger(); + if (e1.op == TOKarrayliteral) + { + ArrayLiteralExp ale = cast(ArrayLiteralExp)e1; + if (i >= ale.elements.dim) + { + e1.error("array index %llu is out of bounds %s[0 .. %u]", i, e1.toChars(), ale.elements.dim); + emplaceExp!(ErrorExp)(&ue); + } + else + { + Expression e = (*ale.elements)[cast(size_t)i]; + e.type = type; + e.loc = loc; + if (hasSideEffect(e)) + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + else + emplaceExp!(UnionExp)(&ue, e); + } + } + else + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + } + else if (e1.op == TOKassocarrayliteral) + { + AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)e1; + /* Search the keys backwards, in case there are duplicate keys + */ + for (size_t i = ae.keys.dim; i;) + { + i--; + Expression ekey = (*ae.keys)[i]; + ue = Equal(TOKequal, Type.tbool, ekey, e2); + if (CTFEExp.isCantExp(ue.exp())) + return ue; + if (ue.exp().isBool(true)) + { + Expression e = (*ae.values)[i]; + e.type = type; + e.loc = loc; + if (hasSideEffect(e)) + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + else + emplaceExp!(UnionExp)(&ue, e); + return ue; + } + } + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + } + else + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; +} + +/* Also return TOKcantexp if this fails + */ +extern (C++) UnionExp Slice(Type type, Expression e1, Expression lwr, Expression upr) +{ + UnionExp ue; + Loc loc = e1.loc; + static if (LOG) + { + printf("Slice()\n"); + if (lwr) + { + printf("\te1 = %s\n", e1.toChars()); + printf("\tlwr = %s\n", lwr.toChars()); + printf("\tupr = %s\n", upr.toChars()); + } + } + if (e1.op == TOKstring && lwr.op == TOKint64 && upr.op == TOKint64) + { + StringExp es1 = cast(StringExp)e1; + uinteger_t ilwr = lwr.toInteger(); + uinteger_t iupr = upr.toInteger(); + if (iupr > es1.len || ilwr > iupr) + { + e1.error("string slice [%llu .. %llu] is out of bounds", ilwr, iupr); + emplaceExp!(ErrorExp)(&ue); + } + else + { + size_t len = cast(size_t)(iupr - ilwr); + ubyte sz = es1.sz; + void* s = mem.xmalloc((len + 1) * sz); + memcpy(cast(char*)s, cast(char*)es1.string + ilwr * sz, len * sz); + memset(cast(char*)s + len * sz, 0, sz); + emplaceExp!(StringExp)(&ue, loc, s, len, es1.postfix); + StringExp es = cast(StringExp)ue.exp(); + es.sz = sz; + es.committed = es1.committed; + es.type = type; + } + } + else if (e1.op == TOKarrayliteral && lwr.op == TOKint64 && upr.op == TOKint64 && !hasSideEffect(e1)) + { + ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1; + uinteger_t ilwr = lwr.toInteger(); + uinteger_t iupr = upr.toInteger(); + if (iupr > es1.elements.dim || ilwr > iupr) + { + e1.error("array slice [%llu .. %llu] is out of bounds", ilwr, iupr); + emplaceExp!(ErrorExp)(&ue); + } + else + { + auto elements = new Expressions(); + elements.setDim(cast(size_t)(iupr - ilwr)); + memcpy(elements.tdata(), es1.elements.tdata() + ilwr, cast(size_t)(iupr - ilwr) * ((*es1.elements)[0]).sizeof); + emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, elements); + ue.exp().type = type; + } + } + else + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + assert(ue.exp().type); + return ue; +} + +/* Set a slice of char/integer array literal 'existingAE' from a string 'newval'. + * existingAE[firstIndex..firstIndex+newval.length] = newval. + */ +extern (C++) void sliceAssignArrayLiteralFromString(ArrayLiteralExp existingAE, StringExp newval, size_t firstIndex) +{ + size_t newlen = newval.len; + size_t sz = newval.sz; + void* s = newval.string; + Type elemType = existingAE.type.nextOf(); + for (size_t j = 0; j < newlen; j++) + { + dinteger_t val; + switch (sz) + { + case 1: + val = (cast(char*)s)[j]; + break; + case 2: + val = (cast(utf16_t*)s)[j]; + break; + case 4: + val = (cast(utf32_t*)s)[j]; + break; + default: + assert(0); + break; + } + (*existingAE.elements)[j + firstIndex] = new IntegerExp(newval.loc, val, elemType); + } +} + +/* Set a slice of string 'existingSE' from a char array literal 'newae'. + * existingSE[firstIndex..firstIndex+newae.length] = newae. + */ +extern (C++) void sliceAssignStringFromArrayLiteral(StringExp existingSE, ArrayLiteralExp newae, size_t firstIndex) +{ + void* s = existingSE.string; + for (size_t j = 0; j < newae.elements.dim; j++) + { + uint val = cast(uint)(*newae.elements)[j].toInteger(); + switch (existingSE.sz) + { + case 1: + (cast(char*)s)[j + firstIndex] = cast(char)val; + break; + case 2: + (cast(utf16_t*)s)[j + firstIndex] = cast(utf16_t)val; + break; + case 4: + (cast(utf32_t*)s)[j + firstIndex] = cast(utf32_t)val; + break; + default: + assert(0); + break; + } + } +} + +/* Set a slice of string 'existingSE' from a string 'newstr'. + * existingSE[firstIndex..firstIndex+newstr.length] = newstr. + */ +extern (C++) void sliceAssignStringFromString(StringExp existingSE, StringExp newstr, size_t firstIndex) +{ + void* s = existingSE.string; + size_t sz = existingSE.sz; + assert(sz == newstr.sz); + memcpy(cast(char*)s + firstIndex * sz, newstr.string, sz * newstr.len); +} + +/* Compare a string slice with another string slice. + * Conceptually equivalent to memcmp( se1[lo1..lo1+len], se2[lo2..lo2+len]) + */ +extern (C++) int sliceCmpStringWithString(StringExp se1, StringExp se2, size_t lo1, size_t lo2, size_t len) +{ + void* s1 = se1.string; + void* s2 = se2.string; + size_t sz = se1.sz; + assert(sz == se2.sz); + return memcmp(cast(char*)s1 + sz * lo1, cast(char*)s2 + sz * lo2, sz * len); +} + +/* Compare a string slice with an array literal slice + * Conceptually equivalent to memcmp( se1[lo1..lo1+len], ae2[lo2..lo2+len]) + */ +extern (C++) int sliceCmpStringWithArray(StringExp se1, ArrayLiteralExp ae2, size_t lo1, size_t lo2, size_t len) +{ + void* s = se1.string; + size_t sz = se1.sz; + for (size_t j = 0; j < len; j++) + { + uint val2 = cast(uint)(*ae2.elements)[j + lo2].toInteger(); + uint val1; + switch (sz) + { + case 1: + val1 = (cast(char*)s)[j + lo1]; + break; + case 2: + val1 = (cast(utf16_t*)s)[j + lo1]; + break; + case 4: + val1 = (cast(utf32_t*)s)[j + lo1]; + break; + default: + assert(0); + break; + } + int c = val1 - val2; + if (c) + return c; + } + return 0; +} + +/* Also return TOKcantexp if this fails + */ +extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) +{ + UnionExp ue; + Expression e = CTFEExp.cantexp; + Loc loc = e1.loc; + Type t; + Type t1 = e1.type.toBasetype(); + Type t2 = e2.type.toBasetype(); + //printf("Cat(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); + //printf("\tt1 = %s, t2 = %s, type = %s\n", t1->toChars(), t2->toChars(), type->toChars()); + if (e1.op == TOKnull && (e2.op == TOKint64 || e2.op == TOKstructliteral)) + { + e = e2; + t = t1; + goto L2; + } + else if ((e1.op == TOKint64 || e1.op == TOKstructliteral) && e2.op == TOKnull) + { + e = e1; + t = t2; + L2: + Type tn = e.type.toBasetype(); + if (tn.ty == Tchar || tn.ty == Twchar || tn.ty == Tdchar) + { + // Create a StringExp + if (t.nextOf()) + t = t.nextOf().toBasetype(); + ubyte sz = cast(ubyte)t.size(); + dinteger_t v = e.toInteger(); + size_t len = (t.ty == tn.ty) ? 1 : utf_codeLength(sz, cast(dchar_t)v); + void* s = mem.xmalloc((len + 1) * sz); + if (t.ty == tn.ty) + memcpy(s, &v, sz); + else + utf_encode(sz, s, cast(dchar_t)v); + // Add terminating 0 + memset(cast(char*)s + len * sz, 0, sz); + emplaceExp!(StringExp)(&ue, loc, s, len); + StringExp es = cast(StringExp)ue.exp(); + es.sz = sz; + es.committed = 1; + } + else + { + // Create an ArrayLiteralExp + auto elements = new Expressions(); + elements.push(e); + emplaceExp!(ArrayLiteralExp)(&ue, e.loc, elements); + } + ue.exp().type = type; + assert(ue.exp().type); + return ue; + } + else if (e1.op == TOKnull && e2.op == TOKnull) + { + if (type == e1.type) + { + // Handle null ~= null + if (t1.ty == Tarray && t2 == t1.nextOf()) + { + emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, e2); + ue.exp().type = type; + assert(ue.exp().type); + return ue; + } + else + { + emplaceExp!(UnionExp)(&ue, e1); + assert(ue.exp().type); + return ue; + } + } + if (type == e2.type) + { + emplaceExp!(UnionExp)(&ue, e2); + assert(ue.exp().type); + return ue; + } + emplaceExp!(NullExp)(&ue, e1.loc, type); + assert(ue.exp().type); + return ue; + } + else if (e1.op == TOKstring && e2.op == TOKstring) + { + // Concatenate the strings + StringExp es1 = cast(StringExp)e1; + StringExp es2 = cast(StringExp)e2; + size_t len = es1.len + es2.len; + ubyte sz = es1.sz; + if (sz != es2.sz) + { + /* Can happen with: + * auto s = "foo"d ~ "bar"c; + */ + assert(global.errors); + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + assert(ue.exp().type); + return ue; + } + void* s = mem.xmalloc((len + 1) * sz); + memcpy(cast(char*)s, es1.string, es1.len * sz); + memcpy(cast(char*)s + es1.len * sz, es2.string, es2.len * sz); + // Add terminating 0 + memset(cast(char*)s + len * sz, 0, sz); + emplaceExp!(StringExp)(&ue, loc, s, len); + StringExp es = cast(StringExp)ue.exp(); + es.sz = sz; + es.committed = es1.committed | es2.committed; + es.type = type; + assert(ue.exp().type); + return ue; + } + else if (e2.op == TOKstring && e1.op == TOKarrayliteral && t1.nextOf().isintegral()) + { + // [chars] ~ string --> [chars] + StringExp es = cast(StringExp)e2; + ArrayLiteralExp ea = cast(ArrayLiteralExp)e1; + size_t len = es.len + ea.elements.dim; + auto elems = new Expressions(); + elems.setDim(len); + for (size_t i = 0; i < ea.elements.dim; ++i) + { + (*elems)[i] = (*ea.elements)[i]; + } + emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, elems); + ArrayLiteralExp dest = cast(ArrayLiteralExp)ue.exp(); + dest.type = type; + sliceAssignArrayLiteralFromString(dest, es, ea.elements.dim); + assert(ue.exp().type); + return ue; + } + else if (e1.op == TOKstring && e2.op == TOKarrayliteral && t2.nextOf().isintegral()) + { + // string ~ [chars] --> [chars] + StringExp es = cast(StringExp)e1; + ArrayLiteralExp ea = cast(ArrayLiteralExp)e2; + size_t len = es.len + ea.elements.dim; + auto elems = new Expressions(); + elems.setDim(len); + for (size_t i = 0; i < ea.elements.dim; ++i) + { + (*elems)[es.len + i] = (*ea.elements)[i]; + } + emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, elems); + ArrayLiteralExp dest = cast(ArrayLiteralExp)ue.exp(); + dest.type = type; + sliceAssignArrayLiteralFromString(dest, es, 0); + assert(ue.exp().type); + return ue; + } + else if (e1.op == TOKstring && e2.op == TOKint64) + { + // string ~ char --> string + StringExp es1 = cast(StringExp)e1; + StringExp es; + ubyte sz = es1.sz; + dinteger_t v = e2.toInteger(); + // Is it a concatentation of homogenous types? + // (char[] ~ char, wchar[]~wchar, or dchar[]~dchar) + bool homoConcat = (sz == t2.size()); + size_t len = es1.len; + len += homoConcat ? 1 : utf_codeLength(sz, cast(dchar_t)v); + void* s = mem.xmalloc((len + 1) * sz); + memcpy(s, es1.string, es1.len * sz); + if (homoConcat) + memcpy(cast(char*)s + (sz * es1.len), &v, sz); + else + utf_encode(sz, cast(char*)s + (sz * es1.len), cast(dchar_t)v); + // Add terminating 0 + memset(cast(char*)s + len * sz, 0, sz); + emplaceExp!(StringExp)(&ue, loc, s, len); + es = cast(StringExp)ue.exp(); + es.sz = sz; + es.committed = es1.committed; + es.type = type; + assert(ue.exp().type); + return ue; + } + else if (e1.op == TOKint64 && e2.op == TOKstring) + { + // Concatenate the strings + StringExp es2 = cast(StringExp)e2; + size_t len = 1 + es2.len; + ubyte sz = es2.sz; + dinteger_t v = e1.toInteger(); + void* s = mem.xmalloc((len + 1) * sz); + memcpy(cast(char*)s, &v, sz); + memcpy(cast(char*)s + sz, es2.string, es2.len * sz); + // Add terminating 0 + memset(cast(char*)s + len * sz, 0, sz); + emplaceExp!(StringExp)(&ue, loc, s, len); + StringExp es = cast(StringExp)ue.exp(); + es.sz = sz; + es.committed = es2.committed; + es.type = type; + assert(ue.exp().type); + return ue; + } + else if (e1.op == TOKarrayliteral && e2.op == TOKarrayliteral && t1.nextOf().equals(t2.nextOf())) + { + // Concatenate the arrays + ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1; + ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; + emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, cast(Expressions*)es1.elements.copy()); + es1 = cast(ArrayLiteralExp)ue.exp(); + es1.elements.insert(es1.elements.dim, es2.elements); + e = es1; + if (type.toBasetype().ty == Tsarray) + { + e.type = t1.nextOf().sarrayOf(es1.elements.dim); + } + else + e.type = type; + assert(ue.exp().type); + return ue; + } + else if (e1.op == TOKarrayliteral && e2.op == TOKnull && t1.nextOf().equals(t2.nextOf())) + { + e = e1; + goto L3; + } + else if (e1.op == TOKnull && e2.op == TOKarrayliteral && t1.nextOf().equals(t2.nextOf())) + { + e = e2; + L3: + // Concatenate the array with null + ArrayLiteralExp es = cast(ArrayLiteralExp)e; + emplaceExp!(ArrayLiteralExp)(&ue, es.loc, cast(Expressions*)es.elements.copy()); + es = cast(ArrayLiteralExp)ue.exp(); + e = es; + if (type.toBasetype().ty == Tsarray) + { + e.type = t1.nextOf().sarrayOf(es.elements.dim); + } + else + e.type = type; + assert(ue.exp().type); + return ue; + } + else if ((e1.op == TOKarrayliteral || e1.op == TOKnull) && e1.type.toBasetype().nextOf() && e1.type.toBasetype().nextOf().equals(e2.type)) + { + ArrayLiteralExp es1; + if (e1.op == TOKarrayliteral) + { + es1 = cast(ArrayLiteralExp)e1; + emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, cast(Expressions*)es1.elements.copy()); + es1 = cast(ArrayLiteralExp)ue.exp(); + es1.elements.push(e2); + } + else + { + emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, e2); + es1 = cast(ArrayLiteralExp)ue.exp(); + } + e = es1; + if (type.toBasetype().ty == Tsarray) + { + e.type = e2.type.sarrayOf(es1.elements.dim); + } + else + e.type = type; + assert(ue.exp().type); + return ue; + } + else if (e2.op == TOKarrayliteral && e2.type.toBasetype().nextOf().equals(e1.type)) + { + ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; + emplaceExp!(ArrayLiteralExp)(&ue, es2.loc, cast(Expressions*)es2.elements.copy()); + es2 = cast(ArrayLiteralExp)ue.exp(); + es2.elements.shift(e1); + e = es2; + if (type.toBasetype().ty == Tsarray) + { + e.type = e1.type.sarrayOf(es2.elements.dim); + } + else + e.type = type; + assert(ue.exp().type); + return ue; + } + else if (e1.op == TOKnull && e2.op == TOKstring) + { + t = e1.type; + e = e2; + goto L1; + } + else if (e1.op == TOKstring && e2.op == TOKnull) + { + e = e1; + t = e2.type; + L1: + Type tb = t.toBasetype(); + if (tb.ty == Tarray && tb.nextOf().equivalent(e.type)) + { + auto expressions = new Expressions(); + expressions.push(e); + emplaceExp!(ArrayLiteralExp)(&ue, loc, expressions); + e = ue.exp(); + e.type = t; + } + else + { + emplaceExp!(UnionExp)(&ue, e); + e = ue.exp(); + } + if (!e.type.equals(type)) + { + StringExp se = cast(StringExp)e.copy(); + e = se.castTo(null, type); + emplaceExp!(UnionExp)(&ue, e); + e = ue.exp(); + } + } + else + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + assert(ue.exp().type); + return ue; +} + +extern (C++) UnionExp Ptr(Type type, Expression e1) +{ + //printf("Ptr(e1 = %s)\n", e1->toChars()); + UnionExp ue; + if (e1.op == TOKadd) + { + AddExp ae = cast(AddExp)e1; + if (ae.e1.op == TOKaddress && ae.e2.op == TOKint64) + { + AddrExp ade = cast(AddrExp)ae.e1; + if (ade.e1.op == TOKstructliteral) + { + StructLiteralExp se = cast(StructLiteralExp)ade.e1; + uint offset = cast(uint)ae.e2.toInteger(); + Expression e = se.getField(type, offset); + if (e) + { + emplaceExp!(UnionExp)(&ue, e); + return ue; + } + } + } + } + emplaceExp!(CTFEExp)(&ue, TOKcantexp); + return ue; +} diff --git a/src/cppmangle.c b/src/cppmangle.c deleted file mode 100644 index 51f795113d31..000000000000 --- a/src/cppmangle.c +++ /dev/null @@ -1,1921 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (c) 1999-2014 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 - * https://github.com/D-Programming-Language/dmd/blob/master/src/cppmangle.c - */ - -#include -#include -#include - -#include "mars.h" -#include "dsymbol.h" -#include "mtype.h" -#include "scope.h" -#include "init.h" -#include "expression.h" -#include "attrib.h" -#include "declaration.h" -#include "template.h" -#include "id.h" -#include "enum.h" -#include "import.h" -#include "aggregate.h" -#include "target.h" - -/* Do mangling for C++ linkage. - * No attempt is made to support mangling of templates, operator - * overloading, or special functions. - * - * So why don't we use the C++ ABI for D name mangling? - * Because D supports a lot of things (like modules) that the C++ - * ABI has no concept of. These affect every D mangled name, - * so nothing would be compatible anyway. - */ - -#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS - -/* - * Follows Itanium C++ ABI 1.86 - */ - -class CppMangleVisitor : public Visitor -{ - Objects components; - OutBuffer buf; - bool is_top_level; - bool components_on; - - void writeBase36(size_t i) - { - if (i >= 36) - { - writeBase36(i / 36); - i %= 36; - } - if (i < 10) - buf.writeByte((char)(i + '0')); - else if (i < 36) - buf.writeByte((char)(i - 10 + 'A')); - else - assert(0); - } - - bool substitute(RootObject *p) - { - //printf("substitute %s\n", p ? p->toChars() : NULL); - if (components_on) - for (size_t i = 0; i < components.dim; i++) - { - if (p == components[i]) - { - /* Sequence is S_, S0_, .., S9_, SA_, ..., SZ_, S10_, ... - */ - buf.writeByte('S'); - if (i) - writeBase36(i - 1); - buf.writeByte('_'); - return true; - } - } - return false; - } - - bool exist(RootObject *p) - { - //printf("exist %s\n", p ? p->toChars() : NULL); - if (components_on) - for (size_t i = 0; i < components.dim; i++) - { - if (p == components[i]) - { - return true; - } - } - return false; - } - - void store(RootObject *p) - { - //printf("store %s\n", p ? p->toChars() : NULL); - if (components_on) - components.push(p); - } - - void source_name(Dsymbol *s, bool skipname = false) - { - //printf("source_name(%s)\n", s->toChars()); - TemplateInstance *ti = s->isTemplateInstance(); - if (ti) - { - if (!skipname && !substitute(ti->tempdecl)) - { - store(ti->tempdecl); - const char *name = ti->toAlias()->ident->toChars(); - buf.printf("%d%s", strlen(name), name); - } - buf.writeByte('I'); - bool is_var_arg = false; - for (size_t i = 0; i < ti->tiargs->dim; i++) - { - RootObject *o = (RootObject *)(*ti->tiargs)[i]; - - TemplateParameter *tp = NULL; - TemplateValueParameter *tv = NULL; - TemplateTupleParameter *tt = NULL; - if (!is_var_arg) - { - TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration(); - assert(td); - tp = (*td->parameters)[i]; - tv = tp->isTemplateValueParameter(); - tt = tp->isTemplateTupleParameter(); - } - /* - * ::= # type or template - * ::= # simple expressions - */ - - if (tt) - { - buf.writeByte('I'); - is_var_arg = true; - tp = NULL; - } - - if (tv) - { - // ::= L E # integer literal - if (tv->valType->isintegral()) - { - Expression *e = isExpression(o); - assert(e); - buf.writeByte('L'); - tv->valType->accept(this); - if (tv->valType->isunsigned()) - { - buf.printf("%llu", e->toUInteger()); - } - else - { - sinteger_t val = e->toInteger(); - if (val < 0) - { - val = -val; - buf.writeByte('n'); - } - buf.printf("%lld", val); - } - buf.writeByte('E'); - } - else - { - s->error("Internal Compiler Error: C++ %s template value parameter is not supported", tv->valType->toChars()); - assert(0); - } - } - else if (!tp || tp->isTemplateTypeParameter()) - { - Type *t = isType(o); - assert(t); - t->accept(this); - } - else if (tp->isTemplateAliasParameter()) - { - Dsymbol *d = isDsymbol(o); - Expression *e = isExpression(o); - if (!d && !e) - { - s->error("Internal Compiler Error: %s is unsupported parameter for C++ template: (%s)", o->toChars()); - assert(0); - } - if (d && d->isFuncDeclaration()) - { - bool is_nested = d->toParent() && !d->toParent()->isModule() && ((TypeFunction *)d->isFuncDeclaration()->type)->linkage == LINKcpp; - if (is_nested) buf.writeByte('X'); - buf.writeByte('L'); - mangle_function(d->isFuncDeclaration()); - buf.writeByte('E'); - if (is_nested) buf.writeByte('E'); - } - else if (e && e->op == TOKvar && ((VarExp*)e)->var->isVarDeclaration()) - { - VarDeclaration *vd = ((VarExp*)e)->var->isVarDeclaration(); - buf.writeByte('L'); - mangle_variable(vd, true); - buf.writeByte('E'); - } - else if (d && d->isTemplateDeclaration() && d->isTemplateDeclaration()->onemember) - { - if (!substitute(d)) - { - cpp_mangle_name(d, false); - } - } - else - { - s->error("Internal Compiler Error: %s is unsupported parameter for C++ template", o->toChars()); - assert(0); - } - - } - else - { - s->error("Internal Compiler Error: C++ templates support only integral value, type parameters, alias templates and alias function parameters"); - assert(0); - } - } - if (is_var_arg) - { - buf.writeByte('E'); - } - buf.writeByte('E'); - return; - } - else - { - const char *name = s->ident->toChars(); - buf.printf("%d%s", strlen(name), name); - } - } - - void prefix_name(Dsymbol *s) - { - //printf("prefix_name(%s)\n", s->toChars()); - if (!substitute(s)) - { - Dsymbol *p = s->toParent(); - if (p && p->isTemplateInstance()) - { - s = p; - if (exist(p->isTemplateInstance()->tempdecl)) - { - p = NULL; - } - else - { - p = p->toParent(); - } - } - - if (p && !p->isModule()) - { - prefix_name(p); - } - store(s); - source_name(s); - } - } - - /* Is s the initial qualifier? - */ - bool is_initial_qualifier(Dsymbol *s) - { - Dsymbol *p = s->toParent(); - if (p && p->isTemplateInstance()) - { - if (exist(p->isTemplateInstance()->tempdecl)) - { - return true; - } - p = p->toParent(); - } - - return !p || p->isModule(); - } - - void cpp_mangle_name(Dsymbol *s, bool qualified) - { - //printf("cpp_mangle_name(%s, %d)\n", s->toChars(), qualified); - Dsymbol *p = s->toParent(); - Dsymbol *se = s; - bool dont_write_prefix = false; - if (p && p->isTemplateInstance()) - { - se = p; - if (exist(p->isTemplateInstance()->tempdecl)) - dont_write_prefix = true; - p = p->toParent(); - } - - if (p && !p->isModule()) - { - /* The N..E is not required if: - * 1. the parent is 'std' - * 2. 'std' is the initial qualifier - * 3. there is no CV-qualifier or a ref-qualifier for a member function - * ABI 5.1.8 - */ - if (p->ident == Id::std && - is_initial_qualifier(p) && - !qualified) - { - if (s->ident == Id::allocator) - { - buf.writestring("Sa"); // "Sa" is short for ::std::allocator - source_name(se, true); - } - else if (s->ident == Id::basic_string) - { - components_on = false; // turn off substitutions - buf.writestring("Sb"); // "Sb" is short for ::std::basic_string - size_t off = buf.offset; - source_name(se, true); - components_on = true; - - // Replace ::std::basic_string < char, ::std::char_traits, ::std::allocator > - // with Ss - //printf("xx: '%.*s'\n", (int)(buf.offset - off), buf.data + off); - if (buf.offset - off >= 26 && - memcmp(buf.data + off, "IcSt11char_traitsIcESaIcEE", 26) == 0) - { - buf.remove(off - 2, 28); - buf.insert(off - 2, (const char *)"Ss", 2); - return; - } - buf.setsize(off); - source_name(se, true); - } - else if (s->ident == Id::basic_istream || - s->ident == Id::basic_ostream || - s->ident == Id::basic_iostream) - { - /* Replace - * ::std::basic_istream > with Si - * ::std::basic_ostream > with So - * ::std::basic_iostream > with Sd - */ - size_t off = buf.offset; - components_on = false; // turn off substitutions - source_name(se, true); - components_on = true; - - //printf("xx: '%.*s'\n", (int)(buf.offset - off), buf.data + off); - if (buf.offset - off >= 21 && - memcmp(buf.data + off, "IcSt11char_traitsIcEE", 21) == 0) - { - buf.remove(off, 21); - char mbuf[2]; - mbuf[0] = 'S'; - mbuf[1] = 'i'; - if (s->ident == Id::basic_ostream) - mbuf[1] = 'o'; - else if(s->ident == Id::basic_iostream) - mbuf[1] = 'd'; - buf.insert(off, mbuf, 2); - return; - } - buf.setsize(off); - buf.writestring("St"); - source_name(se); - } - else - { - buf.writestring("St"); - source_name(se); - } - } - else - { - buf.writeByte('N'); - if (!dont_write_prefix) - prefix_name(p); - source_name(se); - buf.writeByte('E'); - } - } - else - source_name(se); - store(s); - } - - void mangle_variable(VarDeclaration *d, bool is_temp_arg_ref) - { - - if (!(d->storage_class & (STCextern | STCgshared))) - { - d->error("Internal Compiler Error: C++ static non- __gshared non-extern variables not supported"); - assert(0); - } - - Dsymbol *p = d->toParent(); - if (p && !p->isModule()) //for example: char Namespace1::beta[6] should be mangled as "_ZN10Namespace14betaE" - { - buf.writestring("_ZN"); - prefix_name(p); - source_name(d); - buf.writeByte('E'); - } - else //char beta[6] should mangle as "beta" - { - if (!is_temp_arg_ref) - { - buf.writestring(d->ident->toChars()); - } - else - { - buf.writestring("_Z"); - source_name(d); - } - } - } - - void mangle_function(FuncDeclaration *d) - { - //printf("mangle_function(%s)\n", d->toChars()); - /* - * ::= _Z - * ::= - * ::= - * ::= - */ - TypeFunction *tf = (TypeFunction *)d->type; - - buf.writestring("_Z"); - Dsymbol *p = d->toParent(); - if (p && !p->isModule() && tf->linkage == LINKcpp) - { - buf.writeByte('N'); - if (d->type->isConst()) - buf.writeByte('K'); - prefix_name(p); - - // See ABI 5.1.8 Compression - - // Replace ::std::allocator with Sa - if (buf.offset >= 17 && memcmp(buf.data, "_ZN3std9allocator", 17) == 0) - { - buf.remove(3, 14); - buf.insert(3, (const char *)"Sa", 2); - } - - // Replace ::std::basic_string with Sb - if (buf.offset >= 21 && memcmp(buf.data, "_ZN3std12basic_string", 21) == 0) - { - buf.remove(3, 18); - buf.insert(3, (const char *)"Sb", 2); - } - - // Replace ::std with St - if (buf.offset >= 7 && memcmp(buf.data, "_ZN3std", 7) == 0) - { - buf.remove(3, 4); - buf.insert(3, (const char *)"St", 2); - } - - if (d->isDtorDeclaration()) - { - buf.writestring("D1"); - } - else - { - source_name(d); - } - buf.writeByte('E'); - } - else - { - source_name(d); - } - - if (tf->linkage == LINKcpp) //Template args accept extern "C" symbols with special mangling - { - assert(tf->ty == Tfunction); - argsCppMangle(tf->parameters, tf->varargs); - } - } - - static int paramsCppMangleDg(void *ctx, size_t n, Parameter *fparam) - { - CppMangleVisitor *mangler = (CppMangleVisitor *)ctx; - - Type *t = fparam->type->merge2(); - if (fparam->storageClass & (STCout | STCref)) - t = t->referenceTo(); - else if (fparam->storageClass & STClazy) - { - // Mangle as delegate - Type *td = new TypeFunction(NULL, t, 0, LINKd); - td = new TypeDelegate(td); - t = t->merge(); - } - if (t->ty == Tsarray) - { - // Mangle static arrays as pointers - t->error(Loc(), "Internal Compiler Error: unable to pass static array to extern(C++) function."); - t->error(Loc(), "Use pointer instead."); - assert(0); - //t = t->nextOf()->pointerTo(); - } - - /* If it is a basic, enum or struct type, - * then don't mark it const - */ - mangler->is_top_level = true; - if ((t->ty == Tenum || t->ty == Tstruct || t->ty == Tpointer || t->isTypeBasic()) && t->isConst()) - t->mutableOf()->accept(mangler); - else - t->accept(mangler); - mangler->is_top_level = false; - return 0; - } - - void argsCppMangle(Parameters *parameters, int varargs) - { - if (parameters) - Parameter::foreach(parameters, ¶msCppMangleDg, (void*)this); - - if (varargs) - buf.writestring("z"); - else if (!parameters || !parameters->dim) - buf.writeByte('v'); // encode ( ) parameters - } - -public: - CppMangleVisitor() - : buf(), components(), is_top_level(false), components_on(true) - { - } - - char *mangleOf(Dsymbol *s) - { - VarDeclaration *vd = s->isVarDeclaration(); - FuncDeclaration *fd = s->isFuncDeclaration(); - if (vd) - { - mangle_variable(vd, false); - } - else if (fd) - { - mangle_function(fd); - } - else - { - assert(0); - } - Target::prefixName(&buf, LINKcpp); - return buf.extractString(); - } - - void visit(Type *t) - { - if (t->isImmutable() || t->isShared()) - { - t->error(Loc(), "Internal Compiler Error: shared or immutable types can not be mapped to C++ (%s)", t->toChars()); - } - else - { - t->error(Loc(), "Internal Compiler Error: unsupported type %s\n", t->toChars()); - } - assert(0); //Assert, because this error should be handled in frontend - } - - void visit(TypeBasic *t) - { - /* ABI spec says: - * v void - * w wchar_t - * b bool - * c char - * a signed char - * h unsigned char - * s short - * t unsigned short - * i int - * j unsigned int - * l long - * m unsigned long - * x long long, __int64 - * y unsigned long long, __int64 - * n __int128 - * o unsigned __int128 - * f float - * d double - * e long double, __float80 - * g __float128 - * z ellipsis - * u # vendor extended type - */ - - char c; - char p = 0; - switch (t->ty) - { - case Tvoid: c = 'v'; break; - case Tint8: c = 'a'; break; - case Tuns8: c = 'h'; break; - case Tint16: c = 's'; break; - case Tuns16: c = 't'; break; - case Tint32: c = 'i'; break; - case Tuns32: c = 'j'; break; - case Tfloat32: c = 'f'; break; - case Tint64: c = (Target::c_longsize == 8 ? 'l' : 'x'); break; - case Tuns64: c = (Target::c_longsize == 8 ? 'm' : 'y'); break; - case Tint128: c = 'n'; break; - case Tuns128: c = 'o'; break; - case Tfloat64: c = 'd'; break; - case Tfloat80: c = (Target::realsize - Target::realpad == 16) ? 'g' : 'e'; break; - case Tbool: c = 'b'; break; - case Tchar: c = 'c'; break; - case Twchar: c = 't'; break; // unsigned short - case Tdchar: c = 'w'; break; // wchar_t (UTF-32) - - case Timaginary32: p = 'G'; c = 'f'; break; - case Timaginary64: p = 'G'; c = 'd'; break; - case Timaginary80: p = 'G'; c = 'e'; break; - case Tcomplex32: p = 'C'; c = 'f'; break; - case Tcomplex64: p = 'C'; c = 'd'; break; - case Tcomplex80: p = 'C'; c = 'e'; break; - - default: visit((Type *)t); return; - } - if (t->isImmutable() || t->isShared()) - { - visit((Type *)t); - } - if (p || t->isConst()) - { - if (substitute(t)) - { - return; - } - else - { - store(t); - } - } - - if (t->isConst()) - buf.writeByte('K'); - - if (p) - buf.writeByte(p); - - buf.writeByte(c); - } - - - void visit(TypeVector *t) - { - is_top_level = false; - if (substitute(t)) - return; - store(t); - if (t->isImmutable() || t->isShared()) - { - visit((Type *)t); - } - if (t->isConst()) - buf.writeByte('K'); - assert(t->basetype && t->basetype->ty == Tsarray); - assert(((TypeSArray *)t->basetype)->dim); - //buf.printf("Dv%llu_", ((TypeSArray *)t->basetype)->dim->toInteger());// -- Gnu ABI v.4 - buf.writestring("U8__vector"); //-- Gnu ABI v.3 - t->basetype->nextOf()->accept(this); - } - - void visit(TypeSArray *t) - { - is_top_level = false; - if (!substitute(t)) - store(t); - if (t->isImmutable() || t->isShared()) - { - visit((Type *)t); - } - if (t->isConst()) - buf.writeByte('K'); - buf.printf("A%llu_", t->dim ? t->dim->toInteger() : 0); - t->next->accept(this); - } - - void visit(TypeDArray *t) - { - visit((Type *)t); - } - - void visit(TypeAArray *t) - { - visit((Type *)t); - } - - void visit(TypePointer *t) - { - is_top_level = false; - if (substitute(t)) - return; - if (t->isImmutable() || t->isShared()) - { - visit((Type *)t); - } - if (t->isConst()) - buf.writeByte('K'); - buf.writeByte('P'); - t->next->accept(this); - store(t); - } - - void visit(TypeReference *t) - { - is_top_level = false; - if (substitute(t)) - return; - buf.writeByte('R'); - t->next->accept(this); - store(t); - } - - void visit(TypeFunction *t) - { - is_top_level = false; - /* - * ::= F [Y] E - * ::= + - * # types are possible return type, then parameter types - */ - - /* ABI says: - "The type of a non-static member function is considered to be different, - for the purposes of substitution, from the type of a namespace-scope or - static member function whose type appears similar. The types of two - non-static member functions are considered to be different, for the - purposes of substitution, if the functions are members of different - classes. In other words, for the purposes of substitution, the class of - which the function is a member is considered part of the type of - function." - - BUG: Right now, types of functions are never merged, so our simplistic - component matcher always finds them to be different. - We should use Type::equals on these, and use different - TypeFunctions for non-static member functions, and non-static - member functions of different classes. - */ - if (substitute(t)) - return; - buf.writeByte('F'); - if (t->linkage == LINKc) - buf.writeByte('Y'); - Type *tn = t->next; - if (t->isref) - tn = tn->referenceTo(); - tn->accept(this); - argsCppMangle(t->parameters, t->varargs); - buf.writeByte('E'); - store(t); - } - - void visit(TypeDelegate *t) - { - visit((Type *)t); - } - - void visit(TypeStruct *t) - { - Identifier *id = t->sym->ident; - //printf("struct id = '%s'\n", id->toChars()); - char c; - if (id == Id::__c_long) - c = 'l'; - else if (id == Id::__c_ulong) - c = 'm'; - else - c = 0; - if (c) - { - if (t->isImmutable() || t->isShared()) - { - visit((Type *)t); - } - if (t->isConst()) - { - if (substitute(t)) - { - return; - } - else - { - store(t); - } - } - - if (t->isConst()) - buf.writeByte('K'); - - buf.writeByte(c); - return; - } - - is_top_level = false; - - if (substitute(t)) - return; - if (t->isImmutable() || t->isShared()) - { - visit((Type *)t); - } - if (t->isConst()) - buf.writeByte('K'); - - if (!substitute(t->sym)) - { - cpp_mangle_name(t->sym, t->isConst()); - } - - if (t->isImmutable() || t->isShared()) - { - visit((Type *)t); - } - - if (t->isConst()) - store(t); - } - - void visit(TypeEnum *t) - { - is_top_level = false; - if (substitute(t)) - return; - - if (t->isConst()) - buf.writeByte('K'); - - if (!substitute(t->sym)) - { - cpp_mangle_name(t->sym, t->isConst()); - } - - if (t->isImmutable() || t->isShared()) - { - visit((Type *)t); - } - - if (t->isConst()) - store(t); - } - - void visit(TypeClass *t) - { - if (substitute(t)) - return; - if (t->isImmutable() || t->isShared()) - { - visit((Type *)t); - } - if (t->isConst() && !is_top_level) - buf.writeByte('K'); - is_top_level = false; - buf.writeByte('P'); - - if (t->isConst()) - buf.writeByte('K'); - - if (!substitute(t->sym)) - { - cpp_mangle_name(t->sym, t->isConst()); - } - if (t->isConst()) - store(NULL); - store(t); - } -}; - -char *toCppMangle(Dsymbol *s) -{ - //printf("toCppMangle(%s)\n", s->toChars()); - CppMangleVisitor v; - return v.mangleOf(s); -} - -#elif TARGET_WINDOS - -// Windows DMC and Microsoft Visual C++ mangling -#define VC_SAVED_TYPE_CNT 10 -#define VC_SAVED_IDENT_CNT 10 - -class VisualCPPMangler : public Visitor -{ - const char *saved_idents[VC_SAVED_IDENT_CNT]; - Type *saved_types[VC_SAVED_TYPE_CNT]; - - // IS_NOT_TOP_TYPE: when we mangling one argument, we can call visit several times (for base types of arg type) - // but we must save only arg type: - // For example: if we have an int** argument, we should save "int**" but visit will be called for "int**", "int*", "int" - // This flag is set up by the visit(NextType, ) function and should be reset when the arg type output is finished. - // MANGLE_RETURN_TYPE: return type shouldn't be saved and substituted in arguments - // IGNORE_CONST: in some cases we should ignore CV-modifiers. - - enum Flags - { - IS_NOT_TOP_TYPE = 0x1, - MANGLE_RETURN_TYPE = 0x2, - IGNORE_CONST = 0x4, - IS_DMC = 0x8 - }; - - int flags; - OutBuffer buf; - - VisualCPPMangler(VisualCPPMangler *rvl) - : buf(), - flags(0) - { - flags |= (rvl->flags & IS_DMC); - memcpy(&saved_idents, &rvl->saved_idents, sizeof(const char*) * VC_SAVED_IDENT_CNT); - memcpy(&saved_types, &rvl->saved_types, sizeof(Type*) * VC_SAVED_TYPE_CNT); - } -public: - - VisualCPPMangler(bool isdmc) - : buf(), - flags(0) - { - if (isdmc) - { - flags |= IS_DMC; - } - memset(&saved_idents, 0, sizeof(const char*) * VC_SAVED_IDENT_CNT); - memset(&saved_types, 0, sizeof(Type*) * VC_SAVED_TYPE_CNT); - } - - void visit(Type *type) - { - if (type->isImmutable() || type->isShared()) - { - type->error(Loc(), "Internal Compiler Error: shared or immutable types can not be mapped to C++ (%s)", type->toChars()); - } - else - { - type->error(Loc(), "Internal Compiler Error: unsupported type %s\n", type->toChars()); - } - assert(0); // Assert, because this error should be handled in frontend - } - - void visit(TypeBasic *type) - { - //printf("visit(TypeBasic); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE)); - if (type->isImmutable() || type->isShared()) - { - visit((Type*)type); - return; - } - - if (type->isConst() && ((flags & IS_NOT_TOP_TYPE) || (flags & IS_DMC))) - { - if (checkTypeSaved(type)) - return; - } - - if ((type->ty == Tbool) && checkTypeSaved(type))// try to replace long name with number - { - return; - } - mangleModifier(type); - switch (type->ty) - { - case Tvoid: buf.writeByte('X'); break; - case Tint8: buf.writeByte('C'); break; - case Tuns8: buf.writeByte('E'); break; - case Tint16: buf.writeByte('F'); break; - case Tuns16: buf.writeByte('G'); break; - case Tint32: buf.writeByte('H'); break; - case Tuns32: buf.writeByte('I'); break; - case Tfloat32: buf.writeByte('M'); break; - case Tint64: buf.writestring("_J"); break; - case Tuns64: buf.writestring("_K"); break; - case Tint128: buf.writestring("_L"); break; - case Tuns128: buf.writestring("_M"); break; - case Tfloat64: buf.writeByte('N'); break; - case Tbool: buf.writestring("_N"); break; - case Tchar: buf.writeByte('D'); break; - case Tdchar: buf.writeByte('I'); break; // unsigned int - - case Tfloat80: - if (flags & IS_DMC) - buf.writestring("_Z"); // DigitalMars long double - else - buf.writestring("_T"); // Intel long double - break; - - case Twchar: - if (flags & IS_DMC) - buf.writestring("_Y"); // DigitalMars wchar_t - else - buf.writestring("_W"); // Visual C++ wchar_t - break; - - default: visit((Type*)type); return; - } - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; - } - - void visit(TypeVector *type) - { - //printf("visit(TypeVector); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE)); - if (checkTypeSaved(type)) - return; - buf.writestring("T__m128@@"); // may be better as __m128i or __m128d? - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; - } - - void visit(TypeSArray *type) - { - // This method can be called only for static variable type mangling. - //printf("visit(TypeSArray); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE)); - if (checkTypeSaved(type)) - return; - // first dimension always mangled as const pointer - if (flags & IS_DMC) - buf.writeByte('Q'); - else - buf.writeByte('P'); - - flags |= IS_NOT_TOP_TYPE; - assert(type->next); - if (type->next->ty == Tsarray) - { - mangleArray((TypeSArray*)type->next); - } - else - { - type->next->accept(this); - } - } - - // attention: D int[1][2]* arr mapped to C++ int arr[][2][1]; (because it's more typical situation) - // There is not way to map int C++ (*arr)[2][1] to D - void visit(TypePointer *type) - { - //printf("visit(TypePointer); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE)); - if (type->isImmutable() || type->isShared()) - { - visit((Type*)type); - return; - } - - assert(type->next); - if (type->next->ty == Tfunction) - { - const char *arg = mangleFunctionType((TypeFunction*)type->next); // compute args before checking to save; args should be saved before function type - - // If we've mangled this function early, previous call is meaningless. - // However we should do it before checking to save types of function arguments before function type saving. - // If this function was already mangled, types of all it arguments are save too, thus previous can't save - // anything if function is saved. - if (checkTypeSaved(type)) - return; - - if (type->isConst()) - buf.writeByte('Q'); // const - else - buf.writeByte('P'); // mutable - - buf.writeByte('6'); // pointer to a function - buf.writestring(arg); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; - return; - } - else if (type->next->ty == Tsarray) - { - if (checkTypeSaved(type)) - return; - mangleModifier(type); - - if (type->isConst() || !(flags & IS_DMC)) - buf.writeByte('Q'); // const - else - buf.writeByte('P'); // mutable - - if (global.params.is64bit) - buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; - - mangleArray((TypeSArray*)type->next); - return; - } - else - { - if (checkTypeSaved(type)) - return; - mangleModifier(type); - - if (type->isConst()) - { - buf.writeByte('Q'); // const - } - else - { - buf.writeByte('P'); // mutable - } - - if (global.params.is64bit) - buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; - type->next->accept(this); - } - } - - void visit(TypeReference *type) - { - //printf("visit(TypeReference); type = %s\n", type->toChars()); - if (checkTypeSaved(type)) - return; - - if (type->isImmutable() || type->isShared()) - { - visit((Type*)type); - return; - } - - buf.writeByte('A'); // mutable - - if (global.params.is64bit) - buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; - assert(type->next); - if (type->next->ty == Tsarray) - { - mangleArray((TypeSArray*)type->next); - } - else - { - type->next->accept(this); - } - } - - void visit(TypeFunction *type) - { - const char *arg = mangleFunctionType(type); - - if ((flags & IS_DMC)) - { - if (checkTypeSaved(type)) - return; - } - else - { - buf.writestring("$$A6"); - } - buf.writestring(arg); - flags &= ~(IS_NOT_TOP_TYPE | IGNORE_CONST); - } - - void visit(TypeStruct *type) - { - Identifier *id = type->sym->ident; - char c; - if (id == Id::__c_long_double) - c = 'O'; // VC++ long double - else if (id == Id::__c_long) - c = 'J'; // VC++ long - else if (id == Id::__c_ulong) - c = 'K'; // VC++ unsigned long - else - c = 0; - - if (c) - { - if (type->isImmutable() || type->isShared()) - { - visit((Type*)type); - return; - } - - if (type->isConst() && ((flags & IS_NOT_TOP_TYPE) || (flags & IS_DMC))) - { - if (checkTypeSaved(type)) - return; - } - - mangleModifier(type); - buf.writeByte(c); - } - else - { - if (checkTypeSaved(type)) - return; - //printf("visit(TypeStruct); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE)); - mangleModifier(type); - if (type->sym->isUnionDeclaration()) - buf.writeByte('T'); - else - buf.writeByte('U'); - mangleIdent(type->sym); - } - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; - } - - void visit(TypeEnum *type) - { - //printf("visit(TypeEnum); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE)); - if (checkTypeSaved(type)) - return; - mangleModifier(type); - buf.writeByte('W'); - - switch (type->sym->memtype->ty) - { - case Tchar: - case Tint8: - buf.writeByte('0'); - break; - case Tuns8: - buf.writeByte('1'); - break; - case Tint16: - buf.writeByte('2'); - break; - case Tuns16: - buf.writeByte('3'); - break; - case Tint32: - buf.writeByte('4'); - break; - case Tuns32: - buf.writeByte('5'); - break; - case Tint64: - buf.writeByte('6'); - break; - case Tuns64: - buf.writeByte('7'); - break; - default: - visit((Type*)type); - break; - } - - mangleIdent(type->sym); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; - } - - // D class mangled as pointer to C++ class - // const(Object) mangled as Object const* const - void visit(TypeClass *type) - { - //printf("visit(TypeClass); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE)); - if (checkTypeSaved(type)) - return; - if (flags & IS_NOT_TOP_TYPE) - mangleModifier(type); - - if (type->isConst()) - buf.writeByte('Q'); - else - buf.writeByte('P'); - - if (global.params.is64bit) - buf.writeByte('E'); - - flags |= IS_NOT_TOP_TYPE; - mangleModifier(type); - - buf.writeByte('V'); - - mangleIdent(type->sym); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; - } - - char *mangleOf(Dsymbol *s) - { - VarDeclaration *vd = s->isVarDeclaration(); - FuncDeclaration *fd = s->isFuncDeclaration(); - if (vd) - { - mangleVariable(vd); - } - else if (fd) - { - mangleFunction(fd); - } - else - { - assert(0); - } - return buf.extractString(); - } -private: - - void mangleFunction(FuncDeclaration *d) - { - // ? - assert(d); - buf.writeByte('?'); - mangleIdent(d); - - if (d->needThis()) // ::= - { - // Pivate methods always non-virtual in D and it should be mangled as non-virtual in C++ - if (d->isVirtual() && d->vtblIndex != -1) - { - switch (d->protection.kind) - { - case PROTprivate: - buf.writeByte('E'); - break; - case PROTprotected: - buf.writeByte('M'); - break; - default: - buf.writeByte('U'); - break; - } - } - else - { - switch (d->protection.kind) - { - case PROTprivate: - buf.writeByte('A'); - break; - case PROTprotected: - buf.writeByte('I'); - break; - default: - buf.writeByte('Q'); - break; - } - } - if (global.params.is64bit) - buf.writeByte('E'); - if (d->type->isConst()) - { - buf.writeByte('B'); - } - else - { - buf.writeByte('A'); - } - } - else if (d->isMember2()) // static function - { // ::= - switch (d->protection.kind) - { - case PROTprivate: - buf.writeByte('C'); - break; - case PROTprotected: - buf.writeByte('K'); - break; - default: - buf.writeByte('S'); - break; - } - } - else // top-level function - { // ::= Y - buf.writeByte('Y'); - } - - const char *args = mangleFunctionType((TypeFunction *)d->type, (bool)d->needThis(), d->isCtorDeclaration() || d->isDtorDeclaration()); - buf.writestring(args); - } - - void mangleVariable(VarDeclaration *d) - { - // ::= ? - assert(d); - if (!(d->storage_class & (STCextern | STCgshared))) - { - d->error("Internal Compiler Error: C++ static non- __gshared non-extern variables not supported"); - assert(0); - } - buf.writeByte('?'); - mangleIdent(d); - - assert(!d->needThis()); - - if (d->parent && d->parent->isModule()) // static member - { - buf.writeByte('3'); - } - else - { - switch (d->protection.kind) - { - case PROTprivate: - buf.writeByte('0'); - break; - case PROTprotected: - buf.writeByte('1'); - break; - default: - buf.writeByte('2'); - break; - } - } - - char cv_mod = 0; - Type *t = d->type; - - if (t->isImmutable() || t->isShared()) - { - visit((Type*)t); - return; - } - if (t->isConst()) - { - cv_mod = 'B'; // const - } - else - { - cv_mod = 'A'; // mutable - } - - if (t->ty != Tpointer) - t = t->mutableOf(); - - t->accept(this); - - if ((t->ty == Tpointer || t->ty == Treference || t->ty == Tclass) && global.params.is64bit) - { - buf.writeByte('E'); - } - - buf.writeByte(cv_mod); - } - - void mangleName(Dsymbol *sym, bool dont_use_back_reference = false) - { - //printf("mangleName('%s')\n", sym->toChars()); - const char *name = NULL; - bool is_dmc_template = false; - if (sym->isDtorDeclaration()) - { - buf.writestring("?1"); - return; - } - if (TemplateInstance *ti = sym->isTemplateInstance()) - { - VisualCPPMangler tmp((flags & IS_DMC) ? true : false); - tmp.buf.writeByte('?'); - tmp.buf.writeByte('$'); - tmp.buf.writestring(ti->name->toChars()); - tmp.saved_idents[0] = ti->name->toChars(); - tmp.buf.writeByte('@'); - if (flags & IS_DMC) - { - tmp.mangleIdent(sym->parent, true); - is_dmc_template = true; - } - - bool is_var_arg = false; - for (size_t i = 0; i < ti->tiargs->dim; i++) - { - RootObject *o = (*ti->tiargs)[i]; - - TemplateParameter *tp = NULL; - TemplateValueParameter *tv = NULL; - TemplateTupleParameter *tt = NULL; - if (!is_var_arg) - { - TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration(); - assert(td); - tp = (*td->parameters)[i]; - tv = tp->isTemplateValueParameter(); - tt = tp->isTemplateTupleParameter(); - } - - if (tt) - { - is_var_arg = true; - tp = NULL; - } - if (tv) - { - if (tv->valType->isintegral()) - { - - tmp.buf.writeByte('$'); - tmp.buf.writeByte('0'); - - Expression *e = isExpression(o); - assert(e); - - if (tv->valType->isunsigned()) - { - tmp.mangleNumber(e->toUInteger()); - } - else if(is_dmc_template) - { - // NOTE: DMC mangles everything based on - // unsigned int - tmp.mangleNumber(e->toInteger()); - } - else - { - sinteger_t val = e->toInteger(); - if (val < 0) - { - val = -val; - tmp.buf.writeByte('?'); - } - tmp.mangleNumber(val); - } - } - else - { - sym->error("Internal Compiler Error: C++ %s template value parameter is not supported", tv->valType->toChars()); - assert(0); - } - } - else if (!tp || tp->isTemplateTypeParameter()) - { - Type *t = isType(o); - assert(t); - t->accept(&tmp); - } - else if (tp->isTemplateAliasParameter()) - { - Dsymbol *d = isDsymbol(o); - Expression *e = isExpression(o); - if (!d && !e) - { - sym->error("Internal Compiler Error: %s is unsupported parameter for C++ template", o->toChars()); - assert(0); - } - if (d && d->isFuncDeclaration()) - { - tmp.buf.writeByte('$'); - tmp.buf.writeByte('1'); - tmp.mangleFunction(d->isFuncDeclaration()); - } - else if (e && e->op == TOKvar && ((VarExp*)e)->var->isVarDeclaration()) - { - tmp.buf.writeByte('$'); - if (flags & IS_DMC) - tmp.buf.writeByte('1'); - else - tmp.buf.writeByte('E'); - tmp.mangleVariable(((VarExp*)e)->var->isVarDeclaration()); - } - else if (d && d->isTemplateDeclaration() && d->isTemplateDeclaration()->onemember) - { - - Dsymbol *ds = d->isTemplateDeclaration()->onemember; - if (flags & IS_DMC) - { - tmp.buf.writeByte('V'); - } - else - { - if (ds->isUnionDeclaration()) - { - tmp.buf.writeByte('T'); - } - else if (ds->isStructDeclaration()) - { - tmp.buf.writeByte('U'); - } - else if (ds->isClassDeclaration()) - { - tmp.buf.writeByte('V'); - } - else - { - sym->error("Internal Compiler Error: C++ templates support only integral value, type parameters, alias templates and alias function parameters"); - assert(0); - } - } - tmp.mangleIdent(d); - } - else - { - sym->error("Internal Compiler Error: %s is unsupported parameter for C++ template: (%s)", o->toChars()); - assert(0); - } - - } - else - { - sym->error("Internal Compiler Error: C++ templates support only integral value, type parameters, alias templates and alias function parameters"); - assert(0); - } - } - name = tmp.buf.extractString(); - } - else - { - name = sym->ident->toChars(); - } - assert(name); - if (!is_dmc_template) - { - if (dont_use_back_reference) - { - saveIdent(name); - } - else - { - if (checkAndSaveIdent(name)) - return; - } - } - buf.writestring(name); - buf.writeByte('@'); - } - - // returns true if name already saved - bool checkAndSaveIdent(const char *name) - { - for (size_t i = 0; i < VC_SAVED_IDENT_CNT; i++) - { - if (!saved_idents[i]) // no saved same name - { - saved_idents[i] = name; - break; - } - - if (!strcmp(saved_idents[i], name)) // ok, we've found same name. use index instead of name - { - buf.writeByte(i + '0'); - return true; - } - } - return false; - } - - void saveIdent(const char *name) - { - for (size_t i = 0; i < VC_SAVED_IDENT_CNT; i++) - { - if (!saved_idents[i]) // no saved same name - { - saved_idents[i] = name; - break; - } - - if (!strcmp(saved_idents[i], name)) // ok, we've found same name. use index instead of name - { - return; - } - } - } - - void mangleIdent(Dsymbol *sym, bool dont_use_back_reference = false) - { - // ::= @ - // ::= - // ::= - - // ::= @ - // ::= ?$ @