2013-08-27 16:49:13 -06:00
|
|
|
// 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.
|
|
|
|
|
2013-08-22 10:27:55 -06:00
|
|
|
package pointer_test
|
|
|
|
|
|
|
|
// This test uses 'expectation' comments embedded within testdata/*.go
|
|
|
|
// files to specify the expected pointer analysis behaviour.
|
|
|
|
// See below for grammar.
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2013-09-25 15:17:42 -06:00
|
|
|
"errors"
|
2013-08-22 10:27:55 -06:00
|
|
|
"fmt"
|
2013-09-04 11:15:49 -06:00
|
|
|
"go/build"
|
2013-08-22 10:27:55 -06:00
|
|
|
"go/parser"
|
|
|
|
"go/token"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2013-09-25 15:17:42 -06:00
|
|
|
"code.google.com/p/go.tools/call"
|
2013-08-22 10:27:55 -06:00
|
|
|
"code.google.com/p/go.tools/go/types"
|
|
|
|
"code.google.com/p/go.tools/go/types/typemap"
|
|
|
|
"code.google.com/p/go.tools/importer"
|
|
|
|
"code.google.com/p/go.tools/pointer"
|
|
|
|
"code.google.com/p/go.tools/ssa"
|
|
|
|
)
|
|
|
|
|
|
|
|
var inputs = []string{
|
2013-09-23 14:13:01 -06:00
|
|
|
"testdata/a_test.go",
|
2013-08-22 10:27:55 -06:00
|
|
|
"testdata/another.go",
|
2013-10-11 13:34:19 -06:00
|
|
|
"testdata/arrayreflect.go",
|
2013-08-22 10:27:55 -06:00
|
|
|
"testdata/arrays.go",
|
|
|
|
"testdata/channels.go",
|
2013-09-23 14:13:01 -06:00
|
|
|
"testdata/chanreflect.go",
|
2013-08-22 10:27:55 -06:00
|
|
|
"testdata/context.go",
|
|
|
|
"testdata/conv.go",
|
2013-10-17 07:26:44 -06:00
|
|
|
"testdata/finalizer.go",
|
2013-08-22 10:27:55 -06:00
|
|
|
"testdata/flow.go",
|
|
|
|
"testdata/fmtexcerpt.go",
|
|
|
|
"testdata/func.go",
|
2013-10-11 13:34:19 -06:00
|
|
|
"testdata/funcreflect.go",
|
2013-08-22 10:27:55 -06:00
|
|
|
"testdata/hello.go",
|
|
|
|
"testdata/interfaces.go",
|
2013-09-23 14:13:01 -06:00
|
|
|
"testdata/mapreflect.go",
|
2013-08-22 10:27:55 -06:00
|
|
|
"testdata/maps.go",
|
|
|
|
"testdata/panic.go",
|
|
|
|
"testdata/recur.go",
|
2013-09-23 14:13:01 -06:00
|
|
|
"testdata/reflect.go",
|
go.tools/ssa: fix computation of set of types requiring method sets.
Motivation:
Previously, we assumed that the set of types for which a
complete method set (containing all synthesized wrapper
functions) is required at runtime was the set of types
used as operands to some *ssa.MakeInterface instruction.
In fact, this is an underapproximation because types can
be derived from other ones via reflection, and some of
these may need methods. The reflect.Type API allows *T to
be derived from T, and these may have different method
sets. Reflection also allows almost any subcomponent of a
type to be accessed (with one exception: given T, defined
'type T struct{S}', you can reach S but not struct{S}).
As a result, the pointer analysis was unable to generate
all necessary constraints before running the solver,
causing a crash when reflection derives types whose
methods are unavailable. (A similar problem would afflict
an ahead-of-time compiler based on ssa. The ssa/interp
interpreter was immune only because it does not require
all wrapper methods to be created before execution
begins.)
Description:
This change causes the SSA builder to record, for each
package, the set of all types with non-empty method sets that
are referenced within that package. This set is accessed via
Packages.TypesWithMethodSets(). Program.TypesWithMethodSets()
returns its union across all packages.
The set of references that matter are:
- types of operands to some MakeInterface instruction (as before)
- types of all exported package members
- all subcomponents of the above, recursively.
This is a conservative approximation to the set of types
whose methods may be called dynamically.
We define the owning package of a type as follows:
- the owner of a named type is the package in which it is defined;
- the owner of a pointer-to-named type is the owner of that named type;
- the owner of all other types is nil.
A package must include the method sets for all types that it
owns, and all subcomponents of that type that are not owned by
another package, recursively. Types with an owner appear in
exactly one package; types with no owner (such as struct{T})
may appear within multiple packages.
(A typical Go compiler would emit multiple copies of these
methods as weak symbols; a typical linker would eliminate
duplicates.)
Also:
- go/types/typemap: implement hash function for *Tuple.
- pointer: generate nodes/constraints for all of
ssa.Program.TypesWithMethodSets().
Add rtti.go regression test.
- Add API test of Package.TypesWithMethodSets().
- Set Function.Pkg to nil (again) for wrapper functions,
since these may be shared by many packages.
- Remove a redundant logging statement.
- Document that ssa CREATE phase is in fact sequential.
Fixes golang/go#6605
R=gri
CC=golang-dev
https://golang.org/cl/14920056
2013-10-23 15:07:52 -06:00
|
|
|
"testdata/rtti.go",
|
2013-10-17 07:26:44 -06:00
|
|
|
"testdata/structreflect.go",
|
2013-08-22 10:27:55 -06:00
|
|
|
"testdata/structs.go",
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expectation grammar:
|
|
|
|
//
|
|
|
|
// @calls f -> g
|
|
|
|
//
|
|
|
|
// A 'calls' expectation asserts that edge (f, g) appears in the
|
|
|
|
// callgraph. f and g are notated as per Function.String(), which
|
|
|
|
// may contain spaces (e.g. promoted method in anon struct).
|
|
|
|
//
|
|
|
|
// @pointsto a | b | c
|
|
|
|
//
|
|
|
|
// A 'pointsto' expectation asserts that the points-to set of its
|
|
|
|
// operand contains exactly the set of labels {a,b,c} notated as per
|
|
|
|
// labelString.
|
|
|
|
//
|
|
|
|
// A 'pointsto' expectation must appear on the same line as a
|
|
|
|
// print(x) statement; the expectation's operand is x.
|
|
|
|
//
|
|
|
|
// If one of the strings is "...", the expectation asserts that the
|
|
|
|
// points-to set at least the other labels.
|
|
|
|
//
|
|
|
|
// We use '|' because label names may contain spaces, e.g. methods
|
|
|
|
// of anonymous structs.
|
|
|
|
//
|
|
|
|
// From a theoretical perspective, concrete types in interfaces are
|
|
|
|
// labels too, but they are represented differently and so have a
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
// different expectation, @types, below.
|
2013-08-22 10:27:55 -06:00
|
|
|
//
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
// @types t | u | v
|
2013-08-22 10:27:55 -06:00
|
|
|
//
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
// A 'types' expectation asserts that the set of possible dynamic
|
2013-08-22 10:27:55 -06:00
|
|
|
// types of its interface operand is exactly {t,u,v}, notated per
|
|
|
|
// go/types.Type.String(). In other words, it asserts that the type
|
|
|
|
// component of the interface may point to that set of concrete type
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
// literals. It also works for reflect.Value, though the types
|
|
|
|
// needn't be concrete in that case.
|
2013-08-22 10:27:55 -06:00
|
|
|
//
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
// A 'types' expectation must appear on the same line as a
|
2013-08-22 10:27:55 -06:00
|
|
|
// print(x) statement; the expectation's operand is x.
|
|
|
|
//
|
|
|
|
// If one of the strings is "...", the expectation asserts that the
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
// interface's type may point to at least the other types.
|
2013-08-22 10:27:55 -06:00
|
|
|
//
|
|
|
|
// We use '|' because type names may contain spaces.
|
|
|
|
//
|
|
|
|
// @warning "regexp"
|
|
|
|
//
|
|
|
|
// A 'warning' expectation asserts that the analysis issues a
|
|
|
|
// warning that matches the regular expression within the string
|
|
|
|
// literal.
|
|
|
|
//
|
|
|
|
// @line id
|
|
|
|
//
|
|
|
|
// A line directive associates the name "id" with the current
|
|
|
|
// file:line. The string form of labels will use this id instead of
|
|
|
|
// a file:line, making @pointsto expectations more robust against
|
|
|
|
// perturbations in the source file.
|
|
|
|
// (NB, anon functions still include line numbers.)
|
|
|
|
//
|
|
|
|
type expectation struct {
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
kind string // "pointsto" | "types" | "calls" | "warning"
|
2013-08-22 10:27:55 -06:00
|
|
|
filename string
|
|
|
|
linenum int // source line number, 1-based
|
|
|
|
args []string
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
types []types.Type // for types
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *expectation) String() string {
|
|
|
|
return fmt.Sprintf("@%s[%s]", e.kind, strings.Join(e.args, " | "))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *expectation) errorf(format string, args ...interface{}) {
|
|
|
|
fmt.Printf("%s:%d: ", e.filename, e.linenum)
|
|
|
|
fmt.Printf(format, args...)
|
|
|
|
fmt.Println()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *expectation) needsProbe() bool {
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
return e.kind == "pointsto" || e.kind == "types"
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// A record of a call to the built-in print() function. Used for testing.
|
|
|
|
type probe struct {
|
|
|
|
instr *ssa.CallCommon
|
|
|
|
arg0 pointer.Pointer // first argument to print
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find probe (call to print(x)) of same source
|
|
|
|
// file/line as expectation.
|
|
|
|
func findProbe(prog *ssa.Program, probes []probe, e *expectation) *probe {
|
|
|
|
for _, p := range probes {
|
|
|
|
pos := prog.Fset.Position(p.instr.Pos())
|
|
|
|
if pos.Line == e.linenum && pos.Filename == e.filename {
|
|
|
|
// TODO(adonovan): send this to test log (display only on failure).
|
|
|
|
// fmt.Printf("%s:%d: info: found probe for %s: %s\n",
|
|
|
|
// e.filename, e.linenum, e, p.arg0) // debugging
|
|
|
|
return &p
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil // e.g. analysis didn't reach this call
|
|
|
|
}
|
|
|
|
|
|
|
|
func doOneInput(input, filename string) bool {
|
2013-09-04 11:15:49 -06:00
|
|
|
impctx := &importer.Config{Build: &build.Default}
|
2013-08-22 10:27:55 -06:00
|
|
|
imp := importer.New(impctx)
|
|
|
|
|
|
|
|
// Parsing.
|
2013-10-08 08:34:36 -06:00
|
|
|
f, err := parser.ParseFile(imp.Fset, filename, input, 0)
|
2013-08-22 10:27:55 -06:00
|
|
|
if err != nil {
|
|
|
|
// TODO(adonovan): err is a scanner error list;
|
|
|
|
// display all errors not just first?
|
2013-09-06 16:13:57 -06:00
|
|
|
fmt.Println(err)
|
2013-08-22 10:27:55 -06:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2013-10-10 10:37:49 -06:00
|
|
|
// Create single-file main package and import its dependencies.
|
|
|
|
info := imp.CreatePackage("main", f)
|
2013-08-22 10:27:55 -06:00
|
|
|
|
|
|
|
// SSA creation + building.
|
|
|
|
prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
|
2013-09-06 16:13:57 -06:00
|
|
|
if err := prog.CreatePackages(imp); err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return false
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
prog.BuildAll()
|
|
|
|
|
|
|
|
mainpkg := prog.Package(info.Pkg)
|
|
|
|
ptrmain := mainpkg // main package for the pointer analysis
|
|
|
|
if mainpkg.Func("main") == nil {
|
|
|
|
// No main function; assume it's a test.
|
2013-10-23 16:07:53 -06:00
|
|
|
ptrmain = prog.CreateTestMainPackage(mainpkg)
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
ok := true
|
|
|
|
|
|
|
|
lineMapping := make(map[string]string) // maps "file:line" to @line tag
|
|
|
|
|
|
|
|
// Parse expectations in this input.
|
|
|
|
var exps []*expectation
|
|
|
|
re := regexp.MustCompile("// *@([a-z]*) *(.*)$")
|
|
|
|
lines := strings.Split(input, "\n")
|
|
|
|
for linenum, line := range lines {
|
|
|
|
linenum++ // make it 1-based
|
|
|
|
if matches := re.FindAllStringSubmatch(line, -1); matches != nil {
|
|
|
|
match := matches[0]
|
|
|
|
kind, rest := match[1], match[2]
|
|
|
|
e := &expectation{kind: kind, filename: filename, linenum: linenum}
|
|
|
|
|
|
|
|
if kind == "line" {
|
|
|
|
if rest == "" {
|
|
|
|
ok = false
|
|
|
|
e.errorf("@%s expectation requires identifier", kind)
|
|
|
|
} else {
|
|
|
|
lineMapping[fmt.Sprintf("%s:%d", filename, linenum)] = rest
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.needsProbe() && !strings.Contains(line, "print(") {
|
|
|
|
ok = false
|
|
|
|
e.errorf("@%s expectation must follow call to print(x)", kind)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
switch kind {
|
|
|
|
case "pointsto":
|
|
|
|
e.args = split(rest, "|")
|
|
|
|
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
case "types":
|
2013-08-22 10:27:55 -06:00
|
|
|
for _, typstr := range split(rest, "|") {
|
|
|
|
var t types.Type = types.Typ[types.Invalid] // means "..."
|
|
|
|
if typstr != "..." {
|
|
|
|
texpr, err := parser.ParseExpr(typstr)
|
|
|
|
if err != nil {
|
|
|
|
ok = false
|
|
|
|
// Don't print err since its location is bad.
|
|
|
|
e.errorf("'%s' is not a valid type", typstr)
|
|
|
|
continue
|
|
|
|
}
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
mainFileScope := mainpkg.Object.Scope().Child(0)
|
|
|
|
t, _, err = types.EvalNode(imp.Fset, texpr, mainpkg.Object, mainFileScope)
|
2013-08-22 10:27:55 -06:00
|
|
|
if err != nil {
|
|
|
|
ok = false
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
// Don't print err since its location is bad.
|
2013-08-22 10:27:55 -06:00
|
|
|
e.errorf("'%s' is not a valid type: %s", typstr, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
e.types = append(e.types, t)
|
|
|
|
}
|
|
|
|
|
|
|
|
case "calls":
|
|
|
|
e.args = split(rest, "->")
|
|
|
|
// TODO(adonovan): eagerly reject the
|
|
|
|
// expectation if fn doesn't denote
|
|
|
|
// existing function, rather than fail
|
|
|
|
// the expectation after analysis.
|
|
|
|
if len(e.args) != 2 {
|
|
|
|
ok = false
|
|
|
|
e.errorf("@calls expectation wants 'caller -> callee' arguments")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
case "warning":
|
|
|
|
lit, err := strconv.Unquote(strings.TrimSpace(rest))
|
|
|
|
if err != nil {
|
|
|
|
ok = false
|
|
|
|
e.errorf("couldn't parse @warning operand: %s", err.Error())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
e.args = append(e.args, lit)
|
|
|
|
|
|
|
|
default:
|
|
|
|
ok = false
|
|
|
|
e.errorf("unknown expectation kind: %s", e)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
exps = append(exps, e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var probes []probe
|
|
|
|
var log bytes.Buffer
|
|
|
|
|
|
|
|
// Run the analysis.
|
|
|
|
config := &pointer.Config{
|
2013-09-25 15:17:42 -06:00
|
|
|
Reflection: true,
|
|
|
|
BuildCallGraph: true,
|
|
|
|
Mains: []*ssa.Package{ptrmain},
|
|
|
|
Log: &log,
|
2013-08-22 10:27:55 -06:00
|
|
|
Print: func(site *ssa.CallCommon, p pointer.Pointer) {
|
|
|
|
probes = append(probes, probe{site, p})
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print the log is there was an error or a panic.
|
|
|
|
complete := false
|
|
|
|
defer func() {
|
|
|
|
if !complete || !ok {
|
|
|
|
log.WriteTo(os.Stderr)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2013-10-29 19:57:53 -06:00
|
|
|
result := pointer.Analyze(config)
|
|
|
|
|
2013-08-22 10:27:55 -06:00
|
|
|
// Check the expectations.
|
|
|
|
for _, e := range exps {
|
|
|
|
var pr *probe
|
|
|
|
if e.needsProbe() {
|
|
|
|
if pr = findProbe(prog, probes, e); pr == nil {
|
|
|
|
ok = false
|
|
|
|
e.errorf("unreachable print() statement has expectation %s", e)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if pr.arg0 == nil {
|
|
|
|
ok = false
|
|
|
|
e.errorf("expectation on non-pointerlike operand: %s", pr.instr.Args[0].Type())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch e.kind {
|
|
|
|
case "pointsto":
|
|
|
|
if !checkPointsToExpectation(e, pr, lineMapping, prog) {
|
|
|
|
ok = false
|
|
|
|
}
|
|
|
|
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
case "types":
|
|
|
|
if !checkTypesExpectation(e, pr) {
|
2013-08-22 10:27:55 -06:00
|
|
|
ok = false
|
|
|
|
}
|
|
|
|
|
|
|
|
case "calls":
|
2013-09-25 15:17:42 -06:00
|
|
|
if !checkCallsExpectation(prog, e, result.CallGraph) {
|
2013-08-22 10:27:55 -06:00
|
|
|
ok = false
|
|
|
|
}
|
|
|
|
|
|
|
|
case "warning":
|
2013-09-30 10:39:54 -06:00
|
|
|
if !checkWarningExpectation(prog, e, result.Warnings) {
|
2013-08-22 10:27:55 -06:00
|
|
|
ok = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
complete = true
|
|
|
|
|
|
|
|
// ok = false // debugging: uncomment to always see log
|
|
|
|
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func labelString(l *pointer.Label, lineMapping map[string]string, prog *ssa.Program) string {
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
// Functions and Globals need no pos suffix,
|
|
|
|
// nor do allocations in intrinsic operations
|
|
|
|
// (for which we'll print the function name).
|
|
|
|
switch l.Value().(type) {
|
|
|
|
case nil, *ssa.Function, *ssa.Global:
|
2013-08-22 10:27:55 -06:00
|
|
|
return l.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
str := l.String()
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
if pos := l.Pos(); pos != token.NoPos {
|
2013-08-22 10:27:55 -06:00
|
|
|
// Append the position, using a @line tag instead of a line number, if defined.
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
posn := prog.Fset.Position(pos)
|
2013-08-22 10:27:55 -06:00
|
|
|
s := fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
|
|
|
|
if tag, ok := lineMapping[s]; ok {
|
|
|
|
return fmt.Sprintf("%s@%s:%d", str, tag, posn.Column)
|
|
|
|
}
|
|
|
|
str = fmt.Sprintf("%s@%s", str, posn)
|
|
|
|
}
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkPointsToExpectation(e *expectation, pr *probe, lineMapping map[string]string, prog *ssa.Program) bool {
|
2013-10-17 07:26:44 -06:00
|
|
|
expected := make(map[string]int)
|
|
|
|
surplus := make(map[string]int)
|
2013-08-22 10:27:55 -06:00
|
|
|
exact := true
|
|
|
|
for _, g := range e.args {
|
|
|
|
if g == "..." {
|
|
|
|
exact = false
|
|
|
|
continue
|
|
|
|
}
|
2013-10-17 07:26:44 -06:00
|
|
|
expected[g]++
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
// Find the set of labels that the probe's
|
|
|
|
// argument (x in print(x)) may point to.
|
|
|
|
for _, label := range pr.arg0.PointsTo().Labels() {
|
|
|
|
name := labelString(label, lineMapping, prog)
|
2013-10-17 07:26:44 -06:00
|
|
|
if expected[name] > 0 {
|
|
|
|
expected[name]--
|
2013-08-22 10:27:55 -06:00
|
|
|
} else if exact {
|
2013-10-17 07:26:44 -06:00
|
|
|
surplus[name]++
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
}
|
2013-10-17 07:26:44 -06:00
|
|
|
// Report multiset difference:
|
2013-08-22 10:27:55 -06:00
|
|
|
ok := true
|
2013-10-17 07:26:44 -06:00
|
|
|
for _, count := range expected {
|
|
|
|
if count > 0 {
|
|
|
|
ok = false
|
|
|
|
e.errorf("value does not alias these expected labels: %s", join(expected))
|
|
|
|
break
|
|
|
|
}
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
2013-10-17 07:26:44 -06:00
|
|
|
for _, count := range surplus {
|
|
|
|
if count > 0 {
|
|
|
|
ok = false
|
|
|
|
e.errorf("value may additionally alias these labels: %s", join(surplus))
|
|
|
|
break
|
|
|
|
}
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// underlying returns the underlying type of typ. Copied from go/types.
|
|
|
|
func underlyingType(typ types.Type) types.Type {
|
|
|
|
if typ, ok := typ.(*types.Named); ok {
|
|
|
|
return typ.Underlying() // underlying types are never NamedTypes
|
|
|
|
}
|
|
|
|
if typ == nil {
|
|
|
|
panic("underlying(nil)")
|
|
|
|
}
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
func checkTypesExpectation(e *expectation, pr *probe) bool {
|
2013-08-22 10:27:55 -06:00
|
|
|
var expected typemap.M
|
|
|
|
var surplus typemap.M
|
|
|
|
exact := true
|
|
|
|
for _, g := range e.types {
|
|
|
|
if g == types.Typ[types.Invalid] {
|
|
|
|
exact = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
expected.Set(g, struct{}{})
|
|
|
|
}
|
|
|
|
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
if t := pr.instr.Args[0].Type(); !pointer.CanHaveDynamicTypes(t) {
|
|
|
|
e.errorf("@types expectation requires an interface- or reflect.Value-typed operand, got %s", t)
|
2013-08-22 10:27:55 -06:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
// Find the set of types that the probe's
|
2013-08-22 10:27:55 -06:00
|
|
|
// argument (x in print(x)) may contain.
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
for _, T := range pr.arg0.PointsTo().DynamicTypes().Keys() {
|
|
|
|
if expected.At(T) != nil {
|
|
|
|
expected.Delete(T)
|
2013-08-22 10:27:55 -06:00
|
|
|
} else if exact {
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
surplus.Set(T, struct{}{})
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Report set difference:
|
|
|
|
ok := true
|
|
|
|
if expected.Len() > 0 {
|
|
|
|
ok = false
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
e.errorf("interface cannot contain these types: %s", expected.KeysString())
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
if surplus.Len() > 0 {
|
|
|
|
ok = false
|
go.tools/pointer: reflection, part 1: maps, and some core features.
Core:
reflect.TypeOf
reflect.ValueOf
reflect.Zero
reflect.Value.Interface
Maps:
(reflect.Value).MapIndex
(reflect.Value).MapKeys
(reflect.Value).SetMapIndex
(*reflect.rtype).Elem
(*reflect.rtype).Key
+ tests:
pointer/testdata/mapreflect.go.
oracle/testdata/src/main/reflection.go.
Interface objects (T, V...) have been renamed "tagged objects".
Abstraction: we model reflect.Value similar to
interface{}---as a pointer that points only to tagged
objects---but a reflect.Value may also point to an "indirect
tagged object", one in which the payload V is of type *T not T.
These are required because reflect.Values can hold lvalues,
e.g. when derived via Field() or Elem(), though we won't use
them till we get to structs and pointers.
Solving: each reflection intrinsic defines a new constraint
and resolution rule. Because of the nature of reflection,
generalizing across types, the resolution rules dynamically
create additional complex constraints during solving, where
previously only simple (copy) constraints were created.
This requires some solver changes:
The work done before the main solver loop (to attach new
constraints to the graph) is now done before each iteration,
in processNewConstraints.
Its loop over constraints is broken into two passes:
the first handles base (addr-of) constraints,
the second handles simple and complex constraints.
constraint.init() has been inlined. The only behaviour that
varies across constraints is ptr()
Sadly this will pessimize presolver optimisations, when we get
there; such is the price of reflection.
Objects: reflection intrinsics create objects (i.e. cause
memory allocations) with no SSA operation. We will represent
them as the cgnode of the instrinsic (e.g. reflect.New), so we
extend Labels and node.data to represent objects as a product
(not sum) of ssa.Value and cgnode and pull this out into its
own type, struct object. This simplifies a number of
invariants and saves space. The ntObject flag is now
represented by obj!=nil; the other flags are moved into
object.
cgnodes are now always recorded in objects/Labels for which it
is appropriate (all but those for globals, constants and the
shared contours for functions).
Also:
- Prepopulate the flattenMemo cache to consider reflect.Value
a fake pointer, not a struct.
- Improve accessors and documentation on type Label.
- @conctypes assertions renamed @types (since dyn. types needn't be concrete).
- add oracle 'describe' test on an interface (missing, an oversight).
R=crawshaw
CC=golang-dev
https://golang.org/cl/13418048
2013-09-16 07:49:10 -06:00
|
|
|
e.errorf("interface may additionally contain these types: %s", surplus.KeysString())
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2013-09-25 15:17:42 -06:00
|
|
|
var errOK = errors.New("OK")
|
|
|
|
|
|
|
|
func checkCallsExpectation(prog *ssa.Program, e *expectation, callgraph call.Graph) bool {
|
2013-10-17 07:26:44 -06:00
|
|
|
found := make(map[string]int)
|
2013-09-25 15:17:42 -06:00
|
|
|
err := call.GraphVisitEdges(callgraph, func(edge call.Edge) error {
|
|
|
|
// Name-based matching is inefficient but it allows us to
|
|
|
|
// match functions whose names that would not appear in an
|
|
|
|
// index ("<root>") or which are not unique ("func@1.2").
|
|
|
|
if edge.Caller.Func().String() == e.args[0] {
|
|
|
|
calleeStr := edge.Callee.Func().String()
|
|
|
|
if calleeStr == e.args[1] {
|
|
|
|
return errOK // expectation satisified; stop the search
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
2013-10-17 07:26:44 -06:00
|
|
|
found[calleeStr]++
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
2013-09-25 15:17:42 -06:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err == errOK {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if len(found) == 0 {
|
|
|
|
e.errorf("didn't find any calls from %s", e.args[0])
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
2013-09-25 15:17:42 -06:00
|
|
|
e.errorf("found no call from %s to %s, but only to %s",
|
|
|
|
e.args[0], e.args[1], join(found))
|
2013-08-22 10:27:55 -06:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2013-09-30 10:39:54 -06:00
|
|
|
func checkWarningExpectation(prog *ssa.Program, e *expectation, warnings []pointer.Warning) bool {
|
2013-08-22 10:27:55 -06:00
|
|
|
// TODO(adonovan): check the position part of the warning too?
|
|
|
|
re, err := regexp.Compile(e.args[0])
|
|
|
|
if err != nil {
|
|
|
|
e.errorf("invalid regular expression in @warning expectation: %s", err.Error())
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(warnings) == 0 {
|
|
|
|
e.errorf("@warning %s expectation, but no warnings", strconv.Quote(e.args[0]))
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2013-09-30 10:39:54 -06:00
|
|
|
for _, w := range warnings {
|
|
|
|
if re.MatchString(w.Message) {
|
2013-08-22 10:27:55 -06:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
e.errorf("@warning %s expectation not satised; found these warnings though:", strconv.Quote(e.args[0]))
|
2013-09-30 10:39:54 -06:00
|
|
|
for _, w := range warnings {
|
|
|
|
fmt.Printf("%s: warning: %s\n", prog.Fset.Position(w.Pos), w.Message)
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInput(t *testing.T) {
|
|
|
|
ok := true
|
|
|
|
|
|
|
|
wd, err := os.Getwd()
|
|
|
|
if err != nil {
|
2013-09-06 16:13:57 -06:00
|
|
|
t.Errorf("os.Getwd: %s", err)
|
2013-08-22 10:27:55 -06:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 'go test' does a chdir so that relative paths in
|
|
|
|
// diagnostics no longer make sense relative to the invoking
|
|
|
|
// shell's cwd. We print a special marker so that Emacs can
|
|
|
|
// make sense of them.
|
|
|
|
fmt.Fprintf(os.Stderr, "Entering directory `%s'\n", wd)
|
|
|
|
|
|
|
|
for _, filename := range inputs {
|
|
|
|
content, err := ioutil.ReadFile(filename)
|
|
|
|
if err != nil {
|
2013-09-06 16:13:57 -06:00
|
|
|
t.Errorf("couldn't read file '%s': %s", filename, err)
|
2013-08-22 10:27:55 -06:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if !doOneInput(string(content), filename) {
|
|
|
|
ok = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-17 07:26:44 -06:00
|
|
|
// join joins the elements of multiset with " | "s.
|
|
|
|
func join(set map[string]int) string {
|
2013-08-22 10:27:55 -06:00
|
|
|
var buf bytes.Buffer
|
|
|
|
sep := ""
|
2013-10-17 07:26:44 -06:00
|
|
|
for name, count := range set {
|
|
|
|
for i := 0; i < count; i++ {
|
|
|
|
buf.WriteString(sep)
|
|
|
|
sep = " | "
|
|
|
|
buf.WriteString(name)
|
|
|
|
}
|
2013-08-22 10:27:55 -06:00
|
|
|
}
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// split returns the list of sep-delimited non-empty strings in s.
|
|
|
|
func split(s, sep string) (r []string) {
|
|
|
|
for _, elem := range strings.Split(s, sep) {
|
|
|
|
elem = strings.TrimSpace(elem)
|
|
|
|
if elem != "" {
|
|
|
|
r = append(r, elem)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|