summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/ccgexprs.nim4
-rw-r--r--compiler/ccgmerge.nim24
-rw-r--r--compiler/jsgen.nim4
-rw-r--r--compiler/parampatterns.nim7
-rw-r--r--compiler/semdata.nim55
-rw-r--r--compiler/semexprs.nim17
-rw-r--r--compiler/semmagic.nim30
-rw-r--r--compiler/types.nim2
-rw-r--r--lib/system.nim12
-rw-r--r--tests/array/troof1.nim36
-rw-r--r--tests/array/troof2.nim10
-rw-r--r--tests/array/troof3.nim8
-rw-r--r--tests/array/troof4.nim37
-rw-r--r--web/news.txt19
15 files changed, 211 insertions, 56 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index d94f09ccb..b82a3887d 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -529,7 +529,7 @@ type
   TMagic* = enum # symbols that require compiler magic:
     mNone,
     mDefined, mDefinedInScope, mCompiles,
-    mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf,
+    mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof,
     mEcho, mShallowCopy, mSlurp, mStaticExec,
     mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
     mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index e19341db5..a280abc31 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1735,9 +1735,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mReset: genReset(p, e)
   of mEcho: genEcho(p, e[1].skipConv)
   of mArrToSeq: genArrToSeq(p, e, d)
-  of mNLen..mNError:
-    localError(e.info, errCannotGenerateCodeForX, e.sons[0].sym.name.s)
-  of mSlurp..mQuoteAst:
+  of mNLen..mNError, mSlurp..mQuoteAst:
     localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
   of mSpawn:
     let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil)
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index f4f837834..5057b9ff1 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -94,7 +94,7 @@ proc writeIntSet(a: IntSet, s: var string) =
     encodeVInt(x, s)
     inc i
   s.add('}')
-  
+
 proc genMergeInfo*(m: BModule): PRope =
   if optSymbolFiles notin gGlobalOptions: return nil
   var s = "/*\tNIM_merge_INFO:"
@@ -113,7 +113,7 @@ proc genMergeInfo*(m: BModule): PRope =
   s.add("*/")
   result = s.toRope
 
-template `^`(pos: expr): expr = L.buf[pos]
+template `^`(pos: int): expr = L.buf[pos]
 
 proc skipWhite(L: var TBaseLexer) =
   var pos = L.bufpos
@@ -132,7 +132,7 @@ proc skipUntilCmd(L: var TBaseLexer) =
     of CR: pos = nimlexbase.handleCR(L, pos)
     of LF: pos = nimlexbase.handleLF(L, pos)
     of '\0': break
-    of '/': 
+    of '/':
       if ^(pos+1) == '*' and ^(pos+2) == '\t':
         inc pos, 3
         break
@@ -145,7 +145,7 @@ proc atEndMark(buf: cstring, pos: int): bool =
   while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
   result = s == NimMergeEndMark.len
 
-proc readVerbatimSection(L: var TBaseLexer): PRope = 
+proc readVerbatimSection(L: var TBaseLexer): PRope =
   var pos = L.bufpos
   var buf = L.buf
   var r = newStringOfCap(30_000)
@@ -162,7 +162,7 @@ proc readVerbatimSection(L: var TBaseLexer): PRope =
     of '\0':
       internalError("ccgmerge: expected: " & NimMergeEndMark)
       break
-    else: 
+    else:
       if atEndMark(buf, pos):
         inc pos, NimMergeEndMark.len
         break
@@ -181,7 +181,7 @@ proc readKey(L: var TBaseLexer, result: var string) =
   if buf[pos] != ':': internalError("ccgmerge: ':' expected")
   L.bufpos = pos + 1 # skip ':'
 
-proc newFakeType(id: int): PType = 
+proc newFakeType(id: int): PType =
   new(result)
   result.id = id
 
@@ -227,8 +227,8 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
 
 when not defined(nimhygiene):
   {.pragma: inject.}
-  
-template withCFile(cfilename: string, body: stmt) {.immediate.} = 
+
+template withCFile(cfilename: string, body: stmt) {.immediate.} =
   var s = llStreamOpen(cfilename, fmRead)
   if s == nil: return
   var L {.inject.}: TBaseLexer
@@ -239,7 +239,7 @@ template withCFile(cfilename: string, body: stmt) {.immediate.} =
     if ^L.bufpos == '\0': break
     body
   closeBaseLexer(L)
-  
+
 proc readMergeInfo*(cfilename: string, m: BModule) =
   ## reads the merge meta information into `m`.
   withCFile(cfilename):
@@ -257,7 +257,7 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
   ## reads the merge sections into `m`.
   withCFile(cfilename):
     readKey(L, k)
-    if k == "NIM_merge_INFO":   
+    if k == "NIM_merge_INFO":
       discard
     elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
       inc(L.bufpos, 2)
@@ -283,7 +283,7 @@ proc mergeRequired*(m: BModule): bool =
       #echo "not empty: ", i, " ", ropeToStr(m.s[i])
       return true
   for i in low(TCProcSection)..high(TCProcSection):
-    if m.initProc.s(i) != nil: 
+    if m.initProc.s(i) != nil:
       #echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i])
       return true
 
@@ -291,7 +291,7 @@ proc mergeFiles*(cfilename: string, m: BModule) =
   ## merges the C file with the old version on hard disc.
   var old: TMergeSections
   readMergeSections(cfilename, old)
-  # do the merge; old section before new section:    
+  # do the merge; old section before new section:
   for i in low(TCFileSection)..high(TCFileSection):
     m.s[i] = con(old.f[i], m.s[i])
   for i in low(TCProcSection)..high(TCProcSection):
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 4e0e732a5..563f0c866 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -1371,13 +1371,11 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
   of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]")
   of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)")
-  of mNLen..mNError:
-    localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s)
   of mNewSeq: genNewSeq(p, n)
   of mOf: genOf(p, n, r)
   of mReset: genReset(p, n)
   of mEcho: genEcho(p, n, r)
-  of mSlurp, mStaticExec:
+  of mNLen..mNError, mSlurp, mStaticExec:
     localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
   of mCopyStr: binaryExpr(p, n, r, "", "($1.slice($2))")
   of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index 8c0875ab1..3f67005b9 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -131,11 +131,10 @@ proc semNodeKindConstraints*(p: PNode): PNode =
   result.strVal.add(ppEof)
 
 type
-  TSideEffectAnalysis = enum
+  TSideEffectAnalysis* = enum
     seUnknown, seSideEffect, seNoSideEffect
 
-proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
-  # XXX is 'raise' a side effect?
+proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
   case n.kind
   of nkCallKinds:
     # only calls can produce side effects:
@@ -162,6 +161,8 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
     # an atom cannot produce a side effect:
     result = seNoSideEffect
   else:
+    # assume no side effect:
+    result = seNoSideEffect
     for i in 0 .. <n.len:
       let ret = checkForSideEffects(n.sons[i])
       if ret == seSideEffect: return ret
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 27d441000..9b5d788af 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -9,13 +9,13 @@
 
 ## This module contains the data structures for the semantic checking phase.
 
-import 
+import
   strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab,
-  wordrecg, 
-  ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, 
+  wordrecg,
+  ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
   magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef
 
-type 
+type
   TOptionEntry* = object of lists.TListEntry # entries to put on a
                                              # stack for pragma parsing
     options*: TOptions
@@ -26,7 +26,7 @@ type
 
   POptionEntry* = ref TOptionEntry
   PProcCon* = ref TProcCon
-  TProcCon*{.final.} = object # procedure context; also used for top-level
+  TProcCon* = object          # procedure context; also used for top-level
                               # statements
     owner*: PSym              # the symbol this context belongs to
     resultSym*: PSym          # the result symbol (if we are in a proc)
@@ -36,12 +36,13 @@ type
                               # in standalone ``except`` and ``finally``
     next*: PProcCon           # used for stacking procedure contexts
     wasForwarded*: bool       # whether the current proc has a separate header
-  
+    bracketExpr*: PNode       # current bracket expression (for ^ support)
+
   TInstantiationPair* = object
     genericSym*: PSym
     inst*: PInstantiation
 
-  TExprFlag* = enum 
+  TExprFlag* = enum
     efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
     efAllowDestructor, efWantValue, efOperand, efNoSemCheck
   TExprFlags* = set[TExprFlag]
@@ -57,7 +58,7 @@ type
                                # this is used so that generic instantiations
                                # can access private object fields
     instCounter*: int          # to prevent endless instantiations
-   
+
     ambiguousSymbols*: IntSet  # ids of all ambiguous symbols (cannot
                                # store this info in the syms themselves!)
     inTypeClass*: int          # > 0 if we are in a user-defined type class
@@ -95,7 +96,7 @@ type
     instDeepCopy*: proc (c: PContext; dc: PSym; t: PType;
                          info: TLineInfo): PSym {.nimcall.}
 
-   
+
 proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
   result.genericSym = s
   result.inst = inst
@@ -127,7 +128,7 @@ proc popOwner*()
 
 var gOwners*: seq[PSym] = @[]
 
-proc getCurrOwner(): PSym = 
+proc getCurrOwner(): PSym =
   # owner stack (used for initializing the
   # owner field of syms)
   # the documentation comment always gets
@@ -135,19 +136,19 @@ proc getCurrOwner(): PSym =
   # BUGFIX: global array is needed!
   result = gOwners[high(gOwners)]
 
-proc pushOwner(owner: PSym) = 
+proc pushOwner(owner: PSym) =
   add(gOwners, owner)
 
-proc popOwner() = 
+proc popOwner() =
   var length = len(gOwners)
   if length > 0: setLen(gOwners, length - 1)
   else: internalError("popOwner")
 
-proc lastOptionEntry(c: PContext): POptionEntry = 
+proc lastOptionEntry(c: PContext): POptionEntry =
   result = POptionEntry(c.optionStack.tail)
 
-proc pushProcCon*(c: PContext, owner: PSym) {.inline.} = 
-  if owner == nil: 
+proc pushProcCon*(c: PContext, owner: PSym) {.inline.} =
+  if owner == nil:
     internalError("owner is nil")
     return
   var x: PProcCon
@@ -158,7 +159,7 @@ proc pushProcCon*(c: PContext, owner: PSym) {.inline.} =
 
 proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
 
-proc newOptionEntry(): POptionEntry = 
+proc newOptionEntry(): POptionEntry =
   new(result)
   result.options = gOptions
   result.defaultCC = ccDefault
@@ -182,8 +183,8 @@ proc newContext(module: PSym): PContext =
 
 proc inclSym(sq: var TSymSeq, s: PSym) =
   var L = len(sq)
-  for i in countup(0, L - 1): 
-    if sq[i].id == s.id: return 
+  for i in countup(0, L - 1):
+    if sq[i].id == s.id: return
   setLen(sq, L + 1)
   sq[L] = s
 
@@ -193,20 +194,20 @@ proc addConverter*(c: PContext, conv: PSym) =
 proc addPattern*(c: PContext, p: PSym) =
   inclSym(c.patterns, p)
 
-proc newLib(kind: TLibKind): PLib = 
+proc newLib(kind: TLibKind): PLib =
   new(result)
   result.kind = kind          #initObjectSet(result.syms)
-  
+
 proc addToLib(lib: PLib, sym: PSym) =
   #if sym.annex != nil and not isGenericRoutine(sym):
   #  LocalError(sym.info, errInvalidPragma)
   sym.annex = lib
 
-proc makePtrType(c: PContext, baseType: PType): PType = 
+proc makePtrType(c: PContext, baseType: PType): PType =
   result = newTypeS(tyPtr, c)
   addSonSkipIntLit(result, baseType.assertNotNil)
 
-proc makeVarType(c: PContext, baseType: PType): PType = 
+proc makeVarType(c: PContext, baseType: PType): PType =
   result = newTypeS(tyVar, c)
   addSonSkipIntLit(result, baseType.assertNotNil)
 
@@ -286,7 +287,7 @@ proc errorNode*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkEmpty, n.info)
   result.typ = errorType(c)
 
-proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) = 
+proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) =
   dest.kind = kind
   dest.owner = getCurrOwner()
   dest.size = - 1
@@ -311,13 +312,13 @@ proc illFormedAst*(n: PNode) =
 proc illFormedAstLocal*(n: PNode) =
   localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
 
-proc checkSonsLen*(n: PNode, length: int) = 
+proc checkSonsLen*(n: PNode, length: int) =
   if sonsLen(n) != length: illFormedAst(n)
-  
-proc checkMinSonsLen*(n: PNode, length: int) = 
+
+proc checkMinSonsLen*(n: PNode, length: int) =
   if sonsLen(n) < length: illFormedAst(n)
 
-proc isTopLevel*(c: PContext): bool {.inline.} = 
+proc isTopLevel*(c: PContext): bool {.inline.} =
   result = c.currentScope.depthLevel <= 2
 
 proc experimentalMode*(c: PContext): bool {.inline.} =
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index a56de1028..bc7303266 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1133,19 +1133,20 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
   ## returns nil if not a built-in subscript operator; also called for the
   ## checking of assignments
   if sonsLen(n) == 1:
-    var x = semDeref(c, n)
+    let x = semDeref(c, n)
     if x == nil: return nil
     result = newNodeIT(nkDerefExpr, x.info, x.typ)
     result.add(x[0])
     return
   checkMinSonsLen(n, 2)
   n.sons[0] = semExprWithType(c, n.sons[0])
-  var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
+  let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
   case arr.kind
   of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
      tyCString:
     if n.len != 2: return nil
     n.sons[0] = makeDeref(n.sons[0])
+    c.p.bracketExpr = n.sons[0]
     for i in countup(1, sonsLen(n) - 1):
       n.sons[i] = semExprWithType(c, n.sons[i],
                                   flags*{efInTypeof, efDetermineType})
@@ -1166,6 +1167,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
   of tyTuple:
     checkSonsLen(n, 2)
     n.sons[0] = makeDeref(n.sons[0])
+    c.p.bracketExpr = n.sons[0]
     # [] operator for tuples requires constant expression:
     n.sons[1] = semConstExpr(c, n.sons[1])
     if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
@@ -1176,13 +1178,16 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     else:
       localError(n.info, errIndexTypesDoNotMatch)
     result = n
-  else: discard
+  else:
+    c.p.bracketExpr = n.sons[0]
 
 proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
+  let oldBracketExpr = c.p.bracketExpr
   result = semSubscript(c, n, flags)
   if result == nil:
     # overloaded [] operator:
     result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
+  c.p.bracketExpr = oldBracketExpr
 
 proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
   var id = considerQuotedIdent(a[1])
@@ -1249,11 +1254,15 @@ proc semAsgn(c: PContext, n: PNode): PNode =
   of nkBracketExpr:
     # a[i] = x
     # --> `[]=`(a, i, x)
+    let oldBracketExpr = c.p.bracketExpr
     a = semSubscript(c, a, {efLValue})
     if a == nil:
       result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
       add(result, n[1])
-      return semExprNoType(c, result)
+      result = semExprNoType(c, result)
+      c.p.bracketExpr = oldBracketExpr
+      return result
+    c.p.bracketExpr = oldBracketExpr
   of nkCurlyExpr:
     # a{i} = x -->  `{}=`(a, i, x)
     result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 3c9784152..93170e530 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -130,6 +130,11 @@ proc semLocals(c: PContext, n: PNode): PNode =
         result.add(a)
 
 proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
+
+proc isStrangeArray(t: PType): bool =
+  let t = t.skipTypes(abstractInst)
+  result = t.kind == tyArray and t.firstOrd != 0
+
 proc magicsAfterOverloadResolution(c: PContext, n: PNode,
                                    flags: TExprFlags): PNode =
   case n[0].sym.magic
@@ -153,4 +158,29 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   of mProcCall:
     result = n
     result.typ = n[1].typ
+  of mRoof:
+    # error correction:
+    result = n.sons[1]
+    if c.p.bracketExpr.isNil:
+      localError(n.info, "no surrounding array access context for '^'")
+    elif c.p.bracketExpr.checkForSideEffects != seNoSideEffect:
+      localError(n.info, "invalid context for '^' as '$#' has side effects" %
+        renderTree(c.p.bracketExpr))
+    elif c.p.bracketExpr.typ.isStrangeArray:
+      localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" %
+        renderTree(c.p.bracketExpr))
+    else:
+      # ^x  is rewritten to: len(a)-x
+      let lenExpr = newNodeI(nkCall, n.info)
+      lenExpr.add newIdentNode(getIdent"len", n.info)
+      lenExpr.add c.p.bracketExpr
+      let lenExprB = semExprWithType(c, lenExpr)
+      if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ):
+        localError(n.info, "'$#' has to be of an ordinal type for '^'" %
+          renderTree(lenExpr))
+      else:
+        result = newNodeIT(nkCall, n.info, getSysType(tyInt))
+        result.add newSymNode(createMagic("-", mSubI), n.info)
+        result.add lenExprB
+        result.add n.sons[1]
   else: result = n
diff --git a/compiler/types.nim b/compiler/types.nim
index f4ac4daea..153c26a42 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -590,7 +590,7 @@ proc firstOrd(t: PType): BiggestInt =
   of tyUInt..tyUInt64: result = 0
   of tyEnum:
     # if basetype <> nil then return firstOrd of basetype
-    if (sonsLen(t) > 0) and (t.sons[0] != nil):
+    if sonsLen(t) > 0 and t.sons[0] != nil:
       result = firstOrd(t.sons[0])
     else:
       assert(t.n.sons[0].kind == nkSym)
diff --git a/lib/system.nim b/lib/system.nim
index 75d1d40a6..59faa83fb 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -222,8 +222,8 @@ type
   set*{.magic: "Set".}[T]  ## Generic type to construct bit sets.
 
 type
-  Slice* {.final, pure.}[T] = object ## builtin slice type
-    a*, b*: T                        ## the bounds
+  Slice*[T] = object ## builtin slice type
+    a*, b*: T        ## the bounds
 
 when defined(nimalias):
   {.deprecated: [TSlice: Slice].}
@@ -3247,4 +3247,12 @@ proc procCall*(x: expr) {.magic: "ProcCall".} =
   ##   procCall someMethod(a, b)
   discard
 
+proc `^`*(x: int): int {.noSideEffect, magic: "Roof".} =
+  ## builtin `roof`:idx: operator that can be used for convenient array access.
+  ## ``a[^x]`` is rewritten to ``a[a.len-x]``. However currently the ``a``
+  ## expression must not have side effects for this to compile. Note that since
+  ## this is a builtin, it automatically works for all kinds of
+  ## overloaded ``[]`` or ``[]=`` accessors.
+  discard
+
 {.pop.} #{.push warning[GcMem]: off.}
diff --git a/tests/array/troof1.nim b/tests/array/troof1.nim
new file mode 100644
index 000000000..96669a121
--- /dev/null
+++ b/tests/array/troof1.nim
@@ -0,0 +1,36 @@
+discard """
+  output: '''@[2, 3, 4]321
+9.0 4.0
+(a: 1.0, b: 2.0, c: 8.0)2.0'''
+"""
+
+proc foo[T](x, y: T): T = x
+
+var a = @[1, 2, 3, 4]
+var b: array[3, array[2, float]] = [[1.0,2], [3.0,4], [8.0,9]]
+echo a[1.. ^1], a[^2], a[^3], a[^4]
+echo b[^1][^1], " ", (b[^2]).foo(b[^1])[^1]
+
+type
+  MyArray = object
+    a, b, c: float
+
+var
+  ma = MyArray(a: 1.0, b: 2.0, c: 3.0)
+
+proc len(x: MyArray): int = 3
+
+proc `[]=`(x: var MyArray; idx: range[0..2]; val: float) =
+  case idx
+  of 0: x.a = val
+  of 1: x.b = val
+  of 2: x.c = val
+
+proc `[]`(x: var MyArray; idx: range[0..2]): float =
+  case idx
+  of 0: result = x.a
+  of 1: result = x.b
+  of 2: result = x.c
+
+ma[^1] = 8.0
+echo ma, ma[^2]
diff --git a/tests/array/troof2.nim b/tests/array/troof2.nim
new file mode 100644
index 000000000..d4c1a4982
--- /dev/null
+++ b/tests/array/troof2.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "invalid context for '^' as 'foo()' has side effects"
+  line: "9"
+"""
+
+proc foo(): seq[int] =
+  echo "ha"
+
+let f = foo()[^1]
+
diff --git a/tests/array/troof3.nim b/tests/array/troof3.nim
new file mode 100644
index 000000000..4b6e22223
--- /dev/null
+++ b/tests/array/troof3.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "invalid context for '^' as len!=high+1 for 'a'"
+  line: "8"
+"""
+
+var a: array[1..3, string]
+
+echo a[^1]
diff --git a/tests/array/troof4.nim b/tests/array/troof4.nim
new file mode 100644
index 000000000..7a262d9de
--- /dev/null
+++ b/tests/array/troof4.nim
@@ -0,0 +1,37 @@
+discard """
+  errormsg: "no surrounding array access context for '^'"
+  line: "37"
+"""
+
+proc foo[T](x, y: T): T = x
+
+var a = @[1, 2, 3, 4]
+var b: array[3, array[2, float]] = [[1.0,2], [3.0,4], [8.0,9]]
+echo a[1.. ^1], a[^2], a[^3], a[^4]
+echo b[^1][^1], " ", (b[^2]).foo(b[^1])[^1]
+
+type
+  MyArray = object
+    a, b, c: float
+
+var
+  ma = MyArray(a: 1.0, b: 2.0, c: 3.0)
+
+proc len(x: MyArray): int = 3
+
+proc `[]=`(x: var MyArray; idx: range[0..2]; val: float) =
+  case idx
+  of 0: x.a = val
+  of 1: x.b = val
+  of 2: x.c = val
+
+proc `[]`(x: var MyArray; idx: range[0..2]): float =
+  case idx
+  of 0: result = x.a
+  of 1: result = x.b
+  of 2: result = x.c
+
+ma[^1] = 8.0
+echo ma, ma[^2]
+
+echo(^1)
diff --git a/web/news.txt b/web/news.txt
index 08a3cd6f5..1b8ddd3ce 100644
--- a/web/news.txt
+++ b/web/news.txt
@@ -42,6 +42,22 @@ News
     structure; for immediate macro parameters ``nkCall('addr', 'x')`` is
     produced instead of ``nkAddr('x')``.
   - ``concept`` is now a keyword and is used instead of ``generic``.
+  - The ``inc``, ``dec``, ``+=``, ``-=`` builtins now produces OverflowError
+    exceptions. This means code like the following:
+
+  .. code-block:: nim
+    var x = low(T)
+    while x <= high(T):
+      echo x
+      inc x
+
+  Needs to be replaced by something like this:
+
+  .. code-block:: nim
+    var x = low(T).int
+    while x <= high(T).int:
+      echo x.T
+      inc x
 
 
   Language Additions
@@ -84,6 +100,9 @@ News
     varOrConst(x) # "var"
     varOrConst(45) # "const"
 
+  - Array and seq indexing can now use the builtin ``^`` operator to access
+    things from backwards: ``a[^1]`` is like Python's ``a[-1]``.
+
 
   Library additions
   -----------------