Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Issue 6798 - Integrate overloadings for multidimensional indexing and slicing #443

Merged
merged 10 commits into from about 1 month ago
Hara Kenji
Collaborator

http://d.puremagic.com/issues/show_bug.cgi?id=6798

This patch is additional enhancement of opDollar (issue 3474 and #442).
Enable the mixing operator overloadings of indexing and slicing

a[$-1, 2..$] is translated to a.opIndex(a.opDollar!0 - 1, a.opSlice!1(2, a.opDollar!1))

The slicing lwr..upr inside bracket is converted to a.opSlice!(dimension)(lwr, upr).
This enhancement never break existing codes.

expression newly added overloading exists overloading
a[i0, ...] a.opIndex(i0, ...)
a[] a.opIndex() a.opSlice()
a[l..u] a.opIndex(a.opSlice!0(l, u)) a.opSlice(l, u)
a[l..u, ...] a.opIndex(a.opSlice!0(l, u), ...)
op a[i0, ...] a.opIndexUnary!op(i0, ...)
op a[] a.opIndexUnary!op() a.opSliceUnary!op()
op a[l..u] a.opIndexUnary!op(a.opSlice!0(l, u)) a.opSliceUnary!op(l, u)
op a[l..u, ...] a.opIndexUnary!op(a.opSlice!0(l, u), ...)
a[i0, ...] = v a.opIndexAssign(v, i0, ...)
a[] = v a.opIndexAssign(v) a.opSliceAssign(v)
a[l..u] = v a.opIndexAssign(v, a.opSlice!0(l, u)) a.opSliceAssign(v, l, u)
a[l..u, ...] = v a.opIndexAssign(v, a.opSlice!0(l, u), ...)
a[i0, ...] op= v a.opIndexOpAssign!op(v, i0, ...)
a[] op= v a.opIndexOpAssign!op(v) a.opSliceOpAssign!op(v)
a[l..u] op= v a.opIndexOpAssign!op(v, a.opSlice!0(l, u)) a.opSliceOpAssign!op(v, l, u)
a[l..u, ...] op= v a.opIndexOpAssign!op(v, a.opSlice!0(l, u), ...)
Brad Roberts
Owner

This patch is failing on all 64 bit platforms:

http://d.puremagic.com/test-results/pull.ghtml?runid=12403
../druntime/import/core/stdc/stdarg.di(250): Error: cannot implicitly convert expression (parmn[0LU..tsize]) of type void[] to ulong
../druntime/import/core/stdc/stdarg.di(250): Error: cannot implicitly convert expression (p[0LU..tsize]) of type void[] to ulong
../druntime/import/core/stdc/stdarg.di(250): Error: expression p[(__error)] is void and has no value
../druntime/import/core/stdc/stdarg.di(283): Error: cannot implicitly convert expression (parmn[0LU..tsize]) of type void[] to ulong
../druntime/import/core/stdc/stdarg.di(283): Error: cannot implicitly convert expression (p[0LU..tsize]) of type void[] to ulong
../druntime/import/core/stdc/stdarg.di(283): Error: expression p[(__error)] is void and has no value
../druntime/import/core/stdc/stdarg.di(292): Error: cannot implicitly convert expression (parmn[0LU..tsize]) of type void[] to ulong
../druntime/import/core/stdc/stdarg.di(292): Error: cannot implicitly convert expression (p[0LU..tsize]) of type void[] to ulong
../druntime/import/core/stdc/stdarg.di(292): Error: expression p[(__error)] is void and has no value
std/format.d(3973): Error: template instance core.stdc.stdarg.va_arg!() error instantiating

Hara Kenji
Collaborator
9rnsr commented May 31, 2012

Revamped changes.

Andrei Alexandrescu
Owner

(background: I'm doing a review of pull requests < 1000 as they tend to be controversial)

This is nice, self-consistent, and tastefully designed.

The question is scope and intended usage. This is a relatively hefty language addition, with a very narrow clientele. I'm not sure whether those clients actually need this stuff, and if they do, whether the current form is satisfactory to them. (For example one issue is that the slice limits are computed outside the index expression, so presumably they'd make certain usages difficult.) One other issue discussed in the enhancement request is that there might be a need for strides etc.

So I'm torn about this. One one hand it's a work of beauty. On the other it seems of low impact and unclear user base. Thoughts?

Brad Roberts braddr referenced this pull request from a commit October 21, 2012
Commit has since been removed from the repository and is no longer available.
H. S. Teoh

I vote for merging this.

Or at the very least, some subset of this that supports multidimensional opDollar with slicing. Some background: I'm working on a multidimensional array implementation that uses the variadic form of opIndex to dereference array elements, and it works wonderfully (opDollar works as well). However, slicing syntax currently is not supported, so I've implemented a workaround using opSlice with two array arguments, so one could write A[[0,0,0]..[2,2,2]] for example, but the problem is that opDollar doesn't work correctly ($ always gets mapped to opDollar!0, so A[[0,0,0]..[$,$,$]] does not do the right thing when the array has different lengths along each dimension. I've tried various alternative ways to provide convenient slicing syntax, but none of them can support opDollar correctly.

It would be very nice if slicing can also support variadic arguments, since currently it's a hole in the language -- opIndex supports multidimensional arrays but opSlice doesn't.

Walter Bright
Owner

I'd like to include with this some code that is commented out for the moment that issues a warning and corrective action when opSlice, opSliceUnary, opSliceAssign, and opSliceOpAssign as those will now be obsolete.

Andrei Alexandrescu
Owner

Ping on this. Now that time has passed we should be a tad wiser so maybe we have new insights in the matter. Thoughts?

Maksim Zholudev

Slices and strides make code more expressive and can save hours of debugging especially if one deals with matrices.

The design suggested is really good. It doesn't break existing code. It reduces the number of methods one has to implement (e.g. opSliceAssign is not needed anymore). Strides can be added later and will also not break anything.

I just can not understand why it is still not merged.

Hara Kenji
Collaborator
9rnsr commented May 14, 2013

Sorry I'm failing to rebase this change to git master. Currently this change will break test/runnable/aliasthis.d...

Don Clugston

This needs a rebase (perhaps a rebase -i). There are dozens of unrelated commits in here.

Otherwise, I think this is awesome. This finishes the job started with opDollar, and it's something we've needed to do eventually.

What happens if you do:
void foo( int {] x) { x[2..$, 4] = 7; }
after these changes to the parser? Do you still get a nice error message?

Hara Kenji
Collaborator
9rnsr commented May 14, 2013

And, now I have a small performance worry. To represent slice, it would need to pack the start and end of the slice.

struct S {
    auto opSlice(int x, int y) { return tuple(x, y); }
    void opIndex(int, Tuple!(int, int) slice) {}
}
S s;
s[1, 2..3];   // 2..3 will always need creating tuple object

Is this acceptable?

Hara Kenji
Collaborator
9rnsr commented May 19, 2013

OK. I finished rebasing.

fuentes

I vote also for merging this! multidimensional slicing is very interesting for people doing numerical computations!

Hara Kenji
Collaborator
9rnsr commented March 12, 2014

Finished rebasing.

Andrei Alexandrescu
Owner

Is my understanding that this is a large breakage of existing uses of opSlice?

src/expression.c
((10 lines not shown))
13477 13560
 
13478 13561
     for (size_t i = 0; i < ae->arguments->dim; i++)
13479 13562
     {
  13563
+        if (i == 0)
  13564
+            e0 = extractOpDollarSideEffect(sc, ae);
  13565
+
  13566
+        Expression *e = (*ae->arguments)[i];
  13567
+        if (e->op == TOKinterval && !(slice && slice->isTemplateDeclaration()))
  13568
+        {
  13569
+        Lfallback:
  13570
+            if (ae->arguments->dim == 1)
  13571
+            {
  13572
+                ae->e1 = Expression::combine(e0, ae->e1);
  13573
+                return NULL;
  13574
+            }
  13575
+            else
  13576
+            {
  13577
+                ae->error("multi-dimentional slicing requires template opSlice");
2
Andrei Alexandrescu Owner
andralex added a note March 15, 2014

s/dimentional/dimensional/

Hara Kenji Collaborator
9rnsr added a note March 15, 2014

Thanks, fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Andrei Alexandrescu
Owner

For the record Walter and I are on board with this language addition as long as there's no breakage of existing code. Thanks!

Vladimir Panteleev

The pull description states:

This enhancement never break existing codes.

Looking at the tests, it does seem to break alias-this'd tuples. Kenji confirmed it in a comment:

Currently this change will break test/runnable/aliasthis.d...

Andrei Alexandrescu
Owner

@9rnsr can you work some magic to keep alias this working?

added some commits March 12, 2014
[Refactoring] ArrayScopeSymbol::search b12585b
[Refactoring] Operator overload handling in AssignExp
Change to recursive call for later fallback mechanism.
74f5e0e
[Refactoring] More better error propagation from resolveOpDollar 05e72b2
Add IntervalExp and fix parser, and disabled test for issue 6789
All of N-dimensional array operations are now translated to ArrayExp.
But currently they are immediately translated to SliceExp, so have no effect.
ca6643e
Translate IntervalExp in ArrayExp::arguments to opSlice!dim(lwr, upr) 6124e8d
Support multi-dimensional opIndex
If it fails, fall back to opSlice for backward compatibility.
1f308ef
Support multi-dimensional opIndexAssign
If it fails, fall back to opSliceAssign for backward compatibility.
1dd0746
Support multi-dimensional opIndexUnary
If it fails, fall back to opSliceUnary for backward compatibility.
d947c75
Support multi-dimensional opIndexOpAssign
If it fails, fall back to opSliceOpAssign for backward compatibility.
b1f0106
Unmask all test for issue 6798 e46e7e5
Hara Kenji
Collaborator
9rnsr commented March 15, 2014

Is my understanding that this is a large breakage of existing uses of opSlice?

No. New opSlice!dim(lwe, upr) will be preferred, but if it does not exist, old signature opSlice(lwr, upr) is used.

Currently this change will break test/runnable/aliasthis.d

It's an exception. Issue 8735 & 9709 was fixed in 2.065, but in the test case, A[0] should not invoke alias this, because A is the type Tuple9709!(1,int,"foo"), so A[0] should be analyzed as the zero-length static array type. I think that is a bug, so the fix is legitimate.

Andrei Alexandrescu
Owner

OK, great to know! What's with the red though?

Andrei Alexandrescu
Owner

Auto-merge toggled on

Hara Kenji
Collaborator
9rnsr commented March 15, 2014

What's with the red though?

It seems to be caused by the merge of #2256. This and that are both big, so something would be changed. I'll fix it.

Andrei Alexandrescu
Owner

thanks!

Brad Roberts
Owner

Pull updated, auto_merge toggled off

Hara Kenji
Collaborator
9rnsr commented March 15, 2014

Hmm, the red seems to have been fake. Could you re-toggle auto merge?

Andrei Alexandrescu
Owner

Auto-merge toggled on

Andrei Alexandrescu andralex merged commit 7148ab2 into from March 15, 2014
Andrei Alexandrescu andralex closed this March 15, 2014
Hara Kenji
Collaborator
9rnsr commented March 15, 2014

Thanks!

Daniel Murphy yebblies commented on the diff March 17, 2014
src/expression.h
@@ -1069,6 +1069,18 @@ class ArrayLengthExp : public UnaExp
1069 1069
     void accept(Visitor *v) { v->visit(this); }
1070 1070
 };
1071 1071
 
  1072
+struct IntervalExp : Expression
1
Daniel Murphy Collaborator
yebblies added a note March 17, 2014

This should have been a class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Orvid King
Orvid commented March 17, 2014

Akk...... I see a serious design flaw with the multi-dimensional slicing, namely that it's impossible to determine whether the slice is the first or second argument from within the opSlice call, because it would change what the slice actually represents in things such as multi-dimensional arrays. I'm going to put this here for now just because there is an active topic on the mailing list over this, I'll update this momentarily to illustrate the issue better with examples.

So, it appears I have mis-understood what this PR actually does, and instead have the simple question of why? Why is this useful? Why would you want a slice operation to potentially call an opIndex, which is intended for selecting a single element? All I see in this PR is the actual addition of the ability to do multi-dimensional slicing, but even that is using an opIndex, when it should be introducing it as an opSlice. This also will cause issues due to the fact an operator can't currently be overloaded based on return type. There are situations, such as with matrices, that would mean that this syntax would require a dynamic array allocation for each index in order to both allow a user to both do that single dimensional slice and use it, as well as to still allow a multi-dimensional slice to be done.

This isn't the first addition to the D language that's been merged that really shouldn't have been, not without a more public review process. Perhaps requiring a formal review, such as what is required for the addition of Phobos modules, when adding or removing features or behaviors from the D language itself?

H. S. Teoh

Huh? This pull has been referenced multiple times over the course of (at least) the past year in the forum. Is that not public enough? Or are we supposed to copy-n-paste the comments / code from the pull to the forum in order to make it more public? I'm confused.

H. S. Teoh

Regarding your question about opIndex, the idea here is that opSlice!n(a,b) will return some kind of type that represents a slice from a to b in the n'th position, and opIndex will be overloaded to take arguments of this type and implement the actual slicing this way. The idea is that you should be able to call opSlice to get a type representing an index range, and then you can use that range object in multiple subsequent calls to opIndex. This is more flexible than limiting opSlice to do the actual slicing, then you have to keep track of the endpoints of the range manually each time.

Orvid King
Orvid commented March 17, 2014

The thing is that the way it's been being done is subjective to the actual reader of the forum topics, by using the same process that phobos modules go through, it produces a non-subjective result, in the form of votes.

Orvid King
Orvid commented March 17, 2014

But that still doesn't address the fact that your using opIndex, which is intended to retrieve a single element, to retrieve a slice instead. The same overload that is being called for opIndex could be done as an overload for opSlice instead, and would make much more sense to the reader of the code.

H. S. Teoh

I don't know why you think that opIndex is only intended to retrieve a single element. In my own multidimensional array implementation, I've done opIndex(Range a, Range b, ...) because it's easier to consolidate mixed slices that way, e.g., opIndex(int a, Range b, int c, ...). It doesn't make sense to me to differentiate between indexing and slicing, because then how would you implement things like arr[1, 0..$, 2, 3, 2..5]? Rather, think of opIndex as implementing a[...], which can either be a single element (all arguments are indices), a full-dimensional slice (i.e., all arguments are ranges), or a sub-dimensional slice (some arguments are indices, some are ranges).

Orvid King
Orvid commented March 17, 2014

Your use of the ranges there is born out of the lack of a proper way to do it, opIndex should only ever be used to refer to a single element, a sub-dimensional slice is still a slice, and could even be treated as a full-dimensional slice. Whatever way you look at it, they are still slices and thus should be using opSlice.

H. S. Teoh

I find the use of opIndex for both purposes more uniform, because it allows generic implementations that handle all cases in a single interface. Subdimensional slicing, in my implementation, actually returns a different type depending on how many arguments are slices and how many are indices (the types are segmented by dimension; the 0-dimensional case aliases to the element type); the case where all are indices is only one of the many possible combinations. Arbitrarily segmenting opIndex to two names introduces a lot of inelegance in the code, because now everything has to specially treat the case where all arguments happen to be indices, whereas now, it simply handles all cases together.

In any case, you're arguing with the wrong person; you should be asking Kenji the rationale for this design, since he's the one who implemented it! :)

MasonMcGill

Great work folks! I do have a concern, though: it looks like you're bringing Python 2.0-style slice handling to D, even though it turned out there were limitations to that model that could have been avoided with a different design. It worries me that Julia, a language specifically designed ~15 years later to make up for the shortcomings in existing scientific platforms, chose an alternate approach. More discussion of that here:
http://forum.dlang.org/thread/upzdamhmxrrlsexgcdva@forum.dlang.org

It looks like I timed this pretty poorly, but I actually just wrote up a DIP that addresses the problem of "advanced" indexing in a more general way, and enables more MATLAB/Julia-style numerical features, while reducing language complexity and maintaining backwards compatibility:
http://wiki.dlang.org/DIP58

Again, I know the timing's horrible, after all the work that's gone into this, but I think it's important to get numerical syntax right, and I think a bit of discussion about the pros and cons of each approach would do the language some good.

Discussion receptacle:
http://forum.dlang.org/thread/clswrooryjcudncqbkje@forum.dlang.org

Hara Kenji
Collaborator
9rnsr commented March 17, 2014

@quickfur In the proposal, the member function opIndex will be used for every bracket access syntax (a[], a[i], a[i..j], a[$, i..j], etc). It's just syntactic rewriting rule, and it's not directly related to the code meaning of indexing, as the same as that opDollar is not directly related to the length or size of container.

Hara Kenji
Collaborator
9rnsr commented March 17, 2014

@MasonMcGill Sometimes .. operator had been proposed in the newsgroup, but it is not yet exist. So this proposal did not consider it.

MasonMcGill

@9rnsr Understood. My request was for a discussion about why this approach is better than something like http://wiki.dlang.org/DIP58 , and whether this is meant to be the "last stop" for the evolution of numerical syntax in D, or part of a larger transition.

Orvid King
Orvid commented March 17, 2014

I believe that it is related to the code meaning of indexing, by introducing this under the name of opIndex, it's using the wrong term for the slicing operation that is being performed.

Hara Kenji
Collaborator
9rnsr commented March 17, 2014

@Orvid I agree it was related. But, by supporting multi-dimensional slicing, a[i..j] could also be considered as one dimension indexing with an interval.
So uniformly using the name opIndex for both a[i..j] and a[i..j, k] would be practically convenient. I don't think it's so big issue.

Don Clugston

@Orvid

I believe that it is related to the code meaning of indexing, by introducing this under the name of opIndex, it's using the wrong term for the slicing operation that is being performed.

IMHO opSlice would not be the correct term. The only sense in which it is a "slice" is that with an n-dimensional array, it doesn't reduce the dimensionality, whereas an "index" normally does. But we don't even enforce those semantics. For example, you could create something with an infinite number of dimensions, and you could make slicing reduce the dimensionality as well.

Really it is a strided slice, which is a totally different beast, neither an index nor a slice, it doesn't have much in common with normal slices. Pragmatically, the opIndex syntax generalizes, whereas the opSlice syntax does not.

Orvid King
Orvid commented March 21, 2014

While I do see your point that opSlice is not correct in all cases, there are cases where it would be the correct term. This PR adds the ability to use opIndex in places where opSlice was previously the preferred operator. It also adds an overload to opIndex that opSlice should have as well, for use in the cases where opSlice would be the correct term, unless part of the intention of this PR is to start a deprecation path for opSlice in favor of opIndex?

JakobOvrum

Is there a corresponding documentation PR for this?

H. S. Teoh

@Orvid No, in this PR opSlice performs the duty of translating an x..y range into an aggregate index object represent that range. This aggregate index is then translated into an actual slicing operation by opIndex. The idea is to unify slicing and indexing, not to deprecate anything over the other.

Hara Kenji 9rnsr deleted the branch April 15, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 10 unique commits by 1 author.

Mar 16, 2014
[Refactoring] ArrayScopeSymbol::search b12585b
[Refactoring] Operator overload handling in AssignExp
Change to recursive call for later fallback mechanism.
74f5e0e
[Refactoring] More better error propagation from resolveOpDollar 05e72b2
Add IntervalExp and fix parser, and disabled test for issue 6789
All of N-dimensional array operations are now translated to ArrayExp.
But currently they are immediately translated to SliceExp, so have no effect.
ca6643e
Translate IntervalExp in ArrayExp::arguments to opSlice!dim(lwr, upr) 6124e8d
Support multi-dimensional opIndex
If it fails, fall back to opSlice for backward compatibility.
1f308ef
Support multi-dimensional opIndexAssign
If it fails, fall back to opSliceAssign for backward compatibility.
1dd0746
Support multi-dimensional opIndexUnary
If it fails, fall back to opSliceUnary for backward compatibility.
d947c75
Support multi-dimensional opIndexOpAssign
If it fails, fall back to opSliceOpAssign for backward compatibility.
b1f0106
Unmask all test for issue 6798 e46e7e5
This page is out of date. Refresh to see the latest.
53  src/dsymbol.c
@@ -1390,13 +1390,14 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags)
1390 1390
 {
1391 1391
     //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident->toChars(), flags);
1392 1392
     if (ident == Id::dollar)
1393  
-    {   VarDeclaration **pvar;
  1393
+    {
  1394
+        VarDeclaration **pvar;
1394 1395
         Expression *ce;
1395 1396
 
1396 1397
     L1:
1397  
-
1398 1398
         if (td)
1399  
-        {   /* $ gives the number of elements in the tuple
  1399
+        {
  1400
+            /* $ gives the number of elements in the tuple
1400 1401
              */
1401 1402
             VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL);
1402 1403
             Expression *e = new IntegerExp(Loc(), td->objects->dim, Type::tsize_t);
@@ -1407,7 +1408,8 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags)
1407 1408
         }
1408 1409
 
1409 1410
         if (type)
1410  
-        {   /* $ gives the number of type entries in the type tuple
  1411
+        {
  1412
+            /* $ gives the number of type entries in the type tuple
1411 1413
              */
1412 1414
             VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL);
1413 1415
             Expression *e = new IntegerExp(Loc(), type->arguments->dim, Type::tsize_t);
@@ -1418,34 +1420,36 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags)
1418 1420
         }
1419 1421
 
1420 1422
         if (exp->op == TOKindex)
1421  
-        {   /* array[index] where index is some function of $
  1423
+        {
  1424
+            /* array[index] where index is some function of $
1422 1425
              */
1423 1426
             IndexExp *ie = (IndexExp *)exp;
1424  
-
1425 1427
             pvar = &ie->lengthVar;
1426 1428
             ce = ie->e1;
1427 1429
         }
1428 1430
         else if (exp->op == TOKslice)
1429  
-        {   /* array[lwr .. upr] where lwr or upr is some function of $
  1431
+        {
  1432
+            /* array[lwr .. upr] where lwr or upr is some function of $
1430 1433
              */
1431 1434
             SliceExp *se = (SliceExp *)exp;
1432  
-
1433 1435
             pvar = &se->lengthVar;
1434 1436
             ce = se->e1;
1435 1437
         }
1436 1438
         else if (exp->op == TOKarray)
1437  
-        {   /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $
  1439
+        {
  1440
+            /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $
1438 1441
              * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...)
1439 1442
              */
1440 1443
             ArrayExp *ae = (ArrayExp *)exp;
1441  
-
1442 1444
             pvar = &ae->lengthVar;
1443 1445
             ce = ae->e1;
1444 1446
         }
1445 1447
         else
  1448
+        {
1446 1449
             /* Didn't find $, look in enclosing scope(s).
1447 1450
              */
1448 1451
             return NULL;
  1452
+        }
1449 1453
 
1450 1454
         while (ce->op == TOKcomma)
1451 1455
             ce = ((CommaExp *)ce)->e2;
@@ -1458,7 +1462,8 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags)
1458 1462
         {
1459 1463
             Type *t = ((TypeExp *)ce)->type;
1460 1464
             if (t->ty == Ttuple)
1461  
-            {   type = (TypeTuple *)t;
  1465
+            {
  1466
+                type = (TypeTuple *)t;
1462 1467
                 goto L1;
1463 1468
             }
1464 1469
         }
@@ -1467,12 +1472,14 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags)
1467 1472
          * multiple times, it gets set only once.
1468 1473
          */
1469 1474
         if (!*pvar)             // if not already initialized
1470  
-        {   /* Create variable v and set it to the value of $
  1475
+        {
  1476
+            /* Create variable v and set it to the value of $
1471 1477
              */
1472 1478
             VarDeclaration *v;
1473 1479
             Type *t;
1474 1480
             if (ce->op == TOKtuple)
1475  
-            {   /* It is for an expression tuple, so the
  1481
+            {
  1482
+                /* It is for an expression tuple, so the
1476 1483
                  * length will be a const.
1477 1484
                  */
1478 1485
                 Expression *e = new IntegerExp(Loc(), ((TupleExp *)ce)->exps->dim, Type::tsize_t);
@@ -1481,18 +1488,10 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags)
1481 1488
             }
1482 1489
             else if (ce->type && (t = ce->type->toBasetype()) != NULL &&
1483 1490
                      (t->ty == Tstruct || t->ty == Tclass))
1484  
-            {   // Look for opDollar
  1491
+            {
  1492
+                // Look for opDollar
1485 1493
                 assert(exp->op == TOKarray || exp->op == TOKslice);
1486  
-                AggregateDeclaration *ad = NULL;
1487  
-
1488  
-                if (t->ty == Tclass)
1489  
-                {
1490  
-                    ad = ((TypeClass *)t)->sym;
1491  
-                }
1492  
-                else if (t->ty == Tstruct)
1493  
-                {
1494  
-                    ad = ((TypeStruct *)t)->sym;
1495  
-                }
  1494
+                AggregateDeclaration *ad = isAggregate(t);
1496 1495
                 assert(ad);
1497 1496
 
1498 1497
                 Dsymbol *s = ad->search(loc, Id::opDollar);
@@ -1521,7 +1520,8 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags)
1521 1520
                     e = new DotTemplateInstanceExp(loc, ce, td->ident, tiargs);
1522 1521
                 }
1523 1522
                 else
1524  
-                {   /* opDollar exists, but it's not a template.
  1523
+                {
  1524
+                    /* opDollar exists, but it's not a template.
1525 1525
                      * This is acceptable ONLY for single-dimension indexing.
1526 1526
                      * Note that it's impossible to have both template & function opDollar,
1527 1527
                      * because both take no arguments.
@@ -1545,7 +1545,8 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags)
1545 1545
                 v->storage_class |= STCtemp;
1546 1546
             }
1547 1547
             else
1548  
-            {   /* For arrays, $ will either be a compile-time constant
  1548
+            {
  1549
+                /* For arrays, $ will either be a compile-time constant
1549 1550
                  * (in which case its value in set during constant-folding),
1550 1551
                  * or a variable (in which case an expression is created in
1551 1552
                  * toir.c).
276  src/expression.c
@@ -9719,7 +9719,9 @@ Expression *SliceExp::semantic(Scope *sc)
9719 9719
         if (search_function(ad, Id::slice))
9720 9720
         {
9721 9721
             // Rewrite as e1.slice(lwr, upr)
9722  
-            Expression *e0 = resolveOpDollar(sc, this);
  9722
+            Expression *ex = resolveOpDollar(sc, this);
  9723
+            if (ex->op == TOKerror)
  9724
+                return ex;
9723 9725
             Expressions *a = new Expressions();
9724 9726
             assert(!lwr || upr);
9725 9727
             if (lwr)
@@ -9729,7 +9731,6 @@ Expression *SliceExp::semantic(Scope *sc)
9729 9731
             }
9730 9732
             e = new DotIdExp(loc, e1, Id::slice);
9731 9733
             e = new CallExp(loc, e, a);
9732  
-            e = combine(e0, e);
9733 9734
             e = e->semantic(sc);
9734 9735
             return e;
9735 9736
         }
@@ -9993,6 +9994,50 @@ Expression *ArrayLengthExp::rewriteOpAssign(BinExp *exp)
9993 9994
     return e;
9994 9995
 }
9995 9996
 
  9997
+/*********************** IntervalExp ********************************/
  9998
+
  9999
+// Mainly just a placeholder
  10000
+
  10001
+IntervalExp::IntervalExp(Loc loc, Expression *lwr, Expression *upr)
  10002
+        : Expression(loc, TOKinterval, sizeof(IntervalExp))
  10003
+{
  10004
+    this->lwr = lwr;
  10005
+    this->upr = upr;
  10006
+}
  10007
+
  10008
+Expression *IntervalExp::syntaxCopy()
  10009
+{
  10010
+    return new IntervalExp(loc, lwr->syntaxCopy(), upr->syntaxCopy());
  10011
+}
  10012
+
  10013
+Expression *IntervalExp::semantic(Scope *sc)
  10014
+{
  10015
+#if LOGSEMANTIC
  10016
+    printf("IntervalExp::semantic('%s')\n", toChars());
  10017
+#endif
  10018
+    if (type)
  10019
+        return this;
  10020
+
  10021
+    Expression *le = lwr;
  10022
+    le = le->semantic(sc);
  10023
+    le = resolveProperties(sc, le);
  10024
+
  10025
+    Expression *ue = upr;
  10026
+    ue = ue->semantic(sc);
  10027
+    ue = resolveProperties(sc, ue);
  10028
+
  10029
+    if (le->op == TOKerror)
  10030
+        return le;
  10031
+    if (ue->op == TOKerror)
  10032
+        return ue;
  10033
+
  10034
+    lwr = le;
  10035
+    upr = ue;
  10036
+
  10037
+    type = Type::tvoid;
  10038
+    return this;
  10039
+}
  10040
+
9996 10041
 /*********************** ArrayExp *************************************/
9997 10042
 
9998 10043
 // e1 [ i1, i2, i3, ... ]
@@ -10014,9 +10059,6 @@ Expression *ArrayExp::syntaxCopy()
10014 10059
 
10015 10060
 Expression *ArrayExp::semantic(Scope *sc)
10016 10061
 {
10017  
-    Expression *e;
10018  
-    Type *t1;
10019  
-
10020 10062
 #if LOGSEMANTIC
10021 10063
     printf("ArrayExp::semantic('%s')\n", toChars());
10022 10064
 #endif
@@ -10026,25 +10068,37 @@ Expression *ArrayExp::semantic(Scope *sc)
10026 10068
     if (e1->op == TOKerror)
10027 10069
         return e1;
10028 10070
 
10029  
-    t1 = e1->type->toBasetype();
  10071
+    Type *t1 = e1->type->toBasetype();
10030 10072
     if (t1->ty != Tclass && t1->ty != Tstruct)
10031  
-    {   // Convert to IndexExp
10032  
-        if (arguments->dim != 1)
10033  
-        {   error("only one index allowed to index %s", t1->toChars());
10034  
-            goto Lerr;
  10073
+    {
  10074
+        // Convert to IndexExp
  10075
+        Expression *e;
  10076
+        if (arguments->dim == 0)
  10077
+        {
  10078
+            e = new SliceExp(loc, e1, NULL, NULL);
  10079
+        }
  10080
+        else if (arguments->dim == 1 && (*arguments)[0]->op == TOKinterval)
  10081
+        {
  10082
+            IntervalExp *ie = (IntervalExp *)(*arguments)[0];
  10083
+            e = new SliceExp(loc, e1, ie->lwr, ie->upr);
  10084
+        }
  10085
+        else if (arguments->dim == 1)
  10086
+        {
  10087
+            e = new IndexExp(loc, e1, (*arguments)[0]);
  10088
+        }
  10089
+        else
  10090
+        {
  10091
+            error("only one index allowed to index %s", t1->toChars());
  10092
+            return new ErrorExp();
10035 10093
         }
10036  
-        e = new IndexExp(loc, e1, (*arguments)[0]);
10037 10094
         return e->semantic(sc);
10038 10095
     }
10039 10096
 
10040  
-    e = op_overload(sc);
10041  
-    if (!e)
10042  
-    {   error("no [] operator overload for type %s", e1->type->toChars());
10043  
-        goto Lerr;
10044  
-    }
10045  
-    return e;
  10097
+    Expression *e = op_overload(sc);
  10098
+    if (e)
  10099
+        return e;
10046 10100
 
10047  
-Lerr:
  10101
+    error("no [] operator overload for type %s", e1->type->toChars());
10048 10102
     return new ErrorExp();
10049 10103
 }
10050 10104
 
@@ -10539,42 +10593,68 @@ Expression *AssignExp::semantic(Scope *sc)
10539 10593
         ArrayExp *ae = (ArrayExp *)e1;
10540 10594
         ae->e1 = ae->e1->semantic(sc);
10541 10595
         ae->e1 = resolveProperties(sc, ae->e1);
10542  
-        Expression *ae1old = ae->e1;
10543 10596
 
10544 10597
         Type *t1 = ae->e1->type->toBasetype();
10545 10598
         AggregateDeclaration *ad = isAggregate(t1);
10546 10599
         if (ad)
10547 10600
         {
10548  
-          L1:
10549 10601
             // Rewrite (a[i] = value) to (a.opIndexAssign(value, i))
10550 10602
             if (search_function(ad, Id::indexass))
10551 10603
             {
10552 10604
                 // Deal with $
10553  
-                Expression *e0 = resolveOpDollar(sc, ae);
  10605
+                Expression *ex = resolveOpDollar(sc, ae);
  10606
+                if (!ex)
  10607
+                    goto Lfallback;
  10608
+                if (ex->op == TOKerror)
  10609
+                    return ex;
  10610
+
10554 10611
                 Expressions *a = (Expressions *)ae->arguments->copy();
10555 10612
                 a->insert(0, e2);
10556 10613
 
10557 10614
                 Expression *e = new DotIdExp(loc, ae->e1, Id::indexass);
10558 10615
                 e = new CallExp(loc, e, a);
10559  
-                e = combine(e0, e);
10560  
-                e = e->semantic(sc);
  10616
+                if (ae->arguments->dim == 0)
  10617
+                    e = e->trySemantic(sc);
  10618
+                else
  10619
+                    e = e->semantic(sc);
  10620
+                if (!e)
  10621
+                    goto Lfallback;
10561 10622
                 return e;
10562 10623
             }
10563  
-        }
10564 10624
 
10565  
-        // No opIndexAssign found yet, but there might be an alias this to try.
10566  
-        if (ad && ad->aliasthis && t1 != att1)
10567  
-        {
10568  
-            if (!att1 && t1->checkAliasThisRec())
10569  
-                att1 = t1;
10570  
-            ae->e1 = resolveAliasThis(sc, ae->e1);
10571  
-            t1 = ae->e1->type->toBasetype();
10572  
-            ad = isAggregate(t1);
10573  
-            if (ad)
10574  
-                goto L1;
10575  
-        }
  10625
+            // No opIndexAssign found yet, but there might be an alias this to try.
  10626
+            if (ad->aliasthis && t1 != ae->att1)
  10627
+            {
  10628
+                ArrayExp *aex = (ArrayExp *)ae->copy();
  10629
+                if (!aex->att1 && t1->checkAliasThisRec())
  10630
+                    aex->att1 = t1;
  10631
+                aex->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident);
  10632
+                this->e1 = aex;
  10633
+                Expression *ex = this->trySemantic(sc);
  10634
+                if (ex)
  10635
+                    return ex;
  10636
+                this->e1 = ae;  // restore
  10637
+            }
10576 10638
 
10577  
-        ae->e1 = ae1old;    // restore
  10639
+        Lfallback:
  10640
+            if (ae->arguments->dim == 0)
  10641
+            {
  10642
+                // a[] = e2
  10643
+                SliceExp *se = new SliceExp(ae->loc, ae->e1, NULL, NULL);
  10644
+                se->att1 = ae->att1;
  10645
+                this->e1 = se;
  10646
+                return this->semantic(sc);
  10647
+            }
  10648
+            if (ae->arguments->dim == 1 && (*ae->arguments)[0]->op == TOKinterval)
  10649
+            {
  10650
+                // a[lwr..upr] = e2
  10651
+                IntervalExp *ie = (IntervalExp *)(*ae->arguments)[0];
  10652
+                SliceExp *se = new SliceExp(ae->loc, ae->e1, ie->lwr, ie->upr);
  10653
+                se->att1 = ae->att1;
  10654
+                this->e1 = se;
  10655
+                return this->semantic(sc);
  10656
+            }
  10657
+        }
10578 10658
     }
10579 10659
     /* Look for operator overloading of a[i..j]=value.
10580 10660
      * Do it before semantic() otherwise the a[i..j] will have been
@@ -10585,17 +10665,17 @@ Expression *AssignExp::semantic(Scope *sc)
10585 10665
         SliceExp *ae = (SliceExp *)e1;
10586 10666
         ae->e1 = ae->e1->semantic(sc);
10587 10667
         ae->e1 = resolveProperties(sc, ae->e1);
10588  
-        Expression *ae1old = ae->e1;
10589 10668
 
10590 10669
         Type *t1 = ae->e1->type->toBasetype();
10591 10670
         AggregateDeclaration *ad = isAggregate(t1);
10592 10671
         if (ad)
10593 10672
         {
10594  
-          L2:
10595 10673
             // Rewrite (a[i..j] = value) to (a.opSliceAssign(value, i, j))
10596 10674
             if (search_function(ad, Id::sliceass))
10597 10675
             {
10598  
-                Expression *e0 = resolveOpDollar(sc, ae);
  10676
+                Expression *ex = resolveOpDollar(sc, ae);
  10677
+                if (ex->op == TOKerror)
  10678
+                    return ex;
10599 10679
                 Expressions *a = new Expressions();
10600 10680
                 a->push(e2);
10601 10681
                 assert(!ae->lwr || ae->upr);
@@ -10606,25 +10686,24 @@ Expression *AssignExp::semantic(Scope *sc)
10606 10686
                 }
10607 10687
                 Expression *e = new DotIdExp(loc, ae->e1, Id::sliceass);
10608 10688
                 e = new CallExp(loc, e, a);
10609  
-                e = combine(e0, e);
10610 10689
                 e = e->semantic(sc);
10611 10690
                 return e;
10612 10691
             }
10613  
-        }
10614 10692
 
10615  
-        // No opSliceAssign found yet, but there might be an alias this to try.
10616  
-        if (ad && ad->aliasthis && t1 != att1)
10617  
-        {
10618  
-            if (!att1 && t1->checkAliasThisRec())
10619  
-                att1 = t1;
10620  
-            ae->e1 = resolveAliasThis(sc, ae->e1);
10621  
-            t1 = ae->e1->type->toBasetype();
10622  
-            ad = isAggregate(t1);
10623  
-            if (ad)
10624  
-                goto L2;
  10693
+            // No opSliceAssign found yet, but there might be an alias this to try.
  10694
+            if (ad->aliasthis && t1 != ae->att1)
  10695
+            {
  10696
+                SliceExp *aex = (SliceExp *)ae->copy();
  10697
+                if (!aex->att1 && t1->checkAliasThisRec())
  10698
+                    aex->att1 = t1;
  10699
+                aex->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident);
  10700
+                this->e1 = aex;
  10701
+                Expression *ex = this->trySemantic(sc);
  10702
+                if (ex)
  10703
+                    return ex;
  10704
+                this->e1 = ae;  // restore
  10705
+            }
10625 10706
         }
10626  
-
10627  
-        ae->e1 = ae1old;    // restore
10628 10707
     }
10629 10708
 
10630 10709
     /* With UFCS, e.f = value
@@ -11142,7 +11221,7 @@ Expression *AssignExp::semantic(Scope *sc)
11142 11221
             if (!e2->implicitConvTo(e1->type))
11143 11222
             {
11144 11223
                 /* Internal handling for the default initialization
11145  
-                 * of multi-dimentional static array:
  11224
+                 * of multi-dimensional static array:
11146 11225
                  *  T[2][3] sa; // = T.init; if T is zero-init
11147 11226
                  */
11148 11227
                 // Treat e1 as one large array
@@ -13529,10 +13608,34 @@ Expression *resolveOpDollar(Scope *sc, ArrayExp *ae)
13529 13608
 {
13530 13609
     assert(!ae->lengthVar);
13531 13610
 
13532  
-    Expression *e0 = extractOpDollarSideEffect(sc, ae);
  13611
+    Expression *e0 = NULL;
  13612
+
  13613
+    AggregateDeclaration *ad = isAggregate(ae->e1->type);
  13614
+    Dsymbol *slice = search_function(ad, Id::slice);
  13615
+    //printf("slice = %s %s\n", slice->kind(), slice->toChars());
13533 13616
 
13534 13617
     for (size_t i = 0; i < ae->arguments->dim; i++)
13535 13618
     {
  13619
+        if (i == 0)
  13620
+            e0 = extractOpDollarSideEffect(sc, ae);
  13621
+
  13622
+        Expression *e = (*ae->arguments)[i];
  13623
+        if (e->op == TOKinterval && !(slice && slice->isTemplateDeclaration()))
  13624
+        {
  13625
+        Lfallback:
  13626
+            if (ae->arguments->dim == 1)
  13627
+            {
  13628
+                ae->e1 = Expression::combine(e0, ae->e1);
  13629
+                return NULL;
  13630
+            }
  13631
+            else
  13632
+            {
  13633
+                ae->error("multi-dimensional slicing requires template opSlice");
  13634
+                return new ErrorExp();
  13635
+            }
  13636
+        }
  13637
+        //printf("[%d] e = %s\n", i, e->toChars());
  13638
+
13536 13639
         // Create scope for '$' variable for this dimension
13537 13640
         ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae);
13538 13641
         sym->loc = ae->loc;
@@ -13541,23 +13644,61 @@ Expression *resolveOpDollar(Scope *sc, ArrayExp *ae)
13541 13644
         ae->lengthVar = NULL;       // Create it only if required
13542 13645
         ae->currentDimension = i;   // Dimension for $, if required
13543 13646
 
13544  
-        Expression *e = (*ae->arguments)[i];
13545 13647
         e = e->semantic(sc);
13546 13648
         e = resolveProperties(sc, e);
13547  
-        if (!e->type)
13548  
-            ae->error("%s has no value", e->toChars());
  13649
+
13549 13650
         if (ae->lengthVar && sc->func)
13550 13651
         {
13551 13652
             // If $ was used, declare it now
13552 13653
             Expression *de = new DeclarationExp(ae->loc, ae->lengthVar);
13553  
-            e = new CommaExp(Loc(), de, e);
  13654
+            de = de->semantic(sc);
  13655
+            e0 = Expression::combine(e0, de);
  13656
+        }
  13657
+        sc = sc->pop();
  13658
+
  13659
+        if (e->op == TOKinterval)
  13660
+        {
  13661
+            IntervalExp *ie = (IntervalExp *)e;
  13662
+
  13663
+            Objects *tiargs = new Objects();
  13664
+            Expression *edim = new IntegerExp(ae->loc, i, Type::tsize_t);
  13665
+            edim = edim->semantic(sc);
  13666
+            tiargs->push(edim);
  13667
+
  13668
+            Expressions *fargs = new Expressions();
  13669
+            fargs->push(ie->lwr);
  13670
+            fargs->push(ie->upr);
  13671
+
  13672
+            unsigned xerrors = global.startGagging();
  13673
+            unsigned oldspec = global.speculativeGag;
  13674
+            global.speculativeGag = global.gag;
  13675
+            sc = sc->push();
  13676
+            sc->speculative = true;
  13677
+            FuncDeclaration *fslice = resolveFuncCall(ae->loc, sc, slice, tiargs, ae->e1->type, fargs, 1);
  13678
+            sc = sc->pop();
  13679
+            global.speculativeGag = oldspec;
  13680
+            global.endGagging(xerrors);
  13681
+            if (!fslice)
  13682
+                goto Lfallback;
  13683
+
  13684
+            e = new DotTemplateInstanceExp(ae->loc, ae->e1, slice->ident, tiargs);
  13685
+            e = new CallExp(ae->loc, e, fargs);
13554 13686
             e = e->semantic(sc);
13555 13687
         }
  13688
+
  13689
+        if (!e->type)
  13690
+        {
  13691
+            ae->error("%s has no value", e->toChars());
  13692
+            e = new ErrorExp();
  13693
+        }
  13694
+        if (e->op == TOKerror)
  13695
+            return e;
  13696
+
13556 13697
         (*ae->arguments)[i] = e;
13557  
-        sc = sc->pop();
13558 13698
     }
13559 13699
 
13560  
-    return e0;
  13700
+    ae->e1 = Expression::combine(e0, ae->e1);
  13701
+    return ae;
13561 13702
 }
13562 13703
 
13563 13704
 /**************************************
@@ -13570,7 +13711,8 @@ Expression *resolveOpDollar(Scope *sc, SliceExp *se)
13570 13711
     assert(!se->lengthVar);
13571 13712
     assert(!se->lwr || se->upr);
13572 13713
 
13573  
-    if (!se->lwr) return NULL;
  13714
+    if (!se->lwr)
  13715
+        return se;
13574 13716
 
13575 13717
     Expression *e0 = extractOpDollarSideEffect(sc, se);
13576 13718
 
@@ -13586,7 +13728,10 @@ Expression *resolveOpDollar(Scope *sc, SliceExp *se)
13586 13728
         e = e->semantic(sc);
13587 13729
         e = resolveProperties(sc, e);
13588 13730
         if (!e->type)
  13731
+        {
13589 13732
             se->error("%s has no value", e->toChars());
  13733
+            return new ErrorExp();
  13734
+        }
13590 13735
         (i == 0 ? se->lwr : se->upr) = e;
13591 13736
     }
13592 13737
 
@@ -13594,12 +13739,13 @@ Expression *resolveOpDollar(Scope *sc, SliceExp *se)
13594 13739
     {
13595 13740
         // If $ was used, declare it now
13596 13741
         Expression *de = new DeclarationExp(se->loc, se->lengthVar);
13597  
-        se->lwr = new CommaExp(Loc(), de, se->lwr);
13598  
-        se->lwr = se->lwr->semantic(sc);
  13742
+        de = de->semantic(sc);
  13743
+        e0 = Expression::combine(e0, de);
13599 13744
     }
13600 13745
     sc = sc->pop();
13601 13746
 
13602  
-    return e0;
  13747
+    se->e1 = Expression::combine(e0, se->e1);
  13748
+    return se;
13603 13749
 }
13604 13750
 
13605 13751
 Expression *BinExp::reorderSettingAAElem(Scope *sc)
12  src/expression.h
@@ -1069,6 +1069,18 @@ class ArrayLengthExp : public UnaExp
1069 1069
     void accept(Visitor *v) { v->visit(this); }
1070 1070
 };
1071 1071
 
  1072
+struct IntervalExp : Expression
  1073
+{
  1074
+    Expression *lwr;
  1075
+    Expression *upr;
  1076
+
  1077
+    IntervalExp(Loc loc, Expression *lwr, Expression *upr);
  1078
+    Expression *syntaxCopy();
  1079
+    Expression *semantic(Scope *sc);
  1080
+
  1081
+    void accept(Visitor *v) { v->visit(this); }
  1082
+};
  1083
+
1072 1084
 // e1[a0,a1,a2,a3,...]
1073 1085
 
1074 1086
 class ArrayExp : public UnaExp
7  src/hdrgen.c
@@ -1625,6 +1625,13 @@ class PrettyPrintVisitor : public Visitor
1625 1625
         buf->writestring(".length");
1626 1626
     }
1627 1627
 
  1628
+    void visit(IntervalExp *e)
  1629
+    {
  1630
+        expToCBuffer(buf, hgs, e->lwr, PREC_assign);
  1631
+        buf->writestring("..");
  1632
+        expToCBuffer(buf, hgs, e->upr, PREC_assign);
  1633
+    }
  1634
+
1628 1635
     void visit(ArrayExp *e)
1629 1636
     {
1630 1637
         expToCBuffer(buf, hgs, e->e1, PREC_primary);
2  src/lexer.h
@@ -177,6 +177,8 @@ enum TOK
177 177
         TOKvector,
178 178
         TOKpound,
179 179
 
  180
+        TOKinterval,
  181
+
180 182
         TOKMAX
181 183
 };
182 184
 
159  src/opover.c
@@ -238,12 +238,24 @@ Expression *op_overload(Expression *e, Scope *sc)
238 238
                     Dsymbol *fd = search_function(ad, Id::opIndexUnary);
239 239
                     if (fd)
240 240
                     {
241  
-                        Expression *e0 = resolveOpDollar(sc, ae);
  241
+                        // Deal with $
  242
+                        Expression *ex = resolveOpDollar(sc, ae);
  243
+                        if (!ex)
  244
+                            goto Lfallback;
  245
+                        if (ex->op == TOKerror)
  246
+                        {
  247
+                            result = ex;
  248
+                            return;
  249
+                        }
242 250
                         Objects *tiargs = opToArg(sc, e->op);
243 251
                         result = new DotTemplateInstanceExp(e->loc, ae->e1, fd->ident, tiargs);
244 252
                         result = new CallExp(e->loc, result, ae->arguments);
245  
-                        result = Expression::combine(e0, result);
246  
-                        result = result->semantic(sc);
  253
+                        if (ae->arguments->dim == 0)
  254
+                            result = result->trySemantic(sc);
  255
+                        else
  256
+                            result = result->semantic(sc);
  257
+                        if (!result)
  258
+                            goto Lfallback;
247 259
                         return;
248 260
                     }
249 261
 
@@ -264,6 +276,27 @@ Expression *op_overload(Expression *e, Scope *sc)
264 276
                             return;
265 277
                     }
266 278
                     e->att1 = NULL;
  279
+
  280
+                Lfallback:
  281
+                    if (ae->arguments->dim == 0)
  282
+                    {
  283
+                        // op(a[])
  284
+                        SliceExp *se = new SliceExp(ae->loc, ae->e1, NULL, NULL);
  285
+                        se->att1 = ae->att1;
  286
+                        e->e1 = se;
  287
+                        e->accept(this);
  288
+                        return;
  289
+                    }
  290
+                    if (ae->arguments->dim == 1 && (*ae->arguments)[0]->op == TOKinterval)
  291
+                    {
  292
+                        // op(a[lwr..upr])
  293
+                        IntervalExp *ie = (IntervalExp *)(*ae->arguments)[0];
  294
+                        SliceExp *se = new SliceExp(ae->loc, ae->e1, ie->lwr, ie->upr);
  295
+                        se->att1 = ae->att1;
  296
+                        e->e1 = se;
  297
+                        e->accept(this);
  298
+                        return;
  299
+                    }
267 300
                 }
268 301
             }
269 302
             else if (e->e1->op == TOKslice)
@@ -281,7 +314,13 @@ Expression *op_overload(Expression *e, Scope *sc)
281 314
                     Dsymbol *fd = search_function(ad, Id::opSliceUnary);
282 315
                     if (fd)
283 316
                     {
284  
-                        Expression *e0 = resolveOpDollar(sc, se);
  317
+                        // Deal with $
  318
+                        Expression *ex = resolveOpDollar(sc, se);
  319
+                        if (ex->op == TOKerror)
  320
+                        {
  321
+                            result = ex;
  322
+                            return;
  323
+                        }
285 324
                         Expressions *a = new Expressions();
286 325
                         assert(!se->lwr || se->upr);
287 326
                         if (se->lwr)
@@ -292,7 +331,6 @@ Expression *op_overload(Expression *e, Scope *sc)
292 331
                         Objects *tiargs = opToArg(sc, e->op);
293 332
                         result = new DotTemplateInstanceExp(e->loc, se->e1, fd->ident, tiargs);
294 333
                         result = new CallExp(e->loc, result, a);
295  
-                        result = Expression::combine(e0, result);
296 334
                         result = result->semantic(sc);
297 335
                         return;
298 336
                     }
@@ -382,39 +420,73 @@ Expression *op_overload(Expression *e, Scope *sc)
382 420
             }
383 421
         }
384 422
 
385  
-        void visit(ArrayExp *e)
  423
+        void visit(ArrayExp *ae)
386 424
         {
387  
-            //printf("ArrayExp::op_overload() (%s)\n", e->toChars());
388  
-            AggregateDeclaration *ad = isAggregate(e->e1->type);
  425
+            //printf("ArrayExp::op_overload() (%s)\n", ae->toChars());
  426
+            AggregateDeclaration *ad = isAggregate(ae->e1->type);
389 427
             if (ad)
390 428
             {
391  
-                Dsymbol *fd = search_function(ad, opId(e));
  429
+                Dsymbol *fd = search_function(ad, opId(ae));
392 430
                 if (fd)
393 431
                 {
  432
+                    // Deal with $
  433
+                    Expression *ex = resolveOpDollar(sc, ae);
  434
+                    if (!ex)
  435
+                        goto Lfallback;
  436
+                    if (ex->op == TOKerror)
  437
+                    {
  438
+                        result = ex;
  439
+                        return;
  440
+                    }
  441
+
394 442
                     /* Rewrite op e1[arguments] as:
395 443
                      *    e1.opIndex(arguments)
396 444
                      */
397  
-                    Expression *e0 = resolveOpDollar(sc, e);
398  
-                    result = new DotIdExp(e->loc, e->e1, fd->ident);
399  
-                    result = new CallExp(e->loc, result, e->arguments);
400  
-                    result = Expression::combine(e0, result);
401  
-                    result = result->semantic(sc);
  445
+                    result = new DotIdExp(ae->loc, ae->e1, fd->ident);
  446
+                    result = new CallExp(ae->loc, result, ae->arguments);
  447
+                    if (ae->arguments->dim == 0)
  448
+                        result = result->trySemantic(sc);
  449
+                    else
  450
+                        result = result->semantic(sc);
  451
+                    if (!result)
  452
+                        goto Lfallback;
402 453
                     return;
403 454
                 }
404 455
 
  456
+                if (ae->e1->op == TOKtype && ae->arguments->dim < 2)
  457
+                    goto Lfallback;
  458
+
405 459
                 // Didn't find it. Forward to aliasthis
406  
-                if (ad->aliasthis && e->e1->type != e->att1)
  460
+                if (ad->aliasthis && ae->e1->type != ae->att1)
407 461
                 {
408 462
                     /* Rewrite op(e1) as:
409 463
                      *  op(e1.aliasthis)
410 464
                      */
411 465
                     //printf("att arr e1 = %s\n", this->e1->type->toChars());
412  
-                    Expression *e1 = new DotIdExp(e->loc, e->e1, ad->aliasthis->ident);
413  
-                    UnaExp *ue = (UnaExp *)e->copy();
414  
-                    if (!ue->att1 && e->e1->type->checkAliasThisRec())
415  
-                        ue->att1 = e->e1->type;
  466
+                    Expression *e1 = new DotIdExp(ae->loc, ae->e1, ad->aliasthis->ident);
  467
+                    UnaExp *ue = (UnaExp *)ae->copy();
  468
+                    if (!ue->att1 && ae->e1->type->checkAliasThisRec())
  469
+                        ue->att1 = ae->e1->type;
416 470
                     ue->e1 = e1;
417 471
                     result = ue->trySemantic(sc);
  472
+                    if (result)
  473
+                        return;
  474
+                }
  475
+
  476
+            Lfallback:
  477
+                if (ae->arguments->dim == 0)
  478
+                {
  479
+                    // a[]
  480
+                    SliceExp *se = new SliceExp(ae->loc, ae->e1, NULL, NULL);
  481
+                    result = se->semantic(sc);
  482
+                    return;
  483
+                }
  484
+                if (ae->arguments->dim == 1 && (*ae->arguments)[0]->op == TOKinterval)
  485
+                {
  486
+                    // a[lwr..upr]
  487
+                    IntervalExp *ie = (IntervalExp *)(*ae->arguments)[0];
  488
+                    SliceExp *se = new SliceExp(ae->loc, ae->e1, ie->lwr, ie->upr);
  489
+                    result = se->semantic(sc);
418 490
                     return;
419 491
                 }
420 492
             }
@@ -850,15 +922,28 @@ Expression *op_overload(Expression *e, Scope *sc)
850 922
                     Dsymbol *fd = search_function(ad, Id::opIndexOpAssign);
851 923
                     if (fd)
852 924
                     {
853  
-                        Expression *e0 = resolveOpDollar(sc, ae);
  925
+                        // Deal with $
  926
+                        Expression *ex = resolveOpDollar(sc, ae);
  927
+                        if (!ex)
  928
+                            goto Lfallback;
  929
+                        if (ex->op == TOKerror)
  930
+                        {
  931
+                            result = ex;
  932
+                            return;
  933
+                        }
  934
+
854 935
                         Expressions *a = (Expressions *)ae->arguments->copy();
855 936
                         a->insert(0, e->e2);
856 937
 
857 938
                         Objects *tiargs = opToArg(sc, e->op);
858 939
                         result = new DotTemplateInstanceExp(e->loc, ae->e1, fd->ident, tiargs);
859 940
                         result = new CallExp(e->loc, result, a);
860  
-                        result = Expression::combine(e0, result);
861  
-                        result = result->semantic(sc);
  941
+                        if (ae->arguments->dim == 0)
  942
+                            result = result->trySemantic(sc);
  943
+                        else
  944
+                            result = result->semantic(sc);
  945
+                        if (!result)
  946
+                            goto Lfallback;
862 947
                         return;
863 948
                     }
864 949
 
@@ -879,6 +964,27 @@ Expression *op_overload(Expression *e, Scope *sc)
879 964
                             return;
880 965
                     }
881 966
                     e->att1 = NULL;
  967
+
  968
+                Lfallback:
  969
+                    if (ae->arguments->dim == 0)
  970
+                    {
  971
+                        // a[] op= e2
  972
+                        SliceExp *se = new SliceExp(ae->loc, ae->e1, NULL, NULL);
  973
+                        se->att1 = ae->att1;
  974
+                        e->e1 = se;
  975
+                        e->accept(this);
  976
+                        return;
  977
+                    }
  978
+                    if (ae->arguments->dim == 1 && (*ae->arguments)[0]->op == TOKinterval)
  979
+                    {
  980
+                        // a[lwr..upr] op= e2
  981
+                        IntervalExp *ie = (IntervalExp *)(*ae->arguments)[0];
  982
+                        SliceExp *se = new SliceExp(ae->loc, ae->e1, ie->lwr, ie->upr);
  983
+                        se->att1 = ae->att1;
  984
+                        e->e1 = se;
  985
+                        e->accept(this);
  986
+                        return;
  987
+                    }
882 988
                 }
883 989
             }
884 990
             else if (e->e1->op == TOKslice)
@@ -896,7 +1002,13 @@ Expression *op_overload(Expression *e, Scope *sc)
896 1002
                     Dsymbol *fd = search_function(ad, Id::opSliceOpAssign);
897 1003
                     if (fd)
898 1004
                     {
899  
-                        Expression *e0 = resolveOpDollar(sc, se);
  1005
+                        // Deal with $
  1006
+                        Expression *ex = resolveOpDollar(sc, se);
  1007
+                        if (ex->op == TOKerror)
  1008
+                        {
  1009
+                            result = ex;
  1010
+                            return;
  1011
+                        }
900 1012
                         Expressions *a = new Expressions();
901 1013
                         a->push(e->e2);
902 1014
                         assert(!se->lwr || se->upr);
@@ -909,7 +1021,6 @@ Expression *op_overload(Expression *e, Scope *sc)
909 1021
                         Objects *tiargs = opToArg(sc, e->op);
910 1022
                         result = new DotTemplateInstanceExp(e->loc, se->e1, fd->ident, tiargs);
911 1023
                         result = new CallExp(e->loc, result, a);
912  
-                        result = Expression::combine(e0, result);
913 1024
                         result = result->semantic(sc);
914 1025
                         return;
915 1026
                     }
40  src/parse.c
@@ -6512,45 +6512,29 @@ Expression *Parser::parsePostExp(Expression *e)
6512 6512
                 //      array[lwr .. upr]
6513 6513
                 Expression *index;
6514 6514
                 Expression *upr;
  6515
+                Expressions *arguments = new Expressions();
6515 6516
 
6516 6517
                 inBrackets++;
6517 6518
                 nextToken();
6518  
-                if (token.value == TOKrbracket)
6519  
-                {   // array[]
6520  
-                    inBrackets--;
6521  
-                    e = new SliceExp(loc, e, NULL, NULL);
6522  
-                    nextToken();
6523  
-                }
6524  
-                else
  6519
+                while (token.value != TOKrbracket && token.value != TOKeof)
6525 6520
                 {
6526 6521
                     index = parseAssignExp();
6527 6522
                     if (token.value == TOKslice)
6528  
-                    {   // array[lwr .. upr]
  6523
+                    {
  6524
+                        // array[..., lwr..upr, ...]
6529 6525
                         nextToken();
6530 6526
                         upr = parseAssignExp();
6531  
-                        e = new SliceExp(loc, e, index, upr);
  6527
+                        arguments->push(new IntervalExp(loc, index, upr));
6532 6528
                     }
6533 6529
                     else
6534  
-                    {   // array[index, i2, i3, i4, ...]
6535  
-                        Expressions *arguments = new Expressions();
6536 6530
                         arguments->push(index);
6537  
-                        if (token.value == TOKcomma)
6538  
-                        {
6539  
-                            nextToken();
6540  
-                            while (token.value != TOKrbracket && token.value != TOKeof)
6541  
-                            {
6542  
-                                Expression *arg = parseAssignExp();
6543  
-                                arguments->push(arg);
6544  
-                                if (token.value == TOKrbracket)
6545  
-                                    break;
6546  
-                                check(TOKcomma);
6547  
-                            }
6548  
-                        }
6549  
-                        e = new ArrayExp(loc, e, arguments);
6550  
-                    }
6551  
-                    check(TOKrbracket);
6552  
-                    inBrackets--;
  6531
+                    if (token.value == TOKrbracket)
  6532
+                        break;
  6533
+                    check(TOKcomma);
6553 6534
                 }
  6535
+                check(TOKrbracket);
  6536
+                inBrackets--;
  6537
+                e = new ArrayExp(loc, e, arguments);
6554 6538
                 continue;
6555 6539
             }
6556 6540
 
@@ -7427,5 +7411,7 @@ void initPrecedence()
7427 7411
 
7428 7412
     precedence[TOKcomma] = PREC_expr;
7429 7413
     precedence[TOKdeclaration] = PREC_expr;
  7414
+
  7415
+    precedence[TOKinterval] = PREC_assign;
7430 7416
 }
7431 7417
 
2  src/visitor.h
@@ -217,6 +217,7 @@ class CastExp;
217 217
 class VectorExp;
218 218
 class SliceExp;
219 219
 class ArrayLengthExp;
  220
+class IntervalExp;
220 221
 class ArrayExp;
221 222
 class DotExp;
222 223
 class CommaExp;
@@ -485,6 +486,7 @@ class Visitor
485 486
     virtual void visit(VectorExp *e) { visit((UnaExp *)e); }
486 487
     virtual void visit(SliceExp *e) { visit((UnaExp *)e); }
487 488
     virtual void visit(ArrayLengthExp *e) { visit((UnaExp *)e); }
  489
+    virtual void visit(IntervalExp *e) { visit((Expression *)e); }
488 490
     virtual void visit(ArrayExp *e) { visit((UnaExp *)e); }
489 491
     virtual void visit(DotExp *e) { visit((BinExp *)e); }
490 492
     virtual void visit(CommaExp *e) { visit((BinExp *)e); }
10  test/fail_compilation/fail315.d
... ...
@@ -1,11 +1,11 @@
1 1
 /*
2 2
 TEST_OUTPUT:
3 3
 ---
4  
-fail_compilation/fail315.d-mixin-16(16): Error: found ';' when expecting ']'
5  
-fail_compilation/fail315.d-mixin-16(16): Error: found '}' when expecting ';' following return statement
6  
-fail_compilation/fail315.d-mixin-16(16): Error: expression expected, not ')'
7  
-fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting ')'
8  
-fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting ';' following statement
  4
+fail_compilation/fail315.d-mixin-16(16): Error: found ';' when expecting ','
  5
+fail_compilation/fail315.d-mixin-16(16): Error: expression expected, not '}'
  6
+fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting ','
  7
+fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting ']'
  8
+fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting ';' following return statement
9 9
 fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting '}' following compound statement
10 10
 fail_compilation/fail315.d(21): Error: template instance fail315.foo!() error instantiating
11 11
 ---
4  test/runnable/aliasthis.d
@@ -1268,11 +1268,11 @@ void test8735()
1268 1268
     // 9709 case
1269 1269
     alias A = Tuple9709!(1,int,"foo");
1270 1270
     A a;
1271  
-    static assert(A[0] == 1);
  1271
+    //static assert(A[0] == 1);
1272 1272
     static assert(a[0] == 1);
1273 1273
     //static assert(is(A[1] == int));
1274 1274
     //static assert(is(a[1] == int));
1275  
-    static assert(A[2] == "foo");