cmd/compile:  update the export version for generics

Bump the export version to a new value iexportVersionGo1_18 (2). This
will give a better error message when old compilers/tools encounter the
new export format (that includes parameterized types and functions).

We are also making a breaking change in the format:
 - a 'kind' byte is added to constant values

Also updated tinter() to pass the implicit bit through during type
substitution.

Tested that all tests still pass if the iexportVersionCurrent is changed
back to 1 in typecheck/iexport.go, iimporter/iimport.go, and
gcimporter/iimport.go

Updates #47654

Change-Id: I1dbeb167a97f6c7e0b7e0c011d6bada5db312b36
Reviewed-on: https://go-review.googlesource.com/c/go/+/357049
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Trust: Dan Scales <danscales@google.com>
diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go
index cbc7853..d04ef5c 100644
--- a/src/cmd/compile/internal/importer/iimport.go
+++ b/src/cmd/compile/internal/importer/iimport.go
@@ -43,12 +43,12 @@
 
 // Keep this in sync with constants in iexport.go.
 const (
-	iexportVersionGo1_11 = 0
-	iexportVersionPosCol = 1
-	// TODO: before release, change this back to 2.
-	iexportVersionGenerics = iexportVersionPosCol
+	iexportVersionGo1_11   = 0
+	iexportVersionPosCol   = 1
+	iexportVersionGenerics = 1 // probably change to 2 before release
+	iexportVersionGo1_18   = 2
 
-	iexportVersionCurrent = iexportVersionGenerics
+	iexportVersionCurrent = 2
 )
 
 type ident struct {
@@ -99,13 +99,9 @@
 
 	version = int64(r.uint64())
 	switch version {
-	case /* iexportVersionGenerics, */ iexportVersionPosCol, iexportVersionGo1_11:
+	case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
 	default:
-		if version > iexportVersionGenerics {
-			errorf("unstable iexport format version %d, just rebuild compiler and std library", version)
-		} else {
-			errorf("unknown iexport format version %d", version)
-		}
+		errorf("unknown iexport format version %d", version)
 	}
 
 	sLen := int64(r.uint64())
@@ -374,7 +370,19 @@
 		id := ident{r.currPkg.Name(), name}
 		r.p.tparamIndex[id] = t
 
-		t.SetConstraint(r.typ())
+		var implicit bool
+		if r.p.exportVersion >= iexportVersionGo1_18 {
+			implicit = r.bool()
+		}
+		constraint := r.typ()
+		if implicit {
+			iface, _ := constraint.(*types2.Interface)
+			if iface == nil {
+				errorf("non-interface constraint marked implicit")
+			}
+			iface.MarkImplicit()
+		}
+		t.SetConstraint(constraint)
 
 	case 'V':
 		typ := r.typ()
@@ -392,6 +400,10 @@
 
 func (r *importReader) value() (typ types2.Type, val constant.Value) {
 	typ = r.typ()
+	if r.p.exportVersion >= iexportVersionGo1_18 {
+		// TODO: add support for using the kind
+		_ = constant.Kind(r.int64())
+	}
 
 	switch b := typ.Underlying().(*types2.Basic); b.Info() & types2.IsConstType {
 	case types2.IsBoolean:
diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go
index 46865ba..6057000 100644
--- a/src/cmd/compile/internal/typecheck/iexport.go
+++ b/src/cmd/compile/internal/typecheck/iexport.go
@@ -254,15 +254,16 @@
 // Current indexed export format version. Increase with each format change.
 // 0: Go1.11 encoding
 // 1: added column details to Pos
-// 2: added information for generic function/types (currently unstable)
+// 2: added information for generic function/types.  The export of non-generic
+// functions/types remains largely backward-compatible.  Breaking changes include:
+//    - a 'kind' byte is added to constant values
 const (
-	iexportVersionGo1_11 = 0
-	iexportVersionPosCol = 1
-	// TODO: before release, change this back to 2.  Kept at previous version
-	// for now (for testing).
-	iexportVersionGenerics = iexportVersionPosCol
+	iexportVersionGo1_11   = 0
+	iexportVersionPosCol   = 1
+	iexportVersionGenerics = 1 // probably change to 2 before release
+	iexportVersionGo1_18   = 2
 
-	iexportVersionCurrent = iexportVersionGenerics
+	iexportVersionCurrent = 2
 )
 
 // predeclReserved is the number of type offsets reserved for types
@@ -561,6 +562,10 @@
 			// A typeparam has a name, and has a type bound rather
 			// than an underlying type.
 			w.pos(n.Pos())
+			if iexportVersionCurrent >= iexportVersionGo1_18 {
+				implicit := n.Type().Bound().IsImplicit()
+				w.bool(implicit)
+			}
 			w.typ(n.Type().Bound())
 			break
 		}
@@ -1137,17 +1142,24 @@
 
 func (w *exportWriter) value(typ *types.Type, v constant.Value) {
 	w.typ(typ)
+
+	if iexportVersionCurrent >= iexportVersionGo1_18 {
+		w.int64(int64(v.Kind()))
+	}
+
 	var kind constant.Kind
 	var valType *types.Type
 
 	if typ.IsTypeParam() {
-		// A constant will have a TYPEPARAM type if it appears in a place
-		// where it must match that typeparam type (e.g. in a binary
-		// operation with a variable of that typeparam type). If so, then
-		// we must write out its actual constant kind as well, so its
-		// constant val can be read in properly during import.
 		kind = v.Kind()
-		w.int64(int64(kind))
+		if iexportVersionCurrent < iexportVersionGo1_18 {
+			// A constant will have a TYPEPARAM type if it appears in a place
+			// where it must match that typeparam type (e.g. in a binary
+			// operation with a variable of that typeparam type). If so, then
+			// we must write out its actual constant kind as well, so its
+			// constant val can be read in properly during import.
+			w.int64(int64(kind))
+		}
 
 		switch kind {
 		case constant.Int:
diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go
index fcfadc1..7c6c23e 100644
--- a/src/cmd/compile/internal/typecheck/iimport.go
+++ b/src/cmd/compile/internal/typecheck/iimport.go
@@ -118,13 +118,9 @@
 
 	version := ird.uint64()
 	switch version {
-	case /* iexportVersionGenerics, */ iexportVersionPosCol, iexportVersionGo1_11:
+	case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
 	default:
-		if version > iexportVersionGenerics {
-			base.Errorf("import %q: unstable export format version %d, just recompile", pkg.Path, version)
-		} else {
-			base.Errorf("import %q: unknown export format version %d", pkg.Path, version)
-		}
+		base.Errorf("import %q: unknown export format version %d", pkg.Path, version)
 		base.ErrorExit()
 	}
 
@@ -408,8 +404,15 @@
 		sym.Def = nname
 		nname.SetType(t)
 		t.SetNod(nname)
-
-		t.SetBound(r.typ())
+		implicit := false
+		if r.p.exportVersion >= iexportVersionGo1_18 {
+			implicit = r.bool()
+		}
+		bound := r.typ()
+		if implicit {
+			bound.MarkImplicit()
+		}
+		t.SetBound(bound)
 		return nname
 
 	case 'V':
@@ -429,10 +432,17 @@
 	var kind constant.Kind
 	var valType *types.Type
 
-	if typ.IsTypeParam() {
-		// If a constant had a typeparam type, then we wrote out its
-		// actual constant kind as well.
+	if r.p.exportVersion >= iexportVersionGo1_18 {
+		// TODO: add support for using the kind in the non-typeparam case.
 		kind = constant.Kind(r.int64())
+	}
+
+	if typ.IsTypeParam() {
+		if r.p.exportVersion < iexportVersionGo1_18 {
+			// If a constant had a typeparam type, then we wrote out its
+			// actual constant kind as well.
+			kind = constant.Kind(r.int64())
+		}
 		switch kind {
 		case constant.Int:
 			valType = types.Types[types.TINT64]
diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go
index 9ebd8f1..96e120f 100644
--- a/src/cmd/compile/internal/typecheck/subr.go
+++ b/src/cmd/compile/internal/typecheck/subr.go
@@ -1390,7 +1390,7 @@
 		}
 	}
 	if newfields != nil {
-		return types.NewInterface(t.Pkg(), newfields, false)
+		return types.NewInterface(t.Pkg(), newfields, t.IsImplicit())
 	}
 	return t
 }
diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go
index ec17fe8..c3efbc9 100644
--- a/src/cmd/compile/internal/types/type.go
+++ b/src/cmd/compile/internal/types/type.go
@@ -1884,6 +1884,12 @@
 	return t.extra.(*Interface).implicit
 }
 
+// MarkImplicit marks the interface as implicit.
+func (t *Type) MarkImplicit() {
+	t.wantEtype(TINTER)
+	t.extra.(*Interface).implicit = true
+}
+
 // NewUnion returns a new union with the specified set of terms (types). If
 // tildes[i] is true, then terms[i] represents ~T, rather than just T.
 func NewUnion(terms []*Type, tildes []bool) *Type {
diff --git a/src/go/internal/gcimporter/iimport.go b/src/go/internal/gcimporter/iimport.go
index 0771fa3..49ea643 100644
--- a/src/go/internal/gcimporter/iimport.go
+++ b/src/go/internal/gcimporter/iimport.go
@@ -44,12 +44,12 @@
 
 // Keep this in sync with constants in iexport.go.
 const (
-	iexportVersionGo1_11 = 0
-	iexportVersionPosCol = 1
-	// TODO: before release, change this back to 2.
-	iexportVersionGenerics = iexportVersionPosCol
+	iexportVersionGo1_11   = 0
+	iexportVersionPosCol   = 1
+	iexportVersionGenerics = 1 // probably change to 2 before release
+	iexportVersionGo1_18   = 2
 
-	iexportVersionCurrent = iexportVersionGenerics
+	iexportVersionCurrent = 2
 )
 
 type ident struct {
@@ -98,13 +98,9 @@
 
 	version = int64(r.uint64())
 	switch version {
-	case /* iexportVersionGenerics, */ iexportVersionPosCol, iexportVersionGo1_11:
+	case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
 	default:
-		if version > iexportVersionGenerics {
-			errorf("unstable iexport format version %d, just rebuild compiler and std library", version)
-		} else {
-			errorf("unknown iexport format version %d", version)
-		}
+		errorf("unknown iexport format version %d", version)
 	}
 
 	sLen := int64(r.uint64())
@@ -367,7 +363,19 @@
 		id := ident{r.currPkg.Name(), name}
 		r.p.tparamIndex[id] = t
 
-		t.SetConstraint(r.typ())
+		var implicit bool
+		if r.p.exportVersion >= iexportVersionGo1_18 {
+			implicit = r.bool()
+		}
+		constraint := r.typ()
+		if implicit {
+			iface, _ := constraint.(*types.Interface)
+			if iface == nil {
+				errorf("non-interface constraint marked implicit")
+			}
+			iface.MarkImplicit()
+		}
+		t.SetConstraint(constraint)
 
 	case 'V':
 		typ := r.typ()
@@ -385,6 +393,10 @@
 
 func (r *importReader) value() (typ types.Type, val constant.Value) {
 	typ = r.typ()
+	if r.p.exportVersion >= iexportVersionGo1_18 {
+		// TODO: add support for using the kind
+		_ = constant.Kind(r.int64())
+	}
 
 	switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
 	case types.IsBoolean: