// for an imported package by overloading writeNewExportFunc, then
// that payload will be mapped into memory and passed to
// newReadImportFunc.
-var newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Environment, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
+var newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Context, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
panic("unexpected new export data payload")
}
type gcimports struct {
- env *types2.Environment
+ env *types2.Context
packages map[string]*types2.Package
}
// readImportFile reads the import file for the given package path and
// returns its types.Pkg representation. If packages is non-nil, the
// types2.Package representation is also returned.
-func readImportFile(path string, target *ir.Package, env *types2.Environment, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) {
+func readImportFile(path string, target *ir.Package, env *types2.Context, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) {
path, err = resolveImportPath(path)
if err != nil {
return
}
// typechecking
- env := types2.NewEnvironment()
+ env := types2.NewContext()
importer := gcimports{
env: env,
packages: map[string]*types2.Package{"unsafe": types2.Unsafe},
}
conf := types2.Config{
- Environment: env,
+ Context: env,
GoVersion: base.Flag.Lang,
IgnoreLabels: true, // parser already checked via syntax.CheckBranches mode
CompilerErrorMessages: true, // use error strings matching existing compiler errors
type pkgReader2 struct {
pkgDecoder
- env *types2.Environment
+ env *types2.Context
imports map[string]*types2.Package
posBases []*syntax.PosBase
typs []types2.Type
}
-func readPackage2(env *types2.Environment, imports map[string]*types2.Package, input pkgDecoder) *types2.Package {
+func readPackage2(env *types2.Context, imports map[string]*types2.Package, input pkgDecoder) *types2.Package {
pr := pkgReader2{
pkgDecoder: input,
base.Errorf("cannot use -G and -d=quirksmode together")
}
- newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Environment, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
+ newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Context, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
pr := newPkgDecoder(pkg1.Path, data)
// Read package descriptors for both types2 and compiler backend.
// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
- // Environment is the environment used for resolving global
- // identifiers. If nil, the type checker will initialize this
- // field with a newly created environment.
- Environment *Environment
+ // Context is the context used for resolving global identifiers. If nil, the
+ // type checker will initialize this field with a newly created context.
+ Context *Context
// GoVersion describes the accepted Go language version. The string
// must follow the format "go%d.%d" (e.g. "go1.12") or ist must be
conf = new(Config)
}
- // make sure we have an environment
- if conf.Environment == nil {
- conf.Environment = NewEnvironment()
+ // make sure we have a context
+ if conf.Context == nil {
+ conf.Context = NewContext()
}
// make sure we have an info struct
"sync"
)
-// An Environment is an opaque type checking environment. It may be used to
-// share identical type instances across type-checked packages or calls to
+// An Context is an opaque type checking context. It may be used to share
+// identical type instances across type-checked packages or calls to
// Instantiate.
//
// It is safe for concurrent use.
-type Environment struct {
+type Context struct {
mu sync.Mutex
typeMap map[string]*Named // type hash -> instance
nextID int // next unique ID
seen map[*Named]int // assigned unique IDs
}
-// NewEnvironment creates a new Environment.
-func NewEnvironment() *Environment {
- return &Environment{
+// NewContext creates a new Context.
+func NewContext() *Context {
+ return &Context{
typeMap: make(map[string]*Named),
seen: make(map[*Named]int),
}
// type hash: types that are identical produce identical string representations.
// If typ is a *Named type and targs is not empty, typ is printed as if it were
// instantiated with targs. The result is guaranteed to not contain blanks (" ").
-func (env *Environment) TypeHash(typ Type, targs []Type) string {
- assert(env != nil)
+func (ctxt *Context) TypeHash(typ Type, targs []Type) string {
+ assert(ctxt != nil)
assert(typ != nil)
var buf bytes.Buffer
- h := newTypeHasher(&buf, env)
+ h := newTypeHasher(&buf, ctxt)
if named, _ := typ.(*Named); named != nil && len(targs) > 0 {
// Don't use WriteType because we need to use the provided targs
// and not any targs that might already be with the *Named type.
// typeForHash returns the recorded type for the type hash h, if it exists.
// If no type exists for h and n is non-nil, n is recorded for h.
-func (env *Environment) typeForHash(h string, n *Named) *Named {
- env.mu.Lock()
- defer env.mu.Unlock()
- if existing := env.typeMap[h]; existing != nil {
+func (ctxt *Context) typeForHash(h string, n *Named) *Named {
+ ctxt.mu.Lock()
+ defer ctxt.mu.Unlock()
+ if existing := ctxt.typeMap[h]; existing != nil {
return existing
}
if n != nil {
- env.typeMap[h] = n
+ ctxt.typeMap[h] = n
}
return n
}
// idForType returns a unique ID for the pointer n.
-func (env *Environment) idForType(n *Named) int {
- env.mu.Lock()
- defer env.mu.Unlock()
- id, ok := env.seen[n]
+func (ctxt *Context) idForType(n *Named) int {
+ ctxt.mu.Lock()
+ defer ctxt.mu.Unlock()
+ id, ok := ctxt.seen[n]
if !ok {
- id = env.nextID
- env.seen[n] = id
- env.nextID++
+ id = ctxt.nextID
+ ctxt.seen[n] = id
+ ctxt.nextID++
}
return id
}
// Funcs with m.instRecv set have not yet be completed. Complete them now
// so that they have a type when objDecl exits.
if m, _ := obj.(*Func); m != nil && m.instRecv != nil {
- check.completeMethod(check.conf.Environment, m)
+ check.completeMethod(check.conf.Context, m)
}
// Checking the declaration of obj means inferring its type
}
case *Named:
- t.resolve(check.conf.Environment)
+ t.resolve(check.conf.Context)
// don't touch the type if it is from a different package or the Universe scope
// (doing so would lead to a race condition - was issue #35049)
// *Signature). Any methods attached to a *Named are simply copied; they are
// not instantiated.
//
-// If env is non-nil, it may be used to de-dupe the instance against previous
-// instances with the same identity. This functionality is implemented for
-// environments with non-nil Checkers.
+// If ctxt is non-nil, it may be used to de-dupe the instance against previous
+// instances with the same identity.
//
// If verify is set and constraint satisfaction fails, the returned error may
// be of dynamic type ArgumentError indicating which type argument did not
//
// TODO(rfindley): change this function to also return an error if lengths of
// tparams and targs do not match.
-func Instantiate(env *Environment, typ Type, targs []Type, validate bool) (Type, error) {
- inst := (*Checker)(nil).instance(nopos, typ, targs, env)
+func Instantiate(ctxt *Context, typ Type, targs []Type, validate bool) (Type, error) {
+ inst := (*Checker)(nil).instance(nopos, typ, targs, ctxt)
var err error
if validate {
}()
}
- inst := check.instance(pos, typ, targs, check.conf.Environment)
+ inst := check.instance(pos, typ, targs, check.conf.Context)
assert(len(posList) <= len(targs))
check.later(func() {
// instance creates a type or function instance using the given original type
// typ and arguments targs. For Named types the resulting instance will be
// unexpanded.
-func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, env *Environment) Type {
+func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, ctxt *Context) Type {
switch t := typ.(type) {
case *Named:
var h string
- if env != nil {
- h = env.TypeHash(t, targs)
+ if ctxt != nil {
+ h = ctxt.TypeHash(t, targs)
// typ may already have been instantiated with identical type arguments. In
// that case, re-use the existing instance.
- if named := env.typeForHash(h, nil); named != nil {
+ if named := ctxt.typeForHash(h, nil); named != nil {
return named
}
}
tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil)
named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is resolved
named.targs = NewTypeList(targs)
- named.resolver = func(env *Environment, n *Named) (*TypeParamList, Type, []*Func) {
- return expandNamed(env, n, pos)
+ named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, []*Func) {
+ return expandNamed(ctxt, n, pos)
}
- if env != nil {
- // It's possible that we've lost a race to add named to the environment.
- // In this case, use whichever instance is recorded in the environment.
- named = env.typeForHash(h, named)
+ if ctxt != nil {
+ // It's possible that we've lost a race to add named to the context.
+ // In this case, use whichever instance is recorded in the context.
+ named = ctxt.typeForHash(h, named)
}
return named
if tparams.Len() == 0 {
return typ // nothing to do (minor optimization)
}
- sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), env).(*Signature)
+ sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), ctxt).(*Signature)
// If the signature doesn't use its type parameters, subst
// will not make a copy. In that case, make a copy now (so
// we can set tparams to nil w/o causing side-effects).
// TODO(rfindley): it would be great if users could pass in a qualifier here,
// rather than falling back to verbose qualification. Maybe this can be part
- // of a the shared environment.
+ // of the shared context.
var qf Qualifier
if check != nil {
qf = check.qualifier
T := pkg.Scope().Lookup("T").Type().(*Named)
// Instantiating the same type twice should result in pointer-equivalent
// instances.
- env := NewEnvironment()
- res1, err := Instantiate(env, T, []Type{Typ[Int]}, false)
+ ctxt := NewContext()
+ res1, err := Instantiate(ctxt, T, []Type{Typ[Int]}, false)
if err != nil {
t.Fatal(err)
}
- res2, err := Instantiate(env, T, []Type{Typ[Int]}, false)
+ res2, err := Instantiate(ctxt, T, []Type{Typ[Int]}, false)
if err != nil {
t.Fatal(err)
}
t.Fatal(err)
}
// We consider T1 and T2 to be distinct types, so their instances should not
- // be deduplicated by the environment.
+ // be deduplicated by the context.
T1 := pkg1.Scope().Lookup("T").Type().(*Named)
T2 := pkg2.Scope().Lookup("T").Type().(*Named)
- env := NewEnvironment()
- res1, err := Instantiate(env, T1, []Type{Typ[Int]}, false)
+ ctxt := NewContext()
+ res1, err := Instantiate(ctxt, T1, []Type{Typ[Int]}, false)
if err != nil {
t.Fatal(err)
}
- res2, err := Instantiate(env, T2, []Type{Typ[Int]}, false)
+ res2, err := Instantiate(ctxt, T2, []Type{Typ[Int]}, false)
if err != nil {
t.Fatal(err)
}
methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
// resolver may be provided to lazily resolve type parameters, underlying, and methods.
- resolver func(*Environment, *Named) (tparams *TypeParamList, underlying Type, methods []*Func)
+ resolver func(*Context, *Named) (tparams *TypeParamList, underlying Type, methods []*Func)
once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing
}
return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods)
}
-func (t *Named) resolve(env *Environment) *Named {
+func (t *Named) resolve(ctxt *Context) *Named {
if t.resolver == nil {
return t
}
// methods would need to support reentrant calls though. It would
// also make the API more future-proof towards further extensions
// (like SetTypeParams).
- t.tparams, t.underlying, t.methods = t.resolver(env, t)
+ t.tparams, t.underlying, t.methods = t.resolver(ctxt, t)
t.fromRHS = t.underlying // for cycle detection
})
return t
}
}
-// bestEnv returns the best available environment. In order of preference:
-// - the given env, if non-nil
-// - the Checker env, if check is non-nil
-// - a new environment
-func (check *Checker) bestEnv(env *Environment) *Environment {
- if env != nil {
- return env
+// bestContext returns the best available context. In order of preference:
+// - the given ctxt, if non-nil
+// - check.Config.Context, if check is non-nil
+// - a new Context
+func (check *Checker) bestContext(ctxt *Context) *Context {
+ if ctxt != nil {
+ return ctxt
}
if check != nil {
- assert(check.conf.Environment != nil)
- return check.conf.Environment
+ assert(check.conf.Context != nil)
+ return check.conf.Context
}
- return NewEnvironment()
+ return NewContext()
}
// expandNamed ensures that the underlying type of n is instantiated.
// The underlying type will be Typ[Invalid] if there was an error.
-func expandNamed(env *Environment, n *Named, instPos syntax.Pos) (tparams *TypeParamList, underlying Type, methods []*Func) {
- n.orig.resolve(env)
+func expandNamed(ctxt *Context, n *Named, instPos syntax.Pos) (tparams *TypeParamList, underlying Type, methods []*Func) {
+ n.orig.resolve(ctxt)
check := n.check
if check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) {
- // We must always have an env, to avoid infinite recursion.
- env = check.bestEnv(env)
- h := env.TypeHash(n.orig, n.targs.list())
+ // We must always have a context, to avoid infinite recursion.
+ ctxt = check.bestContext(ctxt)
+ h := ctxt.TypeHash(n.orig, n.targs.list())
// ensure that an instance is recorded for h to avoid infinite recursion.
- env.typeForHash(h, n)
+ ctxt.typeForHash(h, n)
smap := makeSubstMap(n.orig.tparams.list(), n.targs.list())
- underlying = n.check.subst(instPos, n.orig.underlying, smap, env)
+ underlying = n.check.subst(instPos, n.orig.underlying, smap, ctxt)
for i := 0; i < n.orig.NumMethods(); i++ {
origm := n.orig.Method(i)
completeMethods := func() {
for _, m := range methods {
if m.instRecv != nil {
- check.completeMethod(env, m)
+ check.completeMethod(ctxt, m)
}
}
}
return n.orig.tparams, underlying, methods
}
-func (check *Checker) completeMethod(env *Environment, m *Func) {
+func (check *Checker) completeMethod(ctxt *Context, m *Func) {
assert(m.instRecv != nil)
rbase := m.instRecv
m.instRecv = nil
}
smap := makeSubstMap(origSig.RecvTypeParams().list(), rbase.targs.list())
- sig := check.subst(orig.pos, origSig, smap, env).(*Signature)
+ sig := check.subst(orig.pos, origSig, smap, ctxt).(*Signature)
if sig == origSig {
// No substitution occurred, but we still need to create a new signature to
// hold the instantiated receiver.
func NewTypeNameLazy(pos syntax.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName {
obj := NewTypeName(pos, pkg, name, nil)
- resolve := func(_ *Environment, t *Named) (*TypeParamList, Type, []*Func) {
+ resolve := func(_ *Context, t *Named) (*TypeParamList, Type, []*Func) {
tparams, underlying, methods := load(t)
switch underlying.(type) {
var err string
switch T := rtyp.(type) {
case *Named:
- T.resolve(check.conf.Environment)
+ T.resolve(check.conf.Context)
// The receiver type may be an instantiated type referred to
// by an alias (which cannot have receiver parameters for now).
if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
// incoming type. If a substitution took place, the result type is different
// from the incoming type.
//
-// If the given environment is non-nil, it is used in lieu of check.env.
-func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, env *Environment) Type {
+// If the given context is non-nil, it is used in lieu of check.Config.Context.
+func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, ctxt *Context) Type {
if smap.empty() {
return typ
}
pos: pos,
smap: smap,
check: check,
- env: check.bestEnv(env),
+ ctxt: check.bestContext(ctxt),
}
return subst.typ(typ)
}
pos syntax.Pos
smap substMap
check *Checker // nil if called via Instantiate
- env *Environment
+ ctxt *Context
}
func (subst *subster) typ(typ Type) Type {
}
// before creating a new named type, check if we have this one already
- h := subst.env.TypeHash(t.orig, newTArgs)
+ h := subst.ctxt.TypeHash(t.orig, newTArgs)
dump(">>> new type hash: %s", h)
- if named := subst.env.typeForHash(h, nil); named != nil {
+ if named := subst.ctxt.typeForHash(h, nil); named != nil {
dump(">>> found %s", named)
return named
}
- // Create a new instance and populate the environment to avoid endless
+ // Create a new instance and populate the context to avoid endless
// recursion. The position used here is irrelevant because validation only
// occurs on t (we don't call validType on named), but we use subst.pos to
// help with debugging.
- t.orig.resolve(subst.env)
- return subst.check.instance(subst.pos, t.orig, newTArgs, subst.env)
+ t.orig.resolve(subst.ctxt)
+ return subst.check.instance(subst.pos, t.orig, newTArgs, subst.ctxt)
// Note that if we were to expose substitution more generally (not just in
// the context of a declaration), we'd have to substitute in
buf *bytes.Buffer
seen map[Type]bool
qf Qualifier
- env *Environment // if non-nil, we are type hashing
+ ctxt *Context // if non-nil, we are type hashing
}
func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
return &typeWriter{buf, make(map[Type]bool), qf, nil}
}
-func newTypeHasher(buf *bytes.Buffer, env *Environment) *typeWriter {
- assert(env != nil)
- return &typeWriter{buf, make(map[Type]bool), nil, env}
+func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
+ assert(ctxt != nil)
+ return &typeWriter{buf, make(map[Type]bool), nil, ctxt}
}
func (w *typeWriter) byte(b byte) {
- if w.env != nil {
+ if w.ctxt != nil {
if b == ' ' {
b = '#'
}
}
func (w *typeWriter) error(msg string) {
- if w.env != nil {
+ if w.ctxt != nil {
panic(msg)
}
w.buf.WriteString("<" + msg + ">")
if tag := t.Tag(i); tag != "" {
w.byte(' ')
// TODO(gri) If tag contains blanks, replacing them with '#'
- // in Environment.TypeHash may produce another tag
+ // in Context.TypeHash may produce another tag
// accidentally.
w.string(strconv.Quote(tag))
}
if t.targs != nil {
// instantiated type
w.typeList(t.targs.list())
- } else if w.env == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TParams
+ } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TParams
// parameterized type
w.tParamList(t.TypeParams().list())
}
}
}
-// If w.env is non-nil, typePrefix writes a unique prefix for the named type t
-// based on the types already observed by w.env. If w.env is nil, it does
+// If w.ctxt is non-nil, typePrefix writes a unique prefix for the named type t
+// based on the types already observed by w.ctxt. If w.ctxt is nil, it does
// nothing.
func (w *typeWriter) typePrefix(t *Named) {
- if w.env != nil {
- w.string(strconv.Itoa(w.env.idForType(t)))
+ if w.ctxt != nil {
+ w.string(strconv.Itoa(w.ctxt.idForType(t)))
}
}
w.byte(',')
}
// parameter names are ignored for type identity and thus type hashes
- if w.env == nil && v.name != "" {
+ if w.ctxt == nil && v.name != "" {
w.string(v.name)
w.byte(' ')
}
}
w.byte(' ')
- if n == 1 && (w.env != nil || sig.results.vars[0].name == "") {
+ if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
// single unnamed result (if type hashing, name must be ignored)
w.typ(sig.results.vars[0].typ)
return
// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
- // Environment is the environment used for resolving global
- // identifiers. If nil, the type checker will initialize this
- // field with a newly created environment.
- Environment *Environment
+ // Context is the context used for resolving global identifiers. If nil, the
+ // type checker will initialize this field with a newly created context.
+ Context *Context
// GoVersion describes the accepted Go language version. The string
// must follow the format "go%d.%d" (e.g. "go1.12") or it must be
conf = new(Config)
}
- // make sure we have an environment
- if conf.Environment == nil {
- conf.Environment = NewEnvironment()
+ // make sure we have a context
+ if conf.Context == nil {
+ conf.Context = NewContext()
}
// make sure we have an info struct
"sync"
)
-// An Environment is an opaque type checking environment. It may be used to
-// share identical type instances across type-checked packages or calls to
+// An Context is an opaque type checking context. It may be used to share
+// identical type instances across type-checked packages or calls to
// Instantiate.
//
// It is safe for concurrent use.
-type Environment struct {
+type Context struct {
mu sync.Mutex
typeMap map[string]*Named // type hash -> instance
nextID int // next unique ID
seen map[*Named]int // assigned unique IDs
}
-// NewEnvironment creates a new Environment.
-func NewEnvironment() *Environment {
- return &Environment{
+// Temporary alias to allow x/tools tests to pass.
+// TODO(rfindley): remove the Environment type.
+type Environment = Context
+
+// NewContext creates a new Context.
+func NewContext() *Context {
+ return &Context{
typeMap: make(map[string]*Named),
seen: make(map[*Named]int),
}
// type hash: types that are identical produce identical string representations.
// If typ is a *Named type and targs is not empty, typ is printed as if it were
// instantiated with targs. The result is guaranteed to not contain blanks (" ").
-func (env *Environment) typeHash(typ Type, targs []Type) string {
- assert(env != nil)
+func (ctxt *Context) typeHash(typ Type, targs []Type) string {
+ assert(ctxt != nil)
assert(typ != nil)
var buf bytes.Buffer
- h := newTypeHasher(&buf, env)
+ h := newTypeHasher(&buf, ctxt)
if named, _ := typ.(*Named); named != nil && len(targs) > 0 {
// Don't use WriteType because we need to use the provided targs
// and not any targs that might already be with the *Named type.
// typeForHash returns the recorded type for the type hash h, if it exists.
// If no type exists for h and n is non-nil, n is recorded for h.
-func (env *Environment) typeForHash(h string, n *Named) *Named {
- env.mu.Lock()
- defer env.mu.Unlock()
- if existing := env.typeMap[h]; existing != nil {
+func (ctxt *Context) typeForHash(h string, n *Named) *Named {
+ ctxt.mu.Lock()
+ defer ctxt.mu.Unlock()
+ if existing := ctxt.typeMap[h]; existing != nil {
return existing
}
if n != nil {
- env.typeMap[h] = n
+ ctxt.typeMap[h] = n
}
return n
}
// idForType returns a unique ID for the pointer n.
-func (env *Environment) idForType(n *Named) int {
- env.mu.Lock()
- defer env.mu.Unlock()
- id, ok := env.seen[n]
+func (ctxt *Context) idForType(n *Named) int {
+ ctxt.mu.Lock()
+ defer ctxt.mu.Unlock()
+ id, ok := ctxt.seen[n]
if !ok {
- id = env.nextID
- env.seen[n] = id
- env.nextID++
+ id = ctxt.nextID
+ ctxt.seen[n] = id
+ ctxt.nextID++
}
return id
}
// Funcs with m.instRecv set have not yet be completed. Complete them now
// so that they have a type when objDecl exits.
if m, _ := obj.(*Func); m != nil && m.instRecv != nil {
- check.completeMethod(check.conf.Environment, m)
+ check.completeMethod(check.conf.Context, m)
}
// Checking the declaration of obj means inferring its type
}
case *Named:
- t.resolve(check.conf.Environment)
+ t.resolve(check.conf.Context)
// don't touch the type if it is from a different package or the Universe scope
// (doing so would lead to a race condition - was issue #35049)
if t.obj.pkg != check.pkg {
// *Signature). Any methods attached to a *Named are simply copied; they are
// not instantiated.
//
-// If env is non-nil, it may be used to de-dupe the instance against previous
-// instances with the same identity. This functionality is currently
-// unimplemented.
+// If ctxt is non-nil, it may be used to de-dupe the instance against previous
+// instances with the same identity.
//
// If verify is set and constraint satisfaction fails, the returned error may
// wrap an *ArgumentError indicating which type argument did not satisfy its
//
// TODO(rfindley): change this function to also return an error if lengths of
// tparams and targs do not match.
-func Instantiate(env *Environment, typ Type, targs []Type, validate bool) (Type, error) {
- inst := (*Checker)(nil).instance(token.NoPos, typ, targs, env)
+func Instantiate(ctxt *Context, typ Type, targs []Type, validate bool) (Type, error) {
+ inst := (*Checker)(nil).instance(token.NoPos, typ, targs, ctxt)
var err error
if validate {
}()
}
- inst := check.instance(pos, typ, targs, check.conf.Environment)
+ inst := check.instance(pos, typ, targs, check.conf.Context)
assert(len(posList) <= len(targs))
check.later(func() {
// instance creates a type or function instance using the given original type
// typ and arguments targs. For Named types the resulting instance will be
// unexpanded.
-func (check *Checker) instance(pos token.Pos, typ Type, targs []Type, env *Environment) Type {
+func (check *Checker) instance(pos token.Pos, typ Type, targs []Type, ctxt *Context) Type {
switch t := typ.(type) {
case *Named:
var h string
- if env != nil {
- h = env.typeHash(t, targs)
+ if ctxt != nil {
+ h = ctxt.typeHash(t, targs)
// typ may already have been instantiated with identical type arguments. In
// that case, re-use the existing instance.
- if named := env.typeForHash(h, nil); named != nil {
+ if named := ctxt.typeForHash(h, nil); named != nil {
return named
}
}
tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil)
named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is resolved
named.targs = NewTypeList(targs)
- named.resolver = func(env *Environment, n *Named) (*TypeParamList, Type, []*Func) {
- return expandNamed(env, n, pos)
+ named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, []*Func) {
+ return expandNamed(ctxt, n, pos)
}
- if env != nil {
- // It's possible that we've lost a race to add named to the environment.
- // In this case, use whichever instance is recorded in the environment.
- named = env.typeForHash(h, named)
+ if ctxt != nil {
+ // It's possible that we've lost a race to add named to the context.
+ // In this case, use whichever instance is recorded in the context.
+ named = ctxt.typeForHash(h, named)
}
return named
if tparams.Len() == 0 {
return typ // nothing to do (minor optimization)
}
- sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), env).(*Signature)
+ sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), ctxt).(*Signature)
// If the signature doesn't use its type parameters, subst
// will not make a copy. In that case, make a copy now (so
// we can set tparams to nil w/o causing side-effects).
// TODO(rfindley): it would be great if users could pass in a qualifier here,
// rather than falling back to verbose qualification. Maybe this can be part
- // of a the shared environment.
+ // of the shared context.
var qf Qualifier
if check != nil {
qf = check.qualifier
// Instantiating the same type twice should result in pointer-equivalent
// instances.
- env := NewEnvironment()
- res1, err := Instantiate(env, T, []Type{Typ[Int]}, false)
+ ctxt := NewContext()
+ res1, err := Instantiate(ctxt, T, []Type{Typ[Int]}, false)
if err != nil {
t.Fatal(err)
}
- res2, err := Instantiate(env, T, []Type{Typ[Int]}, false)
+ res2, err := Instantiate(ctxt, T, []Type{Typ[Int]}, false)
if err != nil {
t.Fatal(err)
}
}
// We consider T1 and T2 to be distinct types, so their instances should not
- // be deduplicated by the environment.
+ // be deduplicated by the context.
T1 := pkg1.Scope().Lookup("T").Type().(*Named)
T2 := pkg2.Scope().Lookup("T").Type().(*Named)
- env := NewEnvironment()
- res1, err := Instantiate(env, T1, []Type{Typ[Int]}, false)
+ ctxt := NewContext()
+ res1, err := Instantiate(ctxt, T1, []Type{Typ[Int]}, false)
if err != nil {
t.Fatal(err)
}
- res2, err := Instantiate(env, T2, []Type{Typ[Int]}, false)
+ res2, err := Instantiate(ctxt, T2, []Type{Typ[Int]}, false)
if err != nil {
t.Fatal(err)
}
methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
// resolver may be provided to lazily resolve type parameters, underlying, and methods.
- resolver func(*Environment, *Named) (tparams *TypeParamList, underlying Type, methods []*Func)
+ resolver func(*Context, *Named) (tparams *TypeParamList, underlying Type, methods []*Func)
once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing
}
return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods)
}
-func (t *Named) resolve(env *Environment) *Named {
+func (t *Named) resolve(ctxt *Context) *Named {
if t.resolver == nil {
return t
}
// methods would need to support reentrant calls though. It would
// also make the API more future-proof towards further extensions
// (like SetTypeParams).
- t.tparams, t.underlying, t.methods = t.resolver(env, t)
+ t.tparams, t.underlying, t.methods = t.resolver(ctxt, t)
t.fromRHS = t.underlying // for cycle detection
})
return t
}
}
-// bestEnv returns the best available environment. In order of preference:
-// - the given env, if non-nil
-// - the Checker env, if check is non-nil
-// - a new environment
-func (check *Checker) bestEnv(env *Environment) *Environment {
- if env != nil {
- return env
+// bestContext returns the best available context. In order of preference:
+// - the given ctxt, if non-nil
+// - check.Config.Context, if check is non-nil
+// - a new Context
+func (check *Checker) bestContext(ctxt *Context) *Context {
+ if ctxt != nil {
+ return ctxt
}
if check != nil {
- assert(check.conf.Environment != nil)
- return check.conf.Environment
+ assert(check.conf.Context != nil)
+ return check.conf.Context
}
- return NewEnvironment()
+ return NewContext()
}
// expandNamed ensures that the underlying type of n is instantiated.
// The underlying type will be Typ[Invalid] if there was an error.
-func expandNamed(env *Environment, n *Named, instPos token.Pos) (tparams *TypeParamList, underlying Type, methods []*Func) {
- n.orig.resolve(env)
+func expandNamed(ctxt *Context, n *Named, instPos token.Pos) (tparams *TypeParamList, underlying Type, methods []*Func) {
+ n.orig.resolve(ctxt)
check := n.check
if check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) {
- // We must always have an env, to avoid infinite recursion.
- env = check.bestEnv(env)
- h := env.typeHash(n.orig, n.targs.list())
+ // We must always have a context, to avoid infinite recursion.
+ ctxt = check.bestContext(ctxt)
+ h := ctxt.typeHash(n.orig, n.targs.list())
// ensure that an instance is recorded for h to avoid infinite recursion.
- env.typeForHash(h, n)
+ ctxt.typeForHash(h, n)
smap := makeSubstMap(n.orig.tparams.list(), n.targs.list())
- underlying = n.check.subst(instPos, n.orig.underlying, smap, env)
+ underlying = n.check.subst(instPos, n.orig.underlying, smap, ctxt)
for i := 0; i < n.orig.NumMethods(); i++ {
origm := n.orig.Method(i)
completeMethods := func() {
for _, m := range methods {
if m.instRecv != nil {
- check.completeMethod(env, m)
+ check.completeMethod(ctxt, m)
}
}
}
return n.orig.tparams, underlying, methods
}
-func (check *Checker) completeMethod(env *Environment, m *Func) {
+func (check *Checker) completeMethod(ctxt *Context, m *Func) {
assert(m.instRecv != nil)
rbase := m.instRecv
m.instRecv = nil
}
smap := makeSubstMap(origSig.RecvTypeParams().list(), rbase.targs.list())
- sig := check.subst(orig.pos, origSig, smap, env).(*Signature)
+ sig := check.subst(orig.pos, origSig, smap, ctxt).(*Signature)
if sig == origSig {
// No substitution occurred, but we still need to create a new signature to
// hold the instantiated receiver.
func _NewTypeNameLazy(pos token.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName {
obj := NewTypeName(pos, pkg, name, nil)
- resolve := func(_ *Environment, t *Named) (*TypeParamList, Type, []*Func) {
+ resolve := func(_ *Context, t *Named) (*TypeParamList, Type, []*Func) {
tparams, underlying, methods := load(t)
switch underlying.(type) {
var err string
switch T := rtyp.(type) {
case *Named:
- T.resolve(check.conf.Environment)
+ T.resolve(check.conf.Context)
// The receiver type may be an instantiated type referred to
// by an alias (which cannot have receiver parameters for now).
if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
// that it doesn't modify the incoming type. If a substitution took place, the
// result type is different from the incoming type.
//
-// If the given environment is non-nil, it is used in lieu of check.env.
-func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, env *Environment) Type {
+// If the given context is non-nil, it is used in lieu of check.Config.Context
+func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, ctxt *Context) Type {
if smap.empty() {
return typ
}
pos: pos,
smap: smap,
check: check,
- env: check.bestEnv(env),
+ ctxt: check.bestContext(ctxt),
}
return subst.typ(typ)
}
pos token.Pos
smap substMap
check *Checker // nil if called via Instantiate
- env *Environment
+ ctxt *Context
}
func (subst *subster) typ(typ Type) Type {
}
// before creating a new named type, check if we have this one already
- h := subst.env.typeHash(t.orig, newTArgs)
+ h := subst.ctxt.typeHash(t.orig, newTArgs)
dump(">>> new type hash: %s", h)
- if named := subst.env.typeForHash(h, nil); named != nil {
+ if named := subst.ctxt.typeForHash(h, nil); named != nil {
dump(">>> found %s", named)
return named
}
- // Create a new instance and populate the environment to avoid endless
+ // Create a new instance and populate the context to avoid endless
// recursion. The position used here is irrelevant because validation only
// occurs on t (we don't call validType on named), but we use subst.pos to
// help with debugging.
- t.orig.resolve(subst.env)
- return subst.check.instance(subst.pos, t.orig, newTArgs, subst.env)
+ t.orig.resolve(subst.ctxt)
+ return subst.check.instance(subst.pos, t.orig, newTArgs, subst.ctxt)
// Note that if we were to expose substitution more generally (not just in
// the context of a declaration), we'd have to substitute in
buf *bytes.Buffer
seen map[Type]bool
qf Qualifier
- env *Environment // if non-nil, we are type hashing
+ ctxt *Context // if non-nil, we are type hashing
}
func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
return &typeWriter{buf, make(map[Type]bool), qf, nil}
}
-func newTypeHasher(buf *bytes.Buffer, env *Environment) *typeWriter {
- assert(env != nil)
- return &typeWriter{buf, make(map[Type]bool), nil, env}
+func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
+ assert(ctxt != nil)
+ return &typeWriter{buf, make(map[Type]bool), nil, ctxt}
}
func (w *typeWriter) byte(b byte) {
- if w.env != nil {
+ if w.ctxt != nil {
if b == ' ' {
b = '#'
}
}
func (w *typeWriter) error(msg string) {
- if w.env != nil {
+ if w.ctxt != nil {
panic(msg)
}
w.buf.WriteString("<" + msg + ">")
if tag := t.Tag(i); tag != "" {
w.byte(' ')
// TODO(rfindley) If tag contains blanks, replacing them with '#'
- // in Environment.TypeHash may produce another tag
+ // in Context.TypeHash may produce another tag
// accidentally.
w.string(strconv.Quote(tag))
}
if t.targs != nil {
// instantiated type
w.typeList(t.targs.list())
- } else if w.env == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
+ } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
// parameterized type
w.tParamList(t.TypeParams().list())
}
}
}
-// If w.env is non-nil, typePrefix writes a unique prefix for the named type t
-// based on the types already observed by w.env. If w.env is nil, it does
+// If w.ctxt is non-nil, typePrefix writes a unique prefix for the named type t
+// based on the types already observed by w.ctxt. If w.ctxt is nil, it does
// nothing.
func (w *typeWriter) typePrefix(t *Named) {
- if w.env != nil {
- w.string(strconv.Itoa(w.env.idForType(t)))
+ if w.ctxt != nil {
+ w.string(strconv.Itoa(w.ctxt.idForType(t)))
}
}
w.byte(',')
}
// parameter names are ignored for type identity and thus type hashes
- if w.env == nil && v.name != "" {
+ if w.ctxt == nil && v.name != "" {
w.string(v.name)
w.byte(' ')
}
}
w.byte(' ')
- if n == 1 && (w.env != nil || sig.results.vars[0].name == "") {
+ if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
// single unnamed result (if type hashing, name must be ignored)
w.typ(sig.results.vars[0].typ)
return