cue/token: record File pointer in Pos

This is the first step to eliminate FileSet.

Change-Id: Ibb8a17e429d9b2dc2fda0a739520ebef40c67761
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2121
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cmd/cue/cmd/import.go b/cmd/cue/cmd/import.go
index cf6dadb..4b8422b 100644
--- a/cmd/cue/cmd/import.go
+++ b/cmd/cue/cmd/import.go
@@ -462,8 +462,8 @@
 			}
 			if idx.field.Value == nil {
 				idx.field.Value = &ast.ListLit{
-					Lbrack: token.Pos(token.NoSpace),
-					Rbrack: token.Pos(token.NoSpace),
+					Lbrack: token.NoSpace.Pos(),
+					Rbrack: token.NoSpace.Pos(),
 				}
 			}
 			list := idx.field.Value.(*ast.ListLit)
diff --git a/cue/ast/ast.go b/cue/ast/ast.go
index bd71220..43bbde1 100644
--- a/cue/ast/ast.go
+++ b/cue/ast/ast.go
@@ -518,7 +518,8 @@
 	return x.NamePos.Add(len(x.Name))
 }
 func (x *TemplateLabel) End() token.Pos { return x.Rangle }
-func (x *BasicLit) End() token.Pos      { return token.Pos(int(x.ValuePos) + len(x.Value)) }
+func (x *BasicLit) End() token.Pos      { return x.ValuePos.Add(len(x.Value)) }
+
 func (x *Interpolation) End() token.Pos { return x.Elts[len(x.Elts)-1].Pos() }
 func (x *StructLit) End() token.Pos {
 	if x.Rbrace == token.NoPos && len(x.Elts) > 0 {
@@ -580,7 +581,7 @@
 // func (s *TypeSpec) Pos() token.Pos  { return s.Name.Pos() }
 
 func (s *ImportSpec) End() token.Pos {
-	if s.EndPos != 0 {
+	if s.EndPos != token.NoPos {
 		return s.EndPos
 	}
 	return s.Path.End()
diff --git a/cue/binop.go b/cue/binop.go
index 452aa64..11cb05a 100644
--- a/cue/binop.go
+++ b/cue/binop.go
@@ -152,7 +152,7 @@
 		}
 		return
 	}
-	src := binSrc(0, opUnify, x.val, y.val)
+	src := binSrc(token.NoPos, opUnify, x.val, y.val)
 	d.add(ctx, binOp(ctx, src, opUnify, x.val, y.val), mark && x.mark && y.mark)
 }
 
diff --git a/cue/errors.go b/cue/errors.go
index fe8ed52..018e267 100644
--- a/cue/errors.go
+++ b/cue/errors.go
@@ -91,7 +91,7 @@
 func appendPositions(pos []token.Position, fset *token.FileSet, src source) []token.Position {
 	if src != nil {
 		if p := src.Pos(); p != token.NoPos {
-			if p >= sharedOffset {
+			if p.Offset() >= sharedOffset {
 				fset = sharedIndex.fset
 			}
 			return append(pos, fset.Position(src.Pos()))
@@ -124,7 +124,7 @@
 func appendLocations(locs []string, fset *token.FileSet, src source) []string {
 	if src != nil {
 		if p := src.Pos(); p != token.NoPos {
-			if p >= sharedOffset {
+			if p.Offset() >= sharedOffset {
 				fset = sharedIndex.fset
 			}
 			return append(locs, fset.Position(src.Pos()).String())
diff --git a/cue/export.go b/cue/export.go
index a61131a..6adf44e 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -263,7 +263,7 @@
 				if p.mode.omitOptional || p.mode.concrete {
 					continue
 				}
-				f.Optional = 1
+				f.Optional = token.NoSpace.Pos()
 			}
 			if a.feature&hidden != 0 && p.mode.concrete && p.mode.omitHidden {
 				continue
@@ -297,7 +297,7 @@
 					}
 					opt := token.NoPos
 					if yield.opt {
-						opt = 1 // anything but token.NoPos
+						opt = token.NoSpace.Pos() // anything but token.NoPos
 					}
 					f := &ast.Field{
 						Label:    label,
diff --git a/cue/format/format_test.go b/cue/format/format_test.go
index 801c975..ea4010f 100644
--- a/cue/format/format_test.go
+++ b/cue/format/format_test.go
@@ -243,7 +243,7 @@
 		Decls: []ast.Decl{
 			&ast.EmitDecl{
 				Expr: &ast.BasicLit{
-					ValuePos: token.Pos(token.NoSpace),
+					ValuePos: token.NoSpace.Pos(),
 					Value:    "1",
 				},
 			},
diff --git a/cue/go.go b/cue/go.go
index b2610b6..aad63bb 100644
--- a/cue/go.go
+++ b/cue/go.go
@@ -25,6 +25,7 @@
 	"sync"
 
 	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/cue/token"
 	"cuelang.org/go/internal"
 	"github.com/cockroachdb/apd"
 )
@@ -504,7 +505,7 @@
 					// Instead of unifying with the existing type, we substitute
 					// with the constraints from the tags. The type constraints
 					// will be implied when unified with a concrete value.
-					obj.arcs[i].v = mkBin(ctx, 0, opUnify, a.v, v)
+					obj.arcs[i].v = mkBin(ctx, token.NoPos, opUnify, a.v, v)
 				}
 			}
 		}
diff --git a/cue/parser/error_test.go b/cue/parser/error_test.go
index 5bfff87..c6afecf 100644
--- a/cue/parser/error_test.go
+++ b/cue/parser/error_test.go
@@ -112,7 +112,7 @@
 			} else {
 				l = len(tok.String())
 			}
-			here = prev + token.Pos(l)
+			here = prev.Add(l)
 		}
 	}
 }
diff --git a/cue/parser/import.go b/cue/parser/import.go
index ce95246..d5885ab 100644
--- a/cue/parser/import.go
+++ b/cue/parser/import.go
@@ -164,6 +164,8 @@
 
 type byCommentPos []*ast.CommentGroup
 
-func (x byCommentPos) Len() int           { return len(x) }
-func (x byCommentPos) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
-func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() }
+func (x byCommentPos) Len() int      { return len(x) }
+func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x byCommentPos) Less(i, j int) bool {
+	return x[i].Pos().Before(x[j].Pos())
+}
diff --git a/cue/parser/parser.go b/cue/parser/parser.go
index e8ce7a5..1baf33e 100644
--- a/cue/parser/parser.go
+++ b/cue/parser/parser.go
@@ -446,7 +446,7 @@
 				p.syncCnt++
 				return
 			}
-			if p.pos > p.syncPos {
+			if p.syncPos.Before(p.pos) {
 				p.syncPos = p.pos
 				p.syncCnt = 0
 				return
@@ -475,7 +475,7 @@
 func (p *parser) safePos(pos token.Pos) (res token.Pos) {
 	defer func() {
 		if recover() != nil {
-			res = token.Pos(p.file.Base() + p.file.Size()) // EOF position
+			res = p.file.Pos(p.file.Base()+p.file.Size(), pos.RelPos()) // EOF position
 		}
 	}()
 	_ = p.file.Offset(pos) // trigger a panic if position is out-of-range
@@ -969,7 +969,7 @@
 	if clauses := p.parseComprehensionClauses(); clauses != nil {
 		var expr ast.Expr
 		if len(elts) != 1 {
-			p.error(lbrack+1, "list comprehension must have exactly one element")
+			p.error(lbrack.Add(1), "list comprehension must have exactly one element")
 		}
 		if len(elts) > 0 {
 			expr = elts[0]
diff --git a/cue/token/position.go b/cue/token/position.go
index f5ec34f..7f58a12 100644
--- a/cue/token/position.go
+++ b/cue/token/position.go
@@ -77,13 +77,16 @@
 // to comparing the respective source file offsets. If p and q are in different
 // files, p < q is true if the file implied by p was added to the respective
 // file set before the file implied by cue.
-type Pos int
+type Pos struct {
+	file   *File
+	offset int
+}
 
 // NoPos is the zero value for Pos; there is no file and line information
 // associated with it, and NoPos().IsValid() is false. NoPos is always
 // smaller than any other Pos value. The corresponding Position value
 // for NoPos is the zero value for Position.
-const NoPos Pos = 0
+var NoPos = Pos{}
 
 // RelPos indicates the relative position of token to the previous token.
 type RelPos int
@@ -108,8 +111,8 @@
 	// NewSection means there are two or more newlines after this token.
 	NewSection
 
-	relMask  Pos = 0xf
-	relShift     = 4
+	relMask  = 0xf
+	relShift = 4
 )
 
 var relNames = []string{
@@ -118,14 +121,28 @@
 
 func (p RelPos) String() string { return relNames[p] }
 
+func (p RelPos) Pos() Pos {
+	return Pos{nil, int(p)}
+}
+
 // HasRelPos repors whether p has a relative position.
 func (p Pos) HasRelPos() bool {
-	return p&relMask != 0
+	return p.offset&relMask != 0
 
 }
 
+func (p Pos) Before(q Pos) bool {
+	return p.file == q.file && p.Offset() < q.Offset()
+}
+
+// Offset reports the byte offset relative to the file.
+func (p Pos) Offset() int {
+	return p.offset >> relShift
+}
+
+// Add creates a new position relative to the p offset by n.
 func (p Pos) Add(n int) Pos {
-	return p + toPos(index(n))
+	return Pos{p.file, p.offset + toPos(index(n))}
 }
 
 // IsValid reports whether the position is valid.
@@ -140,19 +157,19 @@
 }
 
 func (p Pos) WithRel(rel RelPos) Pos {
-	return p&^relMask | Pos(rel)
+	return Pos{p.file, p.offset&^relMask | int(rel)}
 }
 
 func (p Pos) RelPos() RelPos {
-	return RelPos(p & relMask)
+	return RelPos(p.offset & relMask)
 }
 
 func (p Pos) index() index {
-	return index(p) >> relShift
+	return index(p.offset) >> relShift
 }
 
-func toPos(x index) Pos {
-	return (Pos(x) << relShift)
+func toPos(x index) int {
+	return (int(x) << relShift)
 }
 
 // -----------------------------------------------------------------------------
@@ -316,7 +333,7 @@
 	if index(offset) > f.size {
 		panic("illegal file offset")
 	}
-	return toPos(f.base+index(offset)) + Pos(rel)
+	return Pos{f, toPos(f.base+index(offset)) + int(rel)}
 }
 
 // Offset returns the offset for the given file position p;
diff --git a/cue/token/position_test.go b/cue/token/position_test.go
index 906cac0..39ca970 100644
--- a/cue/token/position_test.go
+++ b/cue/token/position_test.go
@@ -81,7 +81,7 @@
 			t.Errorf("%s, Offset: got offset %d; want %d", f.Name(), offs2, offs)
 		}
 		line, col := linecol(lines, offs)
-		msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p)
+		msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p.offset)
 		checkPos(t, msg, f.Position(f.Pos(offs, 0)), Position{f.Name(), offs, line, col})
 		checkPos(t, msg, fset.Position(p), Position{f.Name(), offs, line, col})
 	}
@@ -168,7 +168,7 @@
 	for offs := 0; offs <= f.Size(); offs++ {
 		p := f.Pos(offs, 0)
 		_, col := linecol(lines, offs)
-		msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p)
+		msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p.offset)
 		checkPos(t, msg, f.Position(f.Pos(offs, 0)), Position{"bar", offs, 42, col})
 		checkPos(t, msg, fset.Position(p), Position{"bar", offs, 42, col})
 	}
@@ -204,7 +204,7 @@
 	for _, test := range tests {
 		fset.AddFile(test.filename, fset.Base(), test.size)
 	}
-	if f := fset.File(toPos(index(fset.Base()))); f != nil {
+	if f := fset.File(Pos{nil, toPos(index(fset.Base()))}); f != nil {
 		t.Errorf("got %v, want nil", f)
 	}
 }
@@ -217,7 +217,7 @@
 		fset.AddFile(test.filename, fset.Base(), test.size)
 	}
 	for file, pos := range offsets {
-		f := fset.File(toPos(pos))
+		f := fset.File(Pos{nil, toPos(pos)})
 		if f.Name() != file {
 			t.Errorf("got %q at position %d, want %q", f.Name(), pos, file)
 		}
@@ -239,7 +239,7 @@
 		stop.Add(1)
 		go func() {
 			for i := 0; i < 1000; i++ {
-				fset.Position(Pos(r.Int31n(max)))
+				fset.Position(Pos{nil, int(r.Int31n(max))})
 			}
 			stop.Done()
 		}()
diff --git a/internal/protobuf/parse.go b/internal/protobuf/parse.go
index 19eb4ec..b81bd5c 100644
--- a/internal/protobuf/parse.go
+++ b/internal/protobuf/parse.go
@@ -386,7 +386,7 @@
 
 		if x.Repeated {
 			f.Value = &ast.ListLit{
-				Ellipsis: token.Pos(token.NoSpace),
+				Ellipsis: token.NoSpace.Pos(),
 				Type:     f.Value,
 			}
 		}
@@ -571,7 +571,7 @@
 	p.addTag(f, o.tags)
 
 	if !o.required {
-		f.Optional = token.Pos(token.NoSpace)
+		f.Optional = token.NoSpace.Pos()
 	}
 	return f
 }
@@ -606,7 +606,7 @@
 			addComments(constraint, 1, o.Comment, o.InlineComment)
 			p.message.Elts = append(p.message.Elts, constraint)
 			if !p.required {
-				constraint.Optional = token.Pos(token.NoSpace)
+				constraint.Optional = token.NoSpace.Pos()
 			}
 
 		default:
diff --git a/internal/protobuf/util.go b/internal/protobuf/util.go
index e001fa8..980a7d3 100644
--- a/internal/protobuf/util.go
+++ b/internal/protobuf/util.go
@@ -38,8 +38,8 @@
 }
 
 var (
-	newline    = token.Pos(token.Newline)
-	newSection = token.Pos(token.NewSection)
+	newline    = token.Newline.Pos()
+	newSection = token.NewSection.Pos()
 )
 
 func addComments(f ast.Node, i int, doc, inline *proto.Comment) bool {
diff --git a/internal/third_party/yaml/decode.go b/internal/third_party/yaml/decode.go
index bc42da9..fc91a8e 100644
--- a/internal/third_party/yaml/decode.go
+++ b/internal/third_party/yaml/decode.go
@@ -322,7 +322,7 @@
 		}
 		// fp := d.p.info.Pos(c.mark.index, 0)
 		comments = append(comments, &ast.Comment{
-			token.Pos(c.pos),
+			c.pos.Pos(),
 			"//" + c.text[1:],
 		})
 		d.p.parser.comments = d.p.parser.comments[1:]
@@ -343,7 +343,7 @@
 	c := d.p.parser.comments[0]
 	if c.mark.index == m.index {
 		comment := &ast.Comment{
-			token.Pos(c.pos),
+			c.pos.Pos(),
 			// d.p.info.Pos(m.index+1, 0),
 			"//" + c.text[1:],
 		}
@@ -374,7 +374,7 @@
 func (d *decoder) ident(n *node, name string) *ast.Ident {
 	return &ast.Ident{
 		// NamePos: d.pos(n.startPos),
-		NamePos: token.Pos(d.p.parser.relPos()),
+		NamePos: d.p.parser.relPos().Pos(),
 		Name:    name,
 	}
 }
@@ -424,7 +424,7 @@
 	case yaml_TIMESTAMP_TAG:
 		return &ast.BasicLit{
 			// ValuePos: d.start(n),
-			ValuePos: token.Pos(d.p.parser.relPos()),
+			ValuePos: d.p.parser.relPos().Pos(),
 			Kind:     token.STRING,
 			Value:    strconv.Quote(n.value),
 		}
@@ -432,7 +432,7 @@
 	case yaml_STR_TAG:
 		return &ast.BasicLit{
 			// ValuePos: d.start(n),
-			ValuePos: token.Pos(d.p.parser.relPos()),
+			ValuePos: d.p.parser.relPos().Pos(),
 			Kind:     token.STRING,
 			Value:    d.quoteString(n.value),
 		}
@@ -443,7 +443,7 @@
 		buf[len(buf)-1] = '\''
 		return &ast.BasicLit{
 			// ValuePos: d.start(n),
-			ValuePos: token.Pos(d.p.parser.relPos()),
+			ValuePos: d.p.parser.relPos().Pos(),
 			Kind:     token.STRING,
 			Value:    string(buf),
 		}
@@ -487,11 +487,11 @@
 	}
 	err := &ast.BottomLit{
 		// Bottom: d.pos(n.startPos)
-		Bottom: token.Pos(d.p.parser.relPos()),
+		Bottom: d.p.parser.relPos().Pos(),
 	}
 	comment := &ast.Comment{
 		// Slash: d.start(n),
-		Slash: token.Pos(token.Blank),
+		Slash: token.Blank.Pos(),
 		Text:  "// " + d.terror(n, tag),
 	}
 	err.AddComment(&ast.CommentGroup{
@@ -522,7 +522,7 @@
 	}
 stringLabel:
 	return &ast.BasicLit{
-		ValuePos: token.Pos(d.p.parser.relPos()),
+		ValuePos: d.p.parser.relPos().Pos(),
 		// ValuePos: d.start(n),
 		Kind:  token.STRING,
 		Value: strconv.Quote(n.value),
@@ -535,15 +535,15 @@
 		minuses++
 	}
 	expr = &ast.BasicLit{
-		// ValuePos: d.start(n) + token.Pos(minuses),
-		ValuePos: token.Pos(d.p.parser.relPos()),
+		// ValuePos: d.start(n) + minuses.Pos(),
+		ValuePos: d.p.parser.relPos().Pos(),
 		Kind:     kind,
 		Value:    val,
 	}
 	if minuses > 0 {
 		expr = &ast.UnaryExpr{
 			// OpPos: d.start(n),
-			OpPos: token.Pos(d.p.parser.relPos()),
+			OpPos: d.p.parser.relPos().Pos(),
 			Op:    token.SUB,
 			X:     expr,
 		}