Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alternative way for issue 7177 - Allow UFCS lookup for opDollar resolution #1793

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
118 changes: 90 additions & 28 deletions src/dsymbol.d
Expand Up @@ -1781,32 +1781,88 @@ public:
assert(exp.op == TOKarray || exp.op == TOKslice);
AggregateDeclaration ad = isAggregate(t);
assert(ad);
Dsymbol s = ad.search(loc, Id.opDollar);
if (!s) // no dollar exists -- search in higher scope
return null;
s = s.toAlias();
Expression e = null;
// Check for multi-dimensional opDollar(dim) template.
if (TemplateDeclaration td = s.isTemplateDeclaration())

Dsymbol sx = ad.search(loc, Id.opDollar);
Dsymbol s;
if (!sx)
{
dinteger_t dim = 0;
if (exp.op == TOKarray)
{
dim = (cast(ArrayExp)exp).currentDimension;
}
else if (exp.op == TOKslice)
// See UFCS opDollar (same as searchUFCS)
s = null;
for (Scope* scx = sc; scx; scx = scx.enclosing)
{
dim = 0; // slices are currently always one-dimensional
if (!scx.scopesym)
continue;
s = scx.scopesym.search(loc, Id.opDollar);
if (s)
{
// overload set contains only module scope symbols.
if (s.isOverloadSet())
break;
// selective/renamed imports also be picked up
if (s.isAliasDeclaration() && (cast(AliasDeclaration)s)._import)
break;
// See only module scope symbols for UFCS target.
Dsymbol p = s.toParent2();
if (p && p.isModule())
break;
}
s = null;
}
else
if (!s) // no opDollar exists
return null;
s = s.toAlias();
}
else
s = sx.toAlias();

dinteger_t curdim, maxdim;
if (exp.op == TOKarray)
{
ArrayExp ae = cast(ArrayExp)exp;
curdim = ae.currentDimension;
maxdim = ae.arguments.dim;
}
else //if (exp.op == TOKslice)
{
curdim = 0; // slices are currently always one-dimensional
maxdim = 1;
}

Expression e = null;
TemplateDeclaration td;
if (auto fd = s.isFuncDeclaration())
td = fd.findTemplateDeclRoot();
else
td = s.isTemplateDeclaration();

bool isMultiDimOpDollar(TemplateDeclaration td)
{
for (; td; td = td.overnext)
{
assert(0);
if (td.parameters.dim &&
(*td.parameters)[0].isTemplateValueParameter())
{
return true;
}
}
return false;
}

// Prefer multi-dimensional opDollar(size_t dim) template.
if (td && isMultiDimOpDollar(td))
{
auto tiargs = new Objects();
Expression edim = new IntegerExp(Loc(), dim, Type.tsize_t);
Expression edim = new IntegerExp(Loc(), curdim, Type.tsize_t);
edim = edim.semantic(sc);
tiargs.push(edim);
e = new DotTemplateInstanceExp(loc, ce, td.ident, tiargs);
if (!sx) // UFCS version
{
e = new ScopeExp(loc, new TemplateInstance(loc, td, tiargs));
e = new CallExp(loc, e, ce);
//printf("1 e = %s\n", e.toChars());
}
else
e = new DotTemplateInstanceExp(loc, ce, td.ident, tiargs);
}
else
{
Expand All @@ -1815,21 +1871,27 @@ public:
* Note that it's impossible to have both template & function opDollar,
* because both take no arguments.
*/
if (exp.op == TOKarray && (cast(ArrayExp)exp).arguments.dim != 1)
if (maxdim != 1)
{
exp.error("%s only defines opDollar for one dimension", ad.toChars());
return null;
if (!sx)
exp.error("multi-dimensional opDollar for %s is not found", ad.toChars());
else
exp.error("%s only defines opDollar for one dimension", ad.toChars());
}
Declaration d = s.isDeclaration();
assert(d);
e = new DotVarExp(loc, ce, d);
if (!sx) // UFCS version
{
e = new DsymbolExp(loc, s);
e = new CallExp(loc, e, ce);
//printf("2 e = %s\n", e.toChars());
}
else
e = new DotIdExp(loc, ce, s.ident);
}
e = e.semantic(sc);
if (!e.type)
exp.error("%s has no value", e.toChars());
t = e.type.toBasetype();
if (t && t.ty == Tfunction)
if (e.type && e.type.toBasetype().ty == Tfunction)
e = new CallExp(e.loc, e);
if (e.checkValue())
return null;
v = new VarDeclaration(loc, null, Id.dollar, new ExpInitializer(Loc(), e));
v.storage_class |= STCtemp | STCctfe | STCrvalue;
}
Expand Down
46 changes: 46 additions & 0 deletions test/fail_compilation/fail7177.d
@@ -0,0 +1,46 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail7177.d(21): Error: X1 only defines opDollar for one dimension
fail_compilation/fail7177.d(21): Error: X1 only defines opDollar for one dimension
fail_compilation/fail7177.d(28): Error: function fail7177.main.X2.opDollar is not callable because it is annotated with @disable
fail_compilation/fail7177.d(35): Error: multi-dimensional opDollar for X3 is not found
fail_compilation/fail7177.d(35): Error: multi-dimensional opDollar for X3 is not found
fail_compilation/fail7177.d(42): Error: multi-dimensional opDollar for X4 is not found
fail_compilation/fail7177.d(42): Error: multi-dimensional opDollar for X4 is not found
---
*/

void main()
{
struct X1
{
@property size_t opDollar() const { return 1; }
void opIndex(size_t i, size_t j) {}
}
X1.init[$, $];

struct X2
{
@disable size_t opDollar();
int opIndex(size_t i) { return 1; }
}
X2.init[$ - 1];

struct X3
{
@property size_t length() const { return 1; }
void opIndex(size_t i, size_t j) {}
}
X3.init[$, $];

struct X4
{
@property size_t length()() const { return 1; }
void opIndex(size_t i, size_t j) {}
}
X4.init[$, $];

}

size_t opDollar(R)(R r) { return r.length; }
3 changes: 3 additions & 0 deletions test/runnable/imports/opover7177a.d
@@ -0,0 +1,3 @@
module imports.opover7177a;

auto opDollar(R)(R r) { return r.length; }
3 changes: 3 additions & 0 deletions test/runnable/imports/opover7177b.d
@@ -0,0 +1,3 @@
module imports.opover7177b;

auto opDollar(size_t dim, R)(R r) { return r.length; }
69 changes: 69 additions & 0 deletions test/runnable/opover2.d
Expand Up @@ -1336,6 +1336,74 @@ void test19()
assert(foo[0 .. $] == [0, 0]);
}

/**************************************/
// 7177

void test7177()
{
struct A
{
alias E = int;
E[] arr;

@property size_t length() const { return arr.length; }

E opIndex(size_t i) { return arr[i]; }
A opSlice(size_t b, size_t e) { return A(arr[b .. e]); }
}
struct X1
{
@property size_t length()() const { return 1; }
@property void length(T)(T) {}
void opIndex(size_t i) {}
void opIndex(size_t i, size_t j) {}
void opSlice(size_t b, size_t e) {}
}
struct X2
{
// Even if 'length' found, e.length!(dim) will never be invoked.
@property size_t length(size_t dim)() const { return 2; }
void opIndex(size_t i) {}
void opIndex(size_t i, size_t j) {}
void opSlice(size_t b, size_t e) {}
}

{
import imports.opover7177a; // opDollar(R)(R r)

auto a = A([1,2,3]);
assert(a[0] == 1);
assert(a[$-1] == 3);
assert(a[2..$].length == 1);

X1 x1;
x1.length = 1;
static assert( __traits(compiles, x1[$]));
static assert(!__traits(compiles, x1[$, $]));

X2 x2;
static assert(!__traits(compiles, x2[$]));
static assert(!__traits(compiles, x2[$, $]));
}
{
import imports.opover7177b; // opDollar(size_t dim, R)(R r)

auto a = A([1,2,3]);
assert(a[0] == 1);
assert(a[$-1] == 3);
assert(a[2..$].length == 1);

X1 x1;
x1.length = 1;
static assert( __traits(compiles, x1[$]));
static assert( __traits(compiles, x1[$, $])); // opDollar!0, opDollar!1

X2 x2;
static assert(!__traits(compiles, x2[$]));
static assert(!__traits(compiles, x2[$, $]));
}
}

/**************************************/
// 9453

Expand Down Expand Up @@ -2039,6 +2107,7 @@ int main()
test8434();
test18();
test19();
test7177();
test9453();
test9496();
test9689();
Expand Down