Repository: go
Issue: proposal: spec: treat s[-1] as equivalent to s[len(s)-1]
Labels: LanguageChange,Proposal,LanguageChangeReview

# Overview

This is a backwards-compatible language change proposal. It will not be as thorough as it could be, since I do not think it should be adopted. I am writing it up merely for future reference.

I propose to treat negative constant literals in slicing and indexing expressions as offsets from the end of the slice or array. For example:

* `s[-1]` is equivalent to `s[len(s)-1]`
* `s[:-1]` is equivalent to `s[:len(s)-1]`
* `s[:-2:-1]` is equivalent to `s[:len(s)-2:len(s)-1]`

The motivation is to improve readability. Slice and index expressions like this occur commonly when treating slices as stacks.

Consider this code from the compiler:

```go
func (f *Func) newPoset() *poset {
	if len(f.Cache.scrPoset) > 0 {
		po := f.Cache.scrPoset[len(f.Cache.scrPoset)-1]
		f.Cache.scrPoset = f.Cache.scrPoset[:len(f.Cache.scrPoset)-1]
		return po
	}
	return newPoset()
}
```

Using the proposed syntactic sugar, it would read:

```go
func (f *Func) newPoset() *poset {
	if len(f.Cache.scrPoset) > 0 {
		po := f.Cache.scrPoset[-1]
		f.Cache.scrPoset = f.Cache.scrPoset[:-1]
		return po
	}
	return newPoset()
}
```


# Scope

The proposed sugar would only apply to negative constant literals.

```go
var b [10]byte
var v int = -1

const c = -1

var (
	_ = b[v]  // unchanged: results in runtime panic
	_ = b[c]  // unchanged: results in compile time error: index bounds out of range
	_ = b[-1] // new: evaluates to b[len(b)-1]
)
```

The rationale for `b[v]` to panic at runtime is that negative indices often arise due to overflow and programmer error.

The same rationale discourages allowing constant expressions such as `b[c]` as syntactic sugar. Constant expressions can be complex, non-local, and build-tag controlled.

However, a constant literal displays clear, obvious, local intent, and overflow-free, and affords little room for programmer error.


# Order of evaluation and side-effects

Currently, the slice/array is evaluated before any index or slice indices. See https://play.golang.org/p/kTr9Az5HoDj.

This allows a natural order of evaluation for the proposal. Given `expr[-1]` or `expr[:-1]`, `expr` is evaluated exactly once, and its length is used in subsequent `len(expr)-c` calculations.


# Data

A quick-and-dirty AST parsing program examining slice expressions suggests that such expressions occur, but not with particularly high frequency.

Running over GOROOT yields that 2.51% of slice expressions could be rewritten to use this syntactic sugar. Running over my GOPATH yields 3.17%. Searching for candidate index expressions yields 0.35% and 0.91% respectively.

This is an underestimate. In many cases, for clarity, the code will already have been rewritten like:

```go
n := len(s)-1
x = s[n]
s = s[:n]
```

And all index expressions were considered candidates for this analysis, which includes map accesses and assignments.

Nevertheless, this analysis suggests that this sugar is unlikely to be transformative in how Go code is written, and therefore probably does not pull its weight.