From 1928c01286fa71901552b33505954d855d2e099e Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 13 Sep 2013 09:52:57 -0700 Subject: [PATCH] go.tools/go/types: separate package descriptor from package object Includes changes by adonovan to make oracle work again (former CL 13395050). R=adonovan CC=golang-dev https://golang.org/cl/13672044 --- go/types/api.go | 46 +++++++++------- go/types/call.go | 6 +-- go/types/check.go | 10 ++-- go/types/gcimporter.go | 4 +- go/types/lookup.go | 2 +- go/types/objects.go | 52 ++----------------- go/types/package.go | 41 +++++++++++++++ go/types/resolver.go | 42 +++++++++------ go/types/resolver_test.go | 6 +-- go/types/typexpr.go | 2 +- go/types/universe.go | 2 +- importer/pkginfo.go | 28 ---------- oracle/describe.go | 19 +++---- oracle/freevars.go | 2 +- oracle/referrers.go | 28 +++++----- .../testdata/src/main/referrers-json.golden | 4 +- ssa/builder.go | 6 +-- ssa/interp/reflect.go | 2 +- 18 files changed, 143 insertions(+), 159 deletions(-) create mode 100644 go/types/package.go diff --git a/go/types/api.go b/go/types/api.go index 8d3655e4..effc38a3 100644 --- a/go/types/api.go +++ b/go/types/api.go @@ -54,11 +54,17 @@ type Config struct { // If FakeImportC is set, `import "C"` (for packages requiring Cgo) // declares an empty "C" package and errors are omitted for qualified // identifiers referring to package C (which won't find an object). - // Caution: Effects may be unpredictable due to unpredictable follow- - // up errors - do not use casually! This feature is mainly intended - // for the standard library cmd/api tool. + // This feature is intended for the standard library cmd/api tool. + // + // Caution: Effects may be unpredictable due to follow-up errors. + // Do not use casually! FakeImportC bool + // Packages is used to look up (and thus canonicalize) packages by + // package path. If Packages is nil, it is set to a new empty map. + // During type-checking, imported packages are added to the map. + Packages map[string]*Package + // If Error != nil, it is called with each error found // during type checking. The error strings of errors with // detailed position information are formatted as follows: @@ -67,15 +73,16 @@ type Config struct { // If Import != nil, it is called for each imported package. // Otherwise, GcImporter is called. - // An importer resolves import paths to Package objects. - // The imports map records the packages already imported, - // indexed by package id (canonical import path). - // An importer must determine the canonical import path and - // check the map to see if it is already present in the imports map. - // If so, the Importer can return the map entry. Otherwise, the - // importer should load the package data for the given path into - // a new *Package, record pkg in the imports map, and then - // return pkg. + // An importer resolves import paths to Packages. + // The imports map records packages already known, + // indexed by canonical package path. The type-checker will + // invoke Import with Config.Packages. + // An importer must determine the canonical package path and + // check imports to see if it is already present in the map. + // If so, the Importer can return the map entry. Otherwise, + // the importer must load the package data for the given path + // into a new *Package, record it in imports map, and return + // the package. Import func(imports map[string]*Package, path string) (pkg *Package, err error) // If Alignof != nil, it is called to determine the alignment @@ -110,9 +117,10 @@ type Info struct { // Objects maps identifiers to their corresponding objects (including // package names, dots "." of dot-imports, and blank "_" identifiers). - // For identifiers that do not denote objects (e.g., blank identifiers - // on the lhs of assignments, or symbolic variables t in t := x.(type) - // of type switch headers), the corresponding objects are nil. + // For identifiers that do not denote objects (e.g., the package name + // in package clauses, blank identifiers on the lhs of assignments, or + // symbolic variables t in t := x.(type) of type switch headers), the + // corresponding objects are nil. // BUG(gri) Label identifiers in break, continue, or goto statements // are not yet mapped. Objects map[*ast.Ident]Object @@ -122,7 +130,7 @@ type Info struct { // // node declared object // - // *ast.ImportSpec *Package (imports w/o renames), or imported objects (dot-imports) + // *ast.ImportSpec *PkgName (imports w/o renames), or imported objects (dot-imports) // *ast.CaseClause type-specific *Var for each type switch case clause (incl. default) // *ast.Field anonymous struct field or parameter *Var // @@ -157,9 +165,9 @@ type Info struct { // The package is marked as complete if no errors occurred, otherwise it is // incomplete. // -// The package is specified by a list of *ast.Files and corresponding file -// set, and the import path the package is identified with. The clean path -// must not be empty or dot ("."). +// The package is specified by a list of *ast.Files and corresponding +// file set, and the package path the package is identified with. +// The clean path must not be empty or dot ("."). func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) { pkg, err := conf.check(path, fset, files, info) if err == nil { diff --git a/go/types/call.go b/go/types/call.go index d54034ee..9fa53fd4 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -185,11 +185,11 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { // can only appear in qualified identifiers which are mapped to // selector expressions. if ident, ok := e.X.(*ast.Ident); ok { - if pkg, _ := check.topScope.LookupParent(ident.Name).(*Package); pkg != nil { + if pkg, _ := check.topScope.LookupParent(ident.Name).(*PkgName); pkg != nil { check.recordObject(ident, pkg) - exp := pkg.scope.Lookup(sel) + exp := pkg.pkg.scope.Lookup(sel) if exp == nil { - if !pkg.fake { + if !pkg.pkg.fake { check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) } goto Error diff --git a/go/types/check.go b/go/types/check.go index 054f3670..9a2f257a 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -138,16 +138,16 @@ func (check *checker) handleBailout(err *error) { } func (conf *Config) check(pkgPath string, fset *token.FileSet, files []*ast.File, info *Info) (pkg *Package, err error) { - pkg = &Package{ - path: pkgPath, - scope: NewScope(Universe), - imports: make(map[string]*Package), + // make sure we have a package canonicalization map + if conf.Packages == nil { + conf.Packages = make(map[string]*Package) } + pkg = NewPackage(pkgPath, "", NewScope(Universe)) // package name is set below check := newChecker(conf, fset, pkg) defer check.handleBailout(&err) - // we need a reasonable path to continue + // we need a reasonable package path to continue if path.Clean(pkgPath) == "." { check.errorf(token.NoPos, "invalid package path provided: %q", pkgPath) return diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go index ad15cf3b..7e08a23e 100644 --- a/go/types/gcimporter.go +++ b/go/types/gcimporter.go @@ -370,7 +370,7 @@ func (p *gcParser) getPkg(id, name string) *Package { } pkg := p.imports[id] if pkg == nil && name != "" { - pkg = NewPackage(token.NoPos, id, name, NewScope(nil), nil) + pkg = NewPackage(id, name, NewScope(nil)) p.imports[id] = pkg } return pkg @@ -985,7 +985,7 @@ func (p *gcParser) parseExport() *Package { } // package was imported completely and without errors - pkg.MarkComplete() + pkg.complete = true return pkg } diff --git a/go/types/lookup.go b/go/types/lookup.go index db098587..8f8390b1 100644 --- a/go/types/lookup.go +++ b/go/types/lookup.go @@ -73,7 +73,7 @@ func LookupFieldOrMethod(T Type, pkg *Package, name string) (obj Object, index [ func lookupFieldOrMethod(T Type, pkg *Package, name string) (obj Object, index []int, indirect bool) { // WARNING: The code in this function is extremely subtle - do not modify casually! - // This function and NewMethodSet should kept in sync. + // This function and NewMethodSet should be kept in sync. if name == "_" { return // blank fields/methods are never found diff --git a/go/types/objects.go b/go/types/objects.go index a2d5900c..6dd8c97f 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -121,58 +121,16 @@ func (obj *object) sameId(pkg *Package, name string) bool { return pkg.path == obj.pkg.path } -// A Package represents the contents (objects) of a Go package. -// -// A package is complete if all its package scope objects are present. -// Incomplete packages may arise via imports where the exported data -// contains only partial information about transitively imported and -// re-exported packages; or as a result of type-checking a package -// that contains errors. -// -// There are two kinds of Package objects, primary and secondary. -// A primary Package has no declaring identifier, and is referenced by -// each ast.File.Name within that package. -// A secondary Package object is created for each ast.ImportSpec that -// imports it; its declaring identifier is the ImportSpec.Name (if -// any), and each qualified reference (e.g. fmt.Println) is a -// reference to a secondary Package. -// The Primary() method of a secondary package returns its primary -// package; called on a primary package, it returns nil. -// The Path(), Name() and Scope() attributes of primary and secondary -// Packages are equal. -// TODO(gri): consider whether this distinction carries its weight; -// adonovan thinks not. -// -type Package struct { +// A PkgName represents an imported Go package. +type PkgName struct { object - path string // import path, "" for current (non-imported) package - scope *Scope // imported objects - imports map[string]*Package // map of import paths to imported packages - complete bool // if set, this package is complete - fake bool // if set, this package is fake (internal use only) - primary *Package // associated primary package (nil => self) } -func NewPackage(pos token.Pos, path, name string, scope *Scope, imports map[string]*Package) *Package { - obj := &Package{object{nil, pos, nil, name, Typ[Invalid]}, path, scope, imports, false, false, nil} - obj.pkg = obj - return obj +func NewPkgName(pos token.Pos, pkg *Package, name string) *PkgName { + return &PkgName{object{nil, pos, pkg, name, Typ[Invalid]}} } -func (obj *Package) String() string { return fmt.Sprintf("package %s", obj.Path()) } -func (obj *Package) Path() string { return obj.path } -func (obj *Package) Scope() *Scope { return obj.scope } -func (obj *Package) Imports() map[string]*Package { return obj.imports } -func (obj *Package) Complete() bool { return obj.complete } -func (obj *Package) Primary() *Package { - if obj.primary != nil { - return obj.primary - } - return obj -} - -// MarkComplete marks a package as complete. -func (obj *Package) MarkComplete() { obj.complete = true } +func (obj *PkgName) String() string { return obj.toString("package", nil) } // A Const represents a declared constant. type Const struct { diff --git a/go/types/package.go b/go/types/package.go new file mode 100644 index 00000000..06803e13 --- /dev/null +++ b/go/types/package.go @@ -0,0 +1,41 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +// A Package describes a Go package. +type Package struct { + path string + name string + scope *Scope + complete bool + imports []*Package + fake bool // scope lookup errors are silently dropped if package is fake (internal use only) +} + +// NewPackage returns a new Package for the given package path, +// name, and scope. The package is not complete and contains no +// explicit imports. +func NewPackage(path, name string, scope *Scope) *Package { + return &Package{path: path, name: name, scope: scope} +} + +// Path returns the package path. +func (pkg *Package) Path() string { return pkg.path } + +// Name returns the package name. +func (pkg *Package) Name() string { return pkg.name } + +// Scope returns the (complete or incomplete) package scope +// holding the objects declared at package level (TypeNames, +// Consts, Vars, and Funcs). +func (pkg *Package) Scope() *Scope { return pkg.scope } + +// A package is complete if its scope contains (at least) all +// exported objects; otherwise it is incomplete. +func (pkg *Package) Complete() bool { return pkg.complete } + +// Imports returns the list of packages explicitly imported by +// pkg; the list is in source order. Package unsafe is excluded. +func (pkg *Package) Imports() []*Package { return pkg.imports } diff --git a/go/types/resolver.go b/go/types/resolver.go index c92a6747..2832ae03 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -145,9 +145,13 @@ func (check *checker) resolveFiles(files []*ast.File) { importer = GcImport } + // only add packages to pkg.imports that have not been seen already + seen := make(map[*Package]bool) + for _, file := range files { - // the package identifier denotes the current package, but it is in no scope - check.recordObject(file.Name, pkg) + // The package identifier denotes the current package, + // but there is no corresponding package object. + check.recordObject(file.Name, nil) scope = NewScope(pkg.scope) check.recordScope(file, scope) @@ -163,15 +167,16 @@ func (check *checker) resolveFiles(files []*ast.File) { for iota, spec := range d.Specs { switch s := spec.(type) { case *ast.ImportSpec: + // import package var imp *Package path, _ := strconv.Unquote(s.Path.Value) if path == "C" && check.conf.FakeImportC { // TODO(gri) shouldn't create a new one each time - imp = NewPackage(token.NoPos, "C", "C", NewScope(nil), nil) + imp = NewPackage("C", "C", NewScope(nil)) imp.fake = true } else { var err error - imp, err = importer(pkg.imports, path) + imp, err = importer(check.conf.Packages, path) if imp == nil && err == nil { err = errors.New("Config.Import returned nil but no error") } @@ -181,6 +186,16 @@ func (check *checker) resolveFiles(files []*ast.File) { } } + // add package to list of explicit imports + // (this functionality is provided as a convenience + // for clients; it is not needed for type-checking) + if !seen[imp] { + seen[imp] = true + if imp != Unsafe { + pkg.imports = append(pkg.imports, imp) + } + } + // local name overrides imported package name name := imp.name if s.Name != nil { @@ -191,33 +206,30 @@ func (check *checker) resolveFiles(files []*ast.File) { } } - imp2 := NewPackage(s.Pos(), path, name, imp.scope, nil) - imp2.primary = imp - imp2.complete = imp.complete - imp2.fake = imp.fake + obj := NewPkgName(s.Pos(), imp, name) if s.Name != nil { - check.recordObject(s.Name, imp2) + // in a dot-import, the dot represents the package + check.recordObject(s.Name, obj) } else { - check.recordImplicit(s, imp2) + check.recordImplicit(s, obj) } // add import to file scope if name == "." { // merge imported scope with file scope for _, obj := range imp.scope.elems { - // gcimported package scopes contain non-exported - // objects such as types used in partially exported - // objects - do not accept them + // A package scope may contain non-exported objects, + // do not import them! if obj.IsExported() { // Note: This will change each imported object's scope! - // May be an issue for types aliases. + // May be an issue for type aliases. check.declareObj(scope, nil, obj) check.recordImplicit(s, obj) } } } else { // declare imported package object in file scope - check.declareObj(scope, nil, imp2) + check.declareObj(scope, nil, obj) } case *ast.ValueSpec: diff --git a/go/types/resolver_test.go b/go/types/resolver_test.go index 7e062b92..e845114f 100644 --- a/go/types/resolver_test.go +++ b/go/types/resolver_test.go @@ -75,14 +75,14 @@ func TestResolveIdents(t *testing.T) { // resolve and type-check package AST var conf Config idents := make(map[*ast.Ident]Object) - pkg, err := conf.Check("testResolveIdents", fset, files, &Info{Objects: idents}) + _, err := conf.Check("testResolveIdents", fset, files, &Info{Objects: idents}) if err != nil { t.Fatal(err) } // check that all packages were imported for _, name := range pkgnames { - if pkg.imports[name] == nil { + if conf.Packages[name] == nil { t.Errorf("package %s not imported", name) } } @@ -97,7 +97,7 @@ func TestResolveIdents(t *testing.T) { t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name) return false } - if _, ok := obj.(*Package); ok && idents[s.Sel] == nil { + if _, ok := obj.(*PkgName); ok && idents[s.Sel] == nil { t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name) return false } diff --git a/go/types/typexpr.go b/go/types/typexpr.go index b57d387e..c0cb2ae9 100644 --- a/go/types/typexpr.go +++ b/go/types/typexpr.go @@ -46,7 +46,7 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool) assert(typ != nil) switch obj := obj.(type) { - case *Package: + case *PkgName: check.errorf(e.Pos(), "use of package %s not in selector", obj.name) return diff --git a/go/types/universe.go b/go/types/universe.go index dac6b14f..86533df5 100644 --- a/go/types/universe.go +++ b/go/types/universe.go @@ -87,7 +87,7 @@ var predeclaredFunctions = [...]*Builtin{ func init() { Universe = NewScope(nil) - Unsafe = NewPackage(token.NoPos, "unsafe", "unsafe", NewScope(Universe), nil) + Unsafe = NewPackage("unsafe", "unsafe", NewScope(Universe)) Unsafe.complete = true // predeclared types diff --git a/importer/pkginfo.go b/importer/pkginfo.go index 72f40ea5..646bbd2b 100644 --- a/importer/pkginfo.go +++ b/importer/pkginfo.go @@ -10,7 +10,6 @@ import ( "fmt" "go/ast" "go/token" - "strconv" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" @@ -33,33 +32,6 @@ func (info *PackageInfo) String() string { return fmt.Sprintf("PackageInfo(%s)", info.Pkg.Path()) } -// Imports returns the set of packages imported by this one, in source -// order. Callers should not mutate the result. -// -func (info *PackageInfo) Imports() []*types.Package { - var imports []*types.Package - - // We iterate over the syntax (info.Files) not the types - // (info.Pkg.Imports()) because the latter may contain the - // transitive closure of dependencies, e.g. when using GcImporter. - seen := make(map[*types.Package]bool) - for _, file := range info.Files { - for _, imp := range file.Imports { - path, _ := strconv.Unquote(imp.Path.Value) - if path == "unsafe" { - continue // not a true package - } - typkg := info.Pkg.Imports()[path] - if seen[typkg] { - continue // already seen - } - seen[typkg] = true - imports = append(imports, typkg) - } - } - return imports -} - // TypeOf returns the type of expression e. // Precondition: e belongs to the package's ASTs. // diff --git a/oracle/describe.go b/oracle/describe.go index 96ef9cec..258e43af 100644 --- a/oracle/describe.go +++ b/oracle/describe.go @@ -196,7 +196,7 @@ func findInterestingNode(pkginfo *importer.PackageInfo, path []ast.Node) ([]ast. case *ast.Ident: switch obj := pkginfo.ObjectOf(n).(type) { - case *types.Package: + case *types.PkgName: return path, actionPackage case *types.Const: @@ -264,6 +264,10 @@ func findInterestingNode(pkginfo *importer.PackageInfo, path []ast.Node) ([]ast. // FuncType.{Params.Results} -- actionExpr // FuncDecl.Recv -- actionExpr + case *ast.File: + // 'package foo' + return path, actionPackage + case *ast.ImportSpec: // TODO(adonovan): fix: why no package object? go/types bug? return path[1:], actionPackage @@ -715,21 +719,14 @@ func describePackage(o *oracle, path []ast.Node) (*describePackageResult, error) pkg = o.prog.ImportedPackage(importPath).Object case *ast.Ident: - pkg = o.queryPkgInfo.ObjectOf(n).(*types.Package) - if _, isDef := path[1].(*ast.File); isDef { // e.g. package id + pkg = o.queryPkgInfo.Pkg description = fmt.Sprintf("definition of package %q", pkg.Path()) } else { // e.g. import id // or id.F() - - // go/types internally creates a new Package - // object for each import, so the packages for - // 'package x' and 'import "x"' differ. - // Call Primary() to get the real thing. - pkg = pkg.Primary() - + pkg = o.queryPkgInfo.ObjectOf(n).Pkg() description = fmt.Sprintf("reference to package %q", pkg.Path()) } @@ -870,7 +867,7 @@ func tokenOf(o types.Object) string { return "type" case *types.Const: return "const" - case *types.Package: + case *types.PkgName: return "package" } panic(o) diff --git a/oracle/freevars.go b/oracle/freevars.go index 211e4818..07973f2a 100644 --- a/oracle/freevars.go +++ b/oracle/freevars.go @@ -54,7 +54,7 @@ func freevars(o *oracle) (queryResult, error) { return nil // TODO(adonovan): fix: this fails for *types.Label. panic(o.errorf(n, "no types.Object for ast.Ident")) } - if _, ok := obj.(*types.Package); ok { + if _, ok := obj.(*types.PkgName); ok { return nil // imported package } if n.Pos() == obj.Pos() { diff --git a/oracle/referrers.go b/oracle/referrers.go index 98e23c8b..ab369d07 100644 --- a/oracle/referrers.go +++ b/oracle/referrers.go @@ -28,14 +28,11 @@ func referrers(o *oracle) (queryResult, error) { return nil, o.errorf(false, "no object for identifier") } - obj = primaryPkg(obj) - // Iterate over all go/types' resolver facts for the entire program. var refs []token.Pos for _, info := range o.typeInfo { for id2, obj2 := range info.Objects { - obj2 = primaryPkg(obj2) - if obj2 == obj { + if sameObj(obj, obj2) { if id2.NamePos == obj.Pos() { continue // skip defining ident } @@ -52,20 +49,19 @@ func referrers(o *oracle) (queryResult, error) { }, nil } -// primaryPkg returns obj unchanged unless it is a (secondary) package -// object created by an ImportSpec, in which case the canonical -// (primary) object is returned. +// same reports whether x and y are identical, or both are PkgNames +// referring to the same Package. // -// TODO(adonovan): The need for this function argues against the -// wisdom of the primary/secondary distinction. Discuss with gri. -// -func primaryPkg(obj types.Object) types.Object { - if pkg, ok := obj.(*types.Package); ok { - if prim := pkg.Primary(); prim != nil { - return prim +func sameObj(x, y types.Object) bool { + if x == y { + return true + } + if _, ok := x.(*types.PkgName); ok { + if _, ok := y.(*types.PkgName); ok { + return x.Pkg() == y.Pkg() } } - return obj + return false } type referrersResult struct { @@ -93,7 +89,7 @@ func (r *referrersResult) toJSON(res *json.Result, fset *token.FileSet) { Pos: fset.Position(r.query).String(), Desc: r.obj.String(), } - if pos := r.obj.Pos(); pos != token.NoPos { // primary package objects have no Pos() + if pos := r.obj.Pos(); pos != token.NoPos { // Package objects have no Pos() referrers.ObjPos = fset.Position(pos).String() } for _, ref := range r.refs { diff --git a/oracle/testdata/src/main/referrers-json.golden b/oracle/testdata/src/main/referrers-json.golden index c85882b6..d31c19f8 100644 --- a/oracle/testdata/src/main/referrers-json.golden +++ b/oracle/testdata/src/main/referrers-json.golden @@ -3,11 +3,11 @@ "mode": "referrers", "referrers": { "pos": "testdata/src/main/referrers-json.go:14:8", + "objpos": "testdata/src/main/referrers-json.go:7:8", "desc": "package lib", "refs": [ "testdata/src/main/referrers-json.go:14:8", - "testdata/src/main/referrers-json.go:14:19", - "testdata/src/lib/lib.go:1:9" + "testdata/src/main/referrers-json.go:14:19" ] } }-------- @referrers ref-method -------- diff --git a/ssa/builder.go b/ssa/builder.go index 6400f6e9..045b7a4b 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -2350,10 +2350,10 @@ func (p *Package) Build() { emitStore(init, initguard, vTrue) // Call the init() function of each package we import. - for _, obj := range p.info.Imports() { - prereq := p.Prog.packages[obj] + for _, pkg := range p.info.Pkg.Imports() { + prereq := p.Prog.packages[pkg] if prereq == nil { - panic(fmt.Sprintf("Package(%q).Build(): unsatisified import: Program.CreatePackage(%q) was not called", p.Object.Path(), obj.Path())) + panic(fmt.Sprintf("Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called", p.Object.Path(), pkg.Path())) } var v Call v.Call.Value = prereq.init diff --git a/ssa/interp/reflect.go b/ssa/interp/reflect.go index b245bf38..ef74a2da 100644 --- a/ssa/interp/reflect.go +++ b/ssa/interp/reflect.go @@ -28,7 +28,7 @@ type opaqueType struct { func (t *opaqueType) String() string { return t.name } // A bogus "reflect" type-checker package. Shared across interpreters. -var reflectTypesPackage = types.NewPackage(token.NoPos, "reflect", "reflect", nil, nil) +var reflectTypesPackage = types.NewPackage("reflect", "reflect", nil) // rtype is the concrete type the interpreter uses to implement the // reflect.Type interface. Since its type is opaque to the target