reflect/prototype: initial commit

The prototype package provides constructors to create protobuf types that
implement the interfaces defined in the protoreflect package.

High-level API:
	func NewFile(t *File) (protoreflect.FileDescriptor, error)
	type File struct{ ... }
	type Message struct{ ... }
	type Field struct{ ... }
	type Oneof struct{ ... }
	type Enum struct{ ... }
	type EnumValue struct{ ... }
	type Extension struct{ ... }
	type Service struct{ ... }
	type Method struct{ ... }

	func NewEnum(t *StandaloneEnum) (protoreflect.EnumDescriptor, error)
	func NewMessage(t *StandaloneMessage) (protoreflect.MessageDescriptor, error)
	func NewExtension(t *StandaloneExtension) (protoreflect.ExtensionDescriptor, error)
	type StandaloneEnum struct{ ... }
	type StandaloneMessage struct{ ... }
	type StandaloneExtension struct{ ... }

	func PlaceholderFile(path string, pkg protoreflect.FullName) protoreflect.FileDescriptor
	func PlaceholderEnum(name protoreflect.FullName) protoreflect.EnumDescriptor
	func PlaceholderMessage(name protoreflect.FullName) protoreflect.MessageDescriptor

This CL is missing some features that are to be added later:
* The stringer methods are not implemented, providing no way to print the
descriptors in a humanly readable manner.
* There is no support for proto options or retrieving the raw descriptor.
* There are constructors for Go specific types (e.g., protoreflect.MessageType).

We drop go1.9 support since we use strings.Builder.
We switch to go.11rc1 to obtain some bug fixes for modules.

Change-Id: Ieac9a2530afc81e5a5bb9ab5816804372f652b18
Reviewed-on: https://go-review.googlesource.com/129057
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/internal/cmd/generate-types/main.go b/internal/cmd/generate-types/main.go
new file mode 100644
index 0000000..9d74a44
--- /dev/null
+++ b/internal/cmd/generate-types/main.go
@@ -0,0 +1,244 @@
+// Copyright 2018 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.
+
+//go:generate go run . -execute
+
+package main
+
+import (
+	"flag"
+	"go/format"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path"
+	"regexp"
+	"strconv"
+	"strings"
+	"text/template"
+	"unicode"
+)
+
+var run = flag.Bool("execute", false, "Write generated files to destination.")
+
+func main() {
+	flag.Parse()
+	chdirRoot()
+	writeSource("reflect/prototype/protofile_list_gen.go", generateListTypes())
+}
+
+// chdirRoot changes the working directory to the repository root.
+func chdirRoot() {
+	out, err := exec.Command("git", "rev-parse", "--show-toplevel").CombinedOutput()
+	if err != nil {
+		panic(err)
+	}
+	if err := os.Chdir(strings.TrimSuffix(string(out), "\n")); err != nil {
+		panic(err)
+	}
+}
+
+// Expr is a single line Go expression.
+type Expr string
+
+type DescriptorType string
+
+const (
+	MessageDesc   DescriptorType = "Message"
+	FieldDesc     DescriptorType = "Field"
+	OneofDesc     DescriptorType = "Oneof"
+	ExtensionDesc DescriptorType = "Extension"
+	EnumDesc      DescriptorType = "Enum"
+	EnumValueDesc DescriptorType = "EnumValue"
+	ServiceDesc   DescriptorType = "Service"
+	MethodDesc    DescriptorType = "Method"
+)
+
+func (d DescriptorType) Expr() Expr {
+	return "protoreflect." + Expr(d) + "Descriptor"
+}
+func (d DescriptorType) NumberExpr() Expr {
+	switch d {
+	case FieldDesc:
+		return "protoreflect.FieldNumber"
+	case EnumValueDesc:
+		return "protoreflect.EnumNumber"
+	default:
+		return ""
+	}
+}
+
+func generateListTypes() string {
+	// TODO: If Go2 has generics, replace this with a single container type.
+	return mustExecute(listTypesTemplate, []DescriptorType{
+		MessageDesc, FieldDesc, OneofDesc, ExtensionDesc, EnumDesc, EnumValueDesc, ServiceDesc, MethodDesc,
+	})
+}
+
+var listTypesTemplate = template.Must(template.New("").Funcs(template.FuncMap{
+	"unexport": func(t DescriptorType) Expr {
+		return Expr(string(unicode.ToLower(rune(t[0]))) + string(t[1:]))
+	},
+}).Parse(`
+	{{- range .}}
+	{{$nameList     := (printf "%ss"     (unexport .))}} {{/* e.g., "messages" */}}
+	{{$nameListMeta := (printf "%ssMeta" (unexport .))}} {{/* e.g., "messagesMeta" */}}
+	{{$nameMeta     := (printf "%sMeta"  (unexport .))}} {{/* e.g., "messageMeta" */}}
+	{{$nameDesc     := (printf "%sDesc"  (unexport .))}} {{/* e.g., "messageDesc" */}}
+
+	type {{$nameListMeta}} struct {
+		once     sync.Once
+		typs     []{{.}}
+		nameOnce sync.Once
+		byName   map[protoreflect.Name]*{{.}}
+		{{- if (eq . "Field")}}
+		jsonOnce sync.Once
+		byJSON   map[string]*{{.}}
+		{{- end}}
+		{{- if .NumberExpr}}
+		numOnce  sync.Once
+		byNum    map[{{.NumberExpr}}]*{{.}}
+		{{- end}}
+	}
+	type {{$nameList}} {{$nameListMeta}}
+
+	func (p *{{$nameListMeta}}) lazyInit(parent protoreflect.Descriptor, ts []{{.}}) *{{$nameList}} {
+		p.once.Do(func() {
+			nb := nameBuilderPool.Get().(*nameBuilder)
+			defer nameBuilderPool.Put(nb)
+			metas := make([]{{$nameMeta}}, len(ts))
+			for i := range ts {
+				t := &ts[i]
+				if t.{{$nameMeta}} != nil {
+					panic("already initialized")
+				}
+				t.{{$nameMeta}} = &metas[i]
+				t.inheritedMeta.init(nb, parent, t.Name, {{printf "%v" (eq . "EnumValue")}})
+			}
+			p.typs = ts
+		})
+		return (*{{$nameList}})(p)
+	}
+	func (p *{{$nameList}}) Len() int            { return len(p.typs) }
+	func (p *{{$nameList}}) Get(i int) {{.Expr}} { return {{$nameDesc}}{&p.typs[i]} }
+	func (p *{{$nameList}}) ByName(s protoreflect.Name) {{.Expr}} {
+		p.nameOnce.Do(func() {
+			if len(p.typs) > 0 {
+				p.byName = make(map[protoreflect.Name]*{{.}}, len(p.typs))
+				for i := range p.typs {
+					t := &p.typs[i]
+					p.byName[t.Name] = t
+				}
+			}
+		})
+		t := p.byName[s]
+		if t == nil {
+			return nil
+		}
+		return {{$nameDesc}}{t}
+	}
+	{{- if (eq . "Field")}}
+	func (p *{{$nameList}}) ByJSONName(s string) {{.Expr}} {
+		p.jsonOnce.Do(func() {
+			if len(p.typs) > 0 {
+				p.byJSON = make(map[string]*{{.}}, len(p.typs))
+				for i := range p.typs {
+					t := &p.typs[i]
+					s := {{$nameDesc}}{t}.JSONName()
+					if _, ok := p.byJSON[s]; !ok {
+						p.byJSON[s] = t
+					}
+				}
+			}
+		})
+		t := p.byJSON[s]
+		if t == nil {
+			return nil
+		}
+		return {{$nameDesc}}{t}
+	}
+	{{- end}}
+	{{- if .NumberExpr}}
+	func (p *{{$nameList}}) ByNumber(n {{.NumberExpr}}) {{.Expr}} {
+		p.numOnce.Do(func() {
+			if len(p.typs) > 0 {
+				p.byNum = make(map[{{.NumberExpr}}]*{{.}}, len(p.typs))
+				for i := range p.typs {
+					t := &p.typs[i]
+					if _, ok := p.byNum[t.Number]; !ok {
+						p.byNum[t.Number] = t
+					}
+				}
+			}
+		})
+		t := p.byNum[n]
+		if t == nil {
+			return nil
+		}
+		return {{$nameDesc}}{t}
+	}
+	{{- end}}
+	func (p *{{$nameList}}) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+	func (p *{{$nameList}}) ProtoInternal(pragma.DoNotImplement) {}
+	{{- end}}
+`))
+
+func mustExecute(t *template.Template, data interface{}) string {
+	var sb strings.Builder
+	if err := t.Execute(&sb, data); err != nil {
+		panic(err)
+	}
+	return sb.String()
+}
+
+func writeSource(file, src string) {
+	// Crude but effective way to detect used imports.
+	var imports []string
+	for _, pkg := range []string{
+		"fmt",
+		"sync",
+		"",
+		"google.golang.org/proto/internal/pragma",
+		"google.golang.org/proto/reflect/protoreflect",
+	} {
+		if pkg == "" {
+			imports = append(imports, "") // blank line between stdlib and proto packages
+		} else if regexp.MustCompile(`[^\pL_0-9]` + path.Base(pkg) + `\.`).MatchString(src) {
+			imports = append(imports, strconv.Quote(pkg))
+		}
+	}
+
+	s := strings.Join([]string{
+		"// Copyright 2018 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.",
+		"",
+		"// Code generated by generate-types. DO NOT EDIT.",
+		"",
+		"package " + path.Base(path.Dir(path.Join("proto", file))),
+		"",
+		"import (" + strings.Join(imports, "\n") + ")",
+		"",
+		src,
+	}, "\n")
+	b, err := format.Source([]byte(s))
+	if err != nil {
+		panic(err)
+	}
+
+	if *run {
+		if err := ioutil.WriteFile(file, b, 0664); err != nil {
+			panic(err)
+		}
+	} else {
+		if err := ioutil.WriteFile(file+".tmp", b, 0664); err != nil {
+			panic(err)
+		}
+		defer os.Remove(file + ".tmp")
+
+		cmd := exec.Command("diff", file, file+".tmp", "-u")
+		cmd.Stdout = os.Stdout
+		cmd.Run()
+	}
+}
diff --git a/reflect/prototype/descriptor.go b/reflect/prototype/descriptor.go
new file mode 100644
index 0000000..8490ad3
--- /dev/null
+++ b/reflect/prototype/descriptor.go
@@ -0,0 +1,29 @@
+// Copyright 2018 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 prototype
+
+import (
+	pref "google.golang.org/proto/reflect/protoreflect"
+)
+
+// TODO: This cannot be implemented without proto.Unmarshal.
+
+type descriptorFileMeta struct{}
+
+func (p *descriptorFileMeta) lazyInit(t fileDesc) (pref.Message, bool) {
+	return nil, false
+}
+
+type descriptorSubMeta struct{}
+
+func (p *descriptorSubMeta) lazyInit(t pref.Descriptor) (pref.Message, bool) {
+	return nil, false
+}
+
+type descriptorOptionsMeta struct{}
+
+func (p *descriptorOptionsMeta) lazyInit(t pref.Descriptor) (pref.DescriptorOptions, bool) {
+	return nil, false
+}
diff --git a/reflect/prototype/placeholder.go b/reflect/prototype/placeholder.go
new file mode 100644
index 0000000..dde30c9
--- /dev/null
+++ b/reflect/prototype/placeholder.go
@@ -0,0 +1,32 @@
+// Copyright 2018 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 prototype
+
+import "google.golang.org/proto/reflect/protoreflect"
+
+// PlaceholderFile returns a placeholder protoreflect.FileType where
+// only the Path and Package accessors are valid.
+func PlaceholderFile(path string, pkg protoreflect.FullName) protoreflect.FileDescriptor {
+	// TODO: Is Package needed for placeholders?
+	return placeholderFile{path, placeholderName(pkg)}
+}
+
+// PlaceholderMessage returns a placeholder protoreflect.MessageType
+// where only the Name and FullName accessors are valid.
+//
+// A placeholder can be used within File literals when referencing a message
+// that is declared within that file.
+func PlaceholderMessage(name protoreflect.FullName) protoreflect.MessageDescriptor {
+	return placeholderMessage{placeholderName(name)}
+}
+
+// PlaceholderEnum returns a placeholder protoreflect.EnumType
+// where only the Name and FullName accessors are valid.
+//
+// A placeholder can be used within File literals when referencing an enum
+// that is declared within that file.
+func PlaceholderEnum(name protoreflect.FullName) protoreflect.EnumDescriptor {
+	return placeholderEnum{placeholderName(name)}
+}
diff --git a/reflect/prototype/placeholder_type.go b/reflect/prototype/placeholder_type.go
new file mode 100644
index 0000000..1aed8de
--- /dev/null
+++ b/reflect/prototype/placeholder_type.go
@@ -0,0 +1,74 @@
+// Copyright 2018 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 prototype
+
+import (
+	"fmt"
+
+	"google.golang.org/proto/internal/pragma"
+	pref "google.golang.org/proto/reflect/protoreflect"
+)
+
+var (
+	emptyFiles      fileImports
+	emptyMessages   messages
+	emptyFields     fields
+	emptyOneofs     oneofs
+	emptyNumbers    numbers
+	emptyRanges     ranges
+	emptyEnums      enums
+	emptyEnumValues enumValues
+	emptyExtensions extensions
+	emptyServices   services
+)
+
+type placeholderName pref.FullName
+
+func (t placeholderName) Parent() (pref.Descriptor, bool)                   { return nil, false }
+func (t placeholderName) Syntax() pref.Syntax                               { return 0 }
+func (t placeholderName) Name() pref.Name                                   { return pref.FullName(t).Name() }
+func (t placeholderName) FullName() pref.FullName                           { return pref.FullName(t) }
+func (t placeholderName) IsPlaceholder() bool                               { return true }
+func (t placeholderName) DescriptorProto() (pref.Message, bool)             { return nil, false }
+func (t placeholderName) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
+func (t placeholderName) ProtoInternal(pragma.DoNotImplement)               {}
+
+type placeholderFile struct {
+	path string
+	placeholderName
+}
+
+func (t placeholderFile) Path() string                          { return t.path }
+func (t placeholderFile) Package() pref.FullName                { return t.FullName() }
+func (t placeholderFile) Imports() pref.FileImports             { return &emptyFiles }
+func (t placeholderFile) Messages() pref.MessageDescriptors     { return &emptyMessages }
+func (t placeholderFile) Enums() pref.EnumDescriptors           { return &emptyEnums }
+func (t placeholderFile) Extensions() pref.ExtensionDescriptors { return &emptyExtensions }
+func (t placeholderFile) Services() pref.ServiceDescriptors     { return &emptyServices }
+func (t placeholderFile) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
+func (t placeholderFile) ProtoType(pref.FileDescriptor)         {}
+
+type placeholderMessage struct {
+	placeholderName
+}
+
+func (t placeholderMessage) IsMapEntry() bool                      { return false }
+func (t placeholderMessage) Fields() pref.FieldDescriptors         { return &emptyFields }
+func (t placeholderMessage) Oneofs() pref.OneofDescriptors         { return &emptyOneofs }
+func (t placeholderMessage) RequiredNumbers() pref.FieldNumbers    { return &emptyNumbers }
+func (t placeholderMessage) ExtensionRanges() pref.FieldRanges     { return &emptyRanges }
+func (t placeholderMessage) Messages() pref.MessageDescriptors     { return &emptyMessages }
+func (t placeholderMessage) Enums() pref.EnumDescriptors           { return &emptyEnums }
+func (t placeholderMessage) Extensions() pref.ExtensionDescriptors { return &emptyExtensions }
+func (t placeholderMessage) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
+func (t placeholderMessage) ProtoType(pref.MessageDescriptor)      {}
+
+type placeholderEnum struct {
+	placeholderName
+}
+
+func (t placeholderEnum) Values() pref.EnumValueDescriptors { return &emptyEnumValues }
+func (t placeholderEnum) Format(s fmt.State, r rune)        { formatDesc(s, r, t) }
+func (t placeholderEnum) ProtoType(pref.EnumDescriptor)     {}
diff --git a/reflect/prototype/protofile.go b/reflect/prototype/protofile.go
new file mode 100644
index 0000000..e2e4307
--- /dev/null
+++ b/reflect/prototype/protofile.go
@@ -0,0 +1,161 @@
+// Copyright 2018 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 prototype provides builders to construct protobuf types that
+// implement the interfaces defined in the protoreflect package.
+//
+// Protobuf types can either be constructed as standalone types
+// (e.g., StandaloneMessage), or together as a batch of types in a single
+// proto file (e.g., File). When creating standalone types, additional
+// information must be provided such as the full type name and the proto syntax.
+// When creating an entire file, the syntax and full name is derived from
+// the parent type.
+package prototype
+
+import (
+	"google.golang.org/proto/reflect/protoreflect"
+)
+
+// Every struct has a "meta" struct embedded within it as a pointer.
+// The meta type provides additional data structures for efficient lookup on
+// certain methods (e.g., ByName) or derived information that can be
+// derived from the parent (e.g., FullName). The meta type is lazily allocated
+// and initialized. This architectural approach keeps the literal representation
+// smaller, which then keeps the generated code size smaller.
+
+// TODO: Support initializing File from a google.protobuf.FileDescriptor?
+
+// TODO: Instead of a top-down construction approach where internal references
+// to message types use placeholder types, we could add a Reference method
+// on Message and Enum that creates a MessageDescriptor or EnumDescriptor
+// reference that only becomes valid after NewFile.
+// However, that API approach is more error prone, as it causes more memory
+// aliasing and provides more opportunity for misuse.
+// Also, it requires that NewFile at least eagerly initialize all
+// messages and enums list types. We can always add that API in the future.
+
+// File is a constructor for protoreflect.FileDescriptor.
+type File struct {
+	Syntax  protoreflect.Syntax
+	Path    string
+	Package protoreflect.FullName
+	Imports []protoreflect.FileImport
+
+	Messages   []Message
+	Enums      []Enum
+	Extensions []Extension
+	Services   []Service
+
+	*fileMeta
+}
+
+// NewFile creates a new protoreflect.FileDescriptor from the provided value.
+// The file must represent a valid proto file according to protobuf semantics.
+//
+// Fields that reference an enum or message that is being declared within the
+// same File can be represented using a placeholder descriptor. NewFile will
+// automatically resolve the placeholder to point to the concrete type.
+//
+// The caller must relinquish full ownership of the input t and must not
+// access or mutate any fields. The input must not contain slices that are
+// sub-slices of each other.
+func NewFile(t *File) (protoreflect.FileDescriptor, error) {
+	// TODO: Provide an unverified make that avoids validating the file.
+	// This is useful for generated code since we know that protoc-gen-go
+	// already validated the protobuf types.
+	ft := newFile(t)
+	if err := validateFile(ft); err != nil {
+		return nil, err
+	}
+	return ft, nil
+}
+
+// Message is a constructor for protoreflect.MessageDescriptor.
+type Message struct {
+	Name            protoreflect.Name
+	IsMapEntry      bool
+	Fields          []Field
+	Oneofs          []Oneof
+	ExtensionRanges [][2]protoreflect.FieldNumber
+
+	Messages   []Message
+	Enums      []Enum
+	Extensions []Extension
+
+	*messageMeta
+}
+
+// Field is a constructor for protoreflect.FieldDescriptor.
+type Field struct {
+	Name        protoreflect.Name
+	Number      protoreflect.FieldNumber
+	Cardinality protoreflect.Cardinality
+	Kind        protoreflect.Kind
+	JSONName    string
+	IsPacked    bool
+	IsWeak      bool
+	Default     protoreflect.Value
+	OneofName   protoreflect.Name
+	MessageType protoreflect.MessageDescriptor
+	EnumType    protoreflect.EnumDescriptor
+
+	*fieldMeta
+}
+
+// Oneof is a constructor for protoreflect.OneofDescriptor.
+type Oneof struct {
+	Name protoreflect.Name
+
+	*oneofMeta
+}
+
+// Extension is a constructor for protoreflect.ExtensionDescriptor.
+type Extension struct {
+	Name         protoreflect.Name
+	Number       protoreflect.FieldNumber
+	Cardinality  protoreflect.Cardinality
+	Kind         protoreflect.Kind
+	IsPacked     bool
+	Default      protoreflect.Value
+	MessageType  protoreflect.MessageDescriptor
+	EnumType     protoreflect.EnumDescriptor
+	ExtendedType protoreflect.MessageDescriptor
+
+	*extensionMeta
+}
+
+// Enum is a constructor for protoreflect.EnumDescriptor.
+type Enum struct {
+	Name   protoreflect.Name
+	Values []EnumValue
+
+	*enumMeta
+}
+
+// EnumValue is a constructor for protoreflect.EnumValueDescriptor.
+type EnumValue struct {
+	Name   protoreflect.Name
+	Number protoreflect.EnumNumber
+
+	*enumValueMeta
+}
+
+// Service is a constructor for protoreflect.ServiceDescriptor.
+type Service struct {
+	Name    protoreflect.Name
+	Methods []Method
+
+	*serviceMeta
+}
+
+// Method is a constructor for protoreflect.MethodDescriptor.
+type Method struct {
+	Name              protoreflect.Name
+	InputType         protoreflect.MessageDescriptor
+	OutputType        protoreflect.MessageDescriptor
+	IsStreamingClient bool
+	IsStreamingServer bool
+
+	*methodMeta
+}
diff --git a/reflect/prototype/protofile_list.go b/reflect/prototype/protofile_list.go
new file mode 100644
index 0000000..52570c0
--- /dev/null
+++ b/reflect/prototype/protofile_list.go
@@ -0,0 +1,100 @@
+// Copyright 2018 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 prototype
+
+import (
+	"fmt"
+	"sync"
+
+	"google.golang.org/proto/internal/pragma"
+	"google.golang.org/proto/internal/set"
+	pref "google.golang.org/proto/reflect/protoreflect"
+)
+
+type numbersMeta struct {
+	once sync.Once
+	ns   []pref.FieldNumber
+	nss  set.Ints
+}
+type numbers numbersMeta
+
+func (p *numbersMeta) lazyInit(fs []Field) *numbers {
+	p.once.Do(func() {
+		for i := range fs {
+			if f := &fs[i]; f.Cardinality == pref.Required {
+				p.ns = append(p.ns, f.Number)
+				p.nss.Set(uint64(f.Number))
+			}
+		}
+	})
+	return (*numbers)(p)
+}
+func (p *numbers) Len() int                            { return len(p.ns) }
+func (p *numbers) Get(i int) pref.FieldNumber          { return p.ns[i] }
+func (p *numbers) Has(n pref.FieldNumber) bool         { return p.nss.Has(uint64(n)) }
+func (p *numbers) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+func (p *numbers) ProtoInternal(pragma.DoNotImplement) {}
+
+type ranges [][2]pref.FieldNumber
+
+func (p *ranges) Len() int                      { return len(*p) }
+func (p *ranges) Get(i int) [2]pref.FieldNumber { return (*p)[i] }
+func (p *ranges) Has(n pref.FieldNumber) bool {
+	for _, r := range *p {
+		if r[0] <= n && n < r[1] {
+			return true
+		}
+	}
+	return false
+}
+func (p *ranges) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+func (p *ranges) ProtoInternal(pragma.DoNotImplement) {}
+
+type fileImports []pref.FileImport
+
+func (p *fileImports) Len() int                            { return len(*p) }
+func (p *fileImports) Get(i int) pref.FileImport           { return (*p)[i] }
+func (p *fileImports) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+func (p *fileImports) ProtoInternal(pragma.DoNotImplement) {}
+
+type oneofFieldsMeta struct {
+	once   sync.Once
+	typs   []pref.FieldDescriptor
+	byName map[pref.Name]pref.FieldDescriptor
+	byJSON map[string]pref.FieldDescriptor
+	byNum  map[pref.FieldNumber]pref.FieldDescriptor
+}
+type oneofFields oneofFieldsMeta
+
+func (p *oneofFieldsMeta) lazyInit(parent pref.Descriptor) *oneofFields {
+	p.once.Do(func() {
+		otyp := parent.(pref.OneofDescriptor)
+		mtyp, _ := parent.Parent()
+		fs := mtyp.(pref.MessageDescriptor).Fields()
+		for i := 0; i < fs.Len(); i++ {
+			if f := fs.Get(i); otyp == f.OneofType() {
+				p.typs = append(p.typs, f)
+			}
+		}
+		if len(p.typs) > 0 {
+			p.byName = make(map[pref.Name]pref.FieldDescriptor, len(p.typs))
+			p.byJSON = make(map[string]pref.FieldDescriptor, len(p.typs))
+			p.byNum = make(map[pref.FieldNumber]pref.FieldDescriptor, len(p.typs))
+			for _, f := range p.typs {
+				p.byName[f.Name()] = f
+				p.byJSON[f.JSONName()] = f
+				p.byNum[f.Number()] = f
+			}
+		}
+	})
+	return (*oneofFields)(p)
+}
+func (p *oneofFields) Len() int                                         { return len(p.typs) }
+func (p *oneofFields) Get(i int) pref.FieldDescriptor                   { return p.typs[i] }
+func (p *oneofFields) ByName(s pref.Name) pref.FieldDescriptor          { return p.byName[s] }
+func (p *oneofFields) ByJSONName(s string) pref.FieldDescriptor         { return p.byJSON[s] }
+func (p *oneofFields) ByNumber(n pref.FieldNumber) pref.FieldDescriptor { return p.byNum[n] }
+func (p *oneofFields) Format(s fmt.State, r rune)                       { formatList(s, r, p) }
+func (p *oneofFields) ProtoInternal(pragma.DoNotImplement)              {}
diff --git a/reflect/prototype/protofile_list_gen.go b/reflect/prototype/protofile_list_gen.go
new file mode 100644
index 0000000..7a3f564
--- /dev/null
+++ b/reflect/prototype/protofile_list_gen.go
@@ -0,0 +1,444 @@
+// Copyright 2018 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.
+
+// Code generated by generate-types. DO NOT EDIT.
+
+package prototype
+
+import (
+	"fmt"
+	"sync"
+
+	"google.golang.org/proto/internal/pragma"
+	"google.golang.org/proto/reflect/protoreflect"
+)
+
+type messagesMeta struct {
+	once     sync.Once
+	typs     []Message
+	nameOnce sync.Once
+	byName   map[protoreflect.Name]*Message
+}
+type messages messagesMeta
+
+func (p *messagesMeta) lazyInit(parent protoreflect.Descriptor, ts []Message) *messages {
+	p.once.Do(func() {
+		nb := nameBuilderPool.Get().(*nameBuilder)
+		defer nameBuilderPool.Put(nb)
+		metas := make([]messageMeta, len(ts))
+		for i := range ts {
+			t := &ts[i]
+			if t.messageMeta != nil {
+				panic("already initialized")
+			}
+			t.messageMeta = &metas[i]
+			t.inheritedMeta.init(nb, parent, t.Name, false)
+		}
+		p.typs = ts
+	})
+	return (*messages)(p)
+}
+func (p *messages) Len() int                                 { return len(p.typs) }
+func (p *messages) Get(i int) protoreflect.MessageDescriptor { return messageDesc{&p.typs[i]} }
+func (p *messages) ByName(s protoreflect.Name) protoreflect.MessageDescriptor {
+	p.nameOnce.Do(func() {
+		if len(p.typs) > 0 {
+			p.byName = make(map[protoreflect.Name]*Message, len(p.typs))
+			for i := range p.typs {
+				t := &p.typs[i]
+				p.byName[t.Name] = t
+			}
+		}
+	})
+	t := p.byName[s]
+	if t == nil {
+		return nil
+	}
+	return messageDesc{t}
+}
+func (p *messages) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+func (p *messages) ProtoInternal(pragma.DoNotImplement) {}
+
+type fieldsMeta struct {
+	once     sync.Once
+	typs     []Field
+	nameOnce sync.Once
+	byName   map[protoreflect.Name]*Field
+	jsonOnce sync.Once
+	byJSON   map[string]*Field
+	numOnce  sync.Once
+	byNum    map[protoreflect.FieldNumber]*Field
+}
+type fields fieldsMeta
+
+func (p *fieldsMeta) lazyInit(parent protoreflect.Descriptor, ts []Field) *fields {
+	p.once.Do(func() {
+		nb := nameBuilderPool.Get().(*nameBuilder)
+		defer nameBuilderPool.Put(nb)
+		metas := make([]fieldMeta, len(ts))
+		for i := range ts {
+			t := &ts[i]
+			if t.fieldMeta != nil {
+				panic("already initialized")
+			}
+			t.fieldMeta = &metas[i]
+			t.inheritedMeta.init(nb, parent, t.Name, false)
+		}
+		p.typs = ts
+	})
+	return (*fields)(p)
+}
+func (p *fields) Len() int                               { return len(p.typs) }
+func (p *fields) Get(i int) protoreflect.FieldDescriptor { return fieldDesc{&p.typs[i]} }
+func (p *fields) ByName(s protoreflect.Name) protoreflect.FieldDescriptor {
+	p.nameOnce.Do(func() {
+		if len(p.typs) > 0 {
+			p.byName = make(map[protoreflect.Name]*Field, len(p.typs))
+			for i := range p.typs {
+				t := &p.typs[i]
+				p.byName[t.Name] = t
+			}
+		}
+	})
+	t := p.byName[s]
+	if t == nil {
+		return nil
+	}
+	return fieldDesc{t}
+}
+func (p *fields) ByJSONName(s string) protoreflect.FieldDescriptor {
+	p.jsonOnce.Do(func() {
+		if len(p.typs) > 0 {
+			p.byJSON = make(map[string]*Field, len(p.typs))
+			for i := range p.typs {
+				t := &p.typs[i]
+				s := fieldDesc{t}.JSONName()
+				if _, ok := p.byJSON[s]; !ok {
+					p.byJSON[s] = t
+				}
+			}
+		}
+	})
+	t := p.byJSON[s]
+	if t == nil {
+		return nil
+	}
+	return fieldDesc{t}
+}
+func (p *fields) ByNumber(n protoreflect.FieldNumber) protoreflect.FieldDescriptor {
+	p.numOnce.Do(func() {
+		if len(p.typs) > 0 {
+			p.byNum = make(map[protoreflect.FieldNumber]*Field, len(p.typs))
+			for i := range p.typs {
+				t := &p.typs[i]
+				if _, ok := p.byNum[t.Number]; !ok {
+					p.byNum[t.Number] = t
+				}
+			}
+		}
+	})
+	t := p.byNum[n]
+	if t == nil {
+		return nil
+	}
+	return fieldDesc{t}
+}
+func (p *fields) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+func (p *fields) ProtoInternal(pragma.DoNotImplement) {}
+
+type oneofsMeta struct {
+	once     sync.Once
+	typs     []Oneof
+	nameOnce sync.Once
+	byName   map[protoreflect.Name]*Oneof
+}
+type oneofs oneofsMeta
+
+func (p *oneofsMeta) lazyInit(parent protoreflect.Descriptor, ts []Oneof) *oneofs {
+	p.once.Do(func() {
+		nb := nameBuilderPool.Get().(*nameBuilder)
+		defer nameBuilderPool.Put(nb)
+		metas := make([]oneofMeta, len(ts))
+		for i := range ts {
+			t := &ts[i]
+			if t.oneofMeta != nil {
+				panic("already initialized")
+			}
+			t.oneofMeta = &metas[i]
+			t.inheritedMeta.init(nb, parent, t.Name, false)
+		}
+		p.typs = ts
+	})
+	return (*oneofs)(p)
+}
+func (p *oneofs) Len() int                               { return len(p.typs) }
+func (p *oneofs) Get(i int) protoreflect.OneofDescriptor { return oneofDesc{&p.typs[i]} }
+func (p *oneofs) ByName(s protoreflect.Name) protoreflect.OneofDescriptor {
+	p.nameOnce.Do(func() {
+		if len(p.typs) > 0 {
+			p.byName = make(map[protoreflect.Name]*Oneof, len(p.typs))
+			for i := range p.typs {
+				t := &p.typs[i]
+				p.byName[t.Name] = t
+			}
+		}
+	})
+	t := p.byName[s]
+	if t == nil {
+		return nil
+	}
+	return oneofDesc{t}
+}
+func (p *oneofs) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+func (p *oneofs) ProtoInternal(pragma.DoNotImplement) {}
+
+type extensionsMeta struct {
+	once     sync.Once
+	typs     []Extension
+	nameOnce sync.Once
+	byName   map[protoreflect.Name]*Extension
+}
+type extensions extensionsMeta
+
+func (p *extensionsMeta) lazyInit(parent protoreflect.Descriptor, ts []Extension) *extensions {
+	p.once.Do(func() {
+		nb := nameBuilderPool.Get().(*nameBuilder)
+		defer nameBuilderPool.Put(nb)
+		metas := make([]extensionMeta, len(ts))
+		for i := range ts {
+			t := &ts[i]
+			if t.extensionMeta != nil {
+				panic("already initialized")
+			}
+			t.extensionMeta = &metas[i]
+			t.inheritedMeta.init(nb, parent, t.Name, false)
+		}
+		p.typs = ts
+	})
+	return (*extensions)(p)
+}
+func (p *extensions) Len() int                                   { return len(p.typs) }
+func (p *extensions) Get(i int) protoreflect.ExtensionDescriptor { return extensionDesc{&p.typs[i]} }
+func (p *extensions) ByName(s protoreflect.Name) protoreflect.ExtensionDescriptor {
+	p.nameOnce.Do(func() {
+		if len(p.typs) > 0 {
+			p.byName = make(map[protoreflect.Name]*Extension, len(p.typs))
+			for i := range p.typs {
+				t := &p.typs[i]
+				p.byName[t.Name] = t
+			}
+		}
+	})
+	t := p.byName[s]
+	if t == nil {
+		return nil
+	}
+	return extensionDesc{t}
+}
+func (p *extensions) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+func (p *extensions) ProtoInternal(pragma.DoNotImplement) {}
+
+type enumsMeta struct {
+	once     sync.Once
+	typs     []Enum
+	nameOnce sync.Once
+	byName   map[protoreflect.Name]*Enum
+}
+type enums enumsMeta
+
+func (p *enumsMeta) lazyInit(parent protoreflect.Descriptor, ts []Enum) *enums {
+	p.once.Do(func() {
+		nb := nameBuilderPool.Get().(*nameBuilder)
+		defer nameBuilderPool.Put(nb)
+		metas := make([]enumMeta, len(ts))
+		for i := range ts {
+			t := &ts[i]
+			if t.enumMeta != nil {
+				panic("already initialized")
+			}
+			t.enumMeta = &metas[i]
+			t.inheritedMeta.init(nb, parent, t.Name, false)
+		}
+		p.typs = ts
+	})
+	return (*enums)(p)
+}
+func (p *enums) Len() int                              { return len(p.typs) }
+func (p *enums) Get(i int) protoreflect.EnumDescriptor { return enumDesc{&p.typs[i]} }
+func (p *enums) ByName(s protoreflect.Name) protoreflect.EnumDescriptor {
+	p.nameOnce.Do(func() {
+		if len(p.typs) > 0 {
+			p.byName = make(map[protoreflect.Name]*Enum, len(p.typs))
+			for i := range p.typs {
+				t := &p.typs[i]
+				p.byName[t.Name] = t
+			}
+		}
+	})
+	t := p.byName[s]
+	if t == nil {
+		return nil
+	}
+	return enumDesc{t}
+}
+func (p *enums) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+func (p *enums) ProtoInternal(pragma.DoNotImplement) {}
+
+type enumValuesMeta struct {
+	once     sync.Once
+	typs     []EnumValue
+	nameOnce sync.Once
+	byName   map[protoreflect.Name]*EnumValue
+	numOnce  sync.Once
+	byNum    map[protoreflect.EnumNumber]*EnumValue
+}
+type enumValues enumValuesMeta
+
+func (p *enumValuesMeta) lazyInit(parent protoreflect.Descriptor, ts []EnumValue) *enumValues {
+	p.once.Do(func() {
+		nb := nameBuilderPool.Get().(*nameBuilder)
+		defer nameBuilderPool.Put(nb)
+		metas := make([]enumValueMeta, len(ts))
+		for i := range ts {
+			t := &ts[i]
+			if t.enumValueMeta != nil {
+				panic("already initialized")
+			}
+			t.enumValueMeta = &metas[i]
+			t.inheritedMeta.init(nb, parent, t.Name, true)
+		}
+		p.typs = ts
+	})
+	return (*enumValues)(p)
+}
+func (p *enumValues) Len() int                                   { return len(p.typs) }
+func (p *enumValues) Get(i int) protoreflect.EnumValueDescriptor { return enumValueDesc{&p.typs[i]} }
+func (p *enumValues) ByName(s protoreflect.Name) protoreflect.EnumValueDescriptor {
+	p.nameOnce.Do(func() {
+		if len(p.typs) > 0 {
+			p.byName = make(map[protoreflect.Name]*EnumValue, len(p.typs))
+			for i := range p.typs {
+				t := &p.typs[i]
+				p.byName[t.Name] = t
+			}
+		}
+	})
+	t := p.byName[s]
+	if t == nil {
+		return nil
+	}
+	return enumValueDesc{t}
+}
+func (p *enumValues) ByNumber(n protoreflect.EnumNumber) protoreflect.EnumValueDescriptor {
+	p.numOnce.Do(func() {
+		if len(p.typs) > 0 {
+			p.byNum = make(map[protoreflect.EnumNumber]*EnumValue, len(p.typs))
+			for i := range p.typs {
+				t := &p.typs[i]
+				if _, ok := p.byNum[t.Number]; !ok {
+					p.byNum[t.Number] = t
+				}
+			}
+		}
+	})
+	t := p.byNum[n]
+	if t == nil {
+		return nil
+	}
+	return enumValueDesc{t}
+}
+func (p *enumValues) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+func (p *enumValues) ProtoInternal(pragma.DoNotImplement) {}
+
+type servicesMeta struct {
+	once     sync.Once
+	typs     []Service
+	nameOnce sync.Once
+	byName   map[protoreflect.Name]*Service
+}
+type services servicesMeta
+
+func (p *servicesMeta) lazyInit(parent protoreflect.Descriptor, ts []Service) *services {
+	p.once.Do(func() {
+		nb := nameBuilderPool.Get().(*nameBuilder)
+		defer nameBuilderPool.Put(nb)
+		metas := make([]serviceMeta, len(ts))
+		for i := range ts {
+			t := &ts[i]
+			if t.serviceMeta != nil {
+				panic("already initialized")
+			}
+			t.serviceMeta = &metas[i]
+			t.inheritedMeta.init(nb, parent, t.Name, false)
+		}
+		p.typs = ts
+	})
+	return (*services)(p)
+}
+func (p *services) Len() int                                 { return len(p.typs) }
+func (p *services) Get(i int) protoreflect.ServiceDescriptor { return serviceDesc{&p.typs[i]} }
+func (p *services) ByName(s protoreflect.Name) protoreflect.ServiceDescriptor {
+	p.nameOnce.Do(func() {
+		if len(p.typs) > 0 {
+			p.byName = make(map[protoreflect.Name]*Service, len(p.typs))
+			for i := range p.typs {
+				t := &p.typs[i]
+				p.byName[t.Name] = t
+			}
+		}
+	})
+	t := p.byName[s]
+	if t == nil {
+		return nil
+	}
+	return serviceDesc{t}
+}
+func (p *services) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+func (p *services) ProtoInternal(pragma.DoNotImplement) {}
+
+type methodsMeta struct {
+	once     sync.Once
+	typs     []Method
+	nameOnce sync.Once
+	byName   map[protoreflect.Name]*Method
+}
+type methods methodsMeta
+
+func (p *methodsMeta) lazyInit(parent protoreflect.Descriptor, ts []Method) *methods {
+	p.once.Do(func() {
+		nb := nameBuilderPool.Get().(*nameBuilder)
+		defer nameBuilderPool.Put(nb)
+		metas := make([]methodMeta, len(ts))
+		for i := range ts {
+			t := &ts[i]
+			if t.methodMeta != nil {
+				panic("already initialized")
+			}
+			t.methodMeta = &metas[i]
+			t.inheritedMeta.init(nb, parent, t.Name, false)
+		}
+		p.typs = ts
+	})
+	return (*methods)(p)
+}
+func (p *methods) Len() int                                { return len(p.typs) }
+func (p *methods) Get(i int) protoreflect.MethodDescriptor { return methodDesc{&p.typs[i]} }
+func (p *methods) ByName(s protoreflect.Name) protoreflect.MethodDescriptor {
+	p.nameOnce.Do(func() {
+		if len(p.typs) > 0 {
+			p.byName = make(map[protoreflect.Name]*Method, len(p.typs))
+			for i := range p.typs {
+				t := &p.typs[i]
+				p.byName[t.Name] = t
+			}
+		}
+	})
+	t := p.byName[s]
+	if t == nil {
+		return nil
+	}
+	return methodDesc{t}
+}
+func (p *methods) Format(s fmt.State, r rune)          { formatList(s, r, p) }
+func (p *methods) ProtoInternal(pragma.DoNotImplement) {}
diff --git a/reflect/prototype/protofile_type.go b/reflect/prototype/protofile_type.go
new file mode 100644
index 0000000..2929232
--- /dev/null
+++ b/reflect/prototype/protofile_type.go
@@ -0,0 +1,506 @@
+// Copyright 2018 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 prototype
+
+import (
+	"bytes"
+	"fmt"
+	"strings"
+	"sync"
+
+	"google.golang.org/proto/internal/pragma"
+	pref "google.golang.org/proto/reflect/protoreflect"
+)
+
+// inheritedMeta is information inherited from the parent.
+type inheritedMeta struct {
+	parent   pref.Descriptor
+	syntax   pref.Syntax
+	fullName pref.FullName
+
+	desc descriptorSubMeta
+	opts descriptorOptionsMeta
+}
+
+func (m *inheritedMeta) init(nb *nameBuilder, parent pref.Descriptor, name pref.Name, child bool) {
+	// Most descriptors are namespaced as a child of their parent.
+	// However, EnumValues are the exception in that they are namespaced
+	// as a sibling of the parent Enum type.
+	prefix := parent.FullName()
+	if child {
+		prefix = prefix.Parent()
+	}
+
+	m.parent = parent
+	m.syntax = parent.Syntax()
+	m.fullName = nb.Append(prefix, name)
+}
+
+type fileMeta struct {
+	desc descriptorFileMeta
+	opts descriptorOptionsMeta
+
+	ms messagesMeta
+	es enumsMeta
+	xs extensionsMeta
+	ss servicesMeta
+}
+type fileDesc struct{ f *File }
+
+func newFile(f *File) fileDesc {
+	if f.fileMeta != nil {
+		panic("already initialized")
+	}
+	f.fileMeta = new(fileMeta)
+	return fileDesc{f}
+}
+func (t fileDesc) Parent() (pref.Descriptor, bool)                   { return nil, false }
+func (t fileDesc) Syntax() pref.Syntax                               { return t.f.Syntax }
+func (t fileDesc) Name() pref.Name                                   { return t.f.Package.Name() }
+func (t fileDesc) FullName() pref.FullName                           { return t.f.Package }
+func (t fileDesc) IsPlaceholder() bool                               { return false }
+func (t fileDesc) DescriptorProto() (pref.Message, bool)             { return t.f.desc.lazyInit(t) }
+func (t fileDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.f.opts.lazyInit(t) }
+func (t fileDesc) Path() string                                      { return t.f.Path }
+func (t fileDesc) Package() pref.FullName                            { return t.f.Package }
+func (t fileDesc) Imports() pref.FileImports                         { return (*fileImports)(&t.f.Imports) }
+func (t fileDesc) Messages() pref.MessageDescriptors                 { return t.f.ms.lazyInit(t, t.f.Messages) }
+func (t fileDesc) Enums() pref.EnumDescriptors                       { return t.f.es.lazyInit(t, t.f.Enums) }
+func (t fileDesc) Extensions() pref.ExtensionDescriptors             { return t.f.xs.lazyInit(t, t.f.Extensions) }
+func (t fileDesc) Services() pref.ServiceDescriptors                 { return t.f.ss.lazyInit(t, t.f.Services) }
+func (t fileDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
+func (t fileDesc) ProtoType(pref.FileDescriptor)                     {}
+func (t fileDesc) ProtoInternal(pragma.DoNotImplement)               {}
+
+type messageMeta struct {
+	inheritedMeta
+
+	fs fieldsMeta
+	os oneofsMeta
+	ns numbersMeta
+	ms messagesMeta
+	es enumsMeta
+	xs extensionsMeta
+}
+type messageDesc struct{ m *Message }
+
+func (t messageDesc) Parent() (pref.Descriptor, bool)                   { return t.m.parent, true }
+func (t messageDesc) Syntax() pref.Syntax                               { return t.m.syntax }
+func (t messageDesc) Name() pref.Name                                   { return t.m.Name }
+func (t messageDesc) FullName() pref.FullName                           { return t.m.fullName }
+func (t messageDesc) IsPlaceholder() bool                               { return false }
+func (t messageDesc) DescriptorProto() (pref.Message, bool)             { return t.m.desc.lazyInit(t) }
+func (t messageDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.m.opts.lazyInit(t) }
+func (t messageDesc) IsMapEntry() bool                                  { return t.m.IsMapEntry }
+func (t messageDesc) Fields() pref.FieldDescriptors                     { return t.m.fs.lazyInit(t, t.m.Fields) }
+func (t messageDesc) Oneofs() pref.OneofDescriptors                     { return t.m.os.lazyInit(t, t.m.Oneofs) }
+func (t messageDesc) RequiredNumbers() pref.FieldNumbers                { return t.m.ns.lazyInit(t.m.Fields) }
+func (t messageDesc) ExtensionRanges() pref.FieldRanges                 { return (*ranges)(&t.m.ExtensionRanges) }
+func (t messageDesc) Messages() pref.MessageDescriptors                 { return t.m.ms.lazyInit(t, t.m.Messages) }
+func (t messageDesc) Enums() pref.EnumDescriptors                       { return t.m.es.lazyInit(t, t.m.Enums) }
+func (t messageDesc) Extensions() pref.ExtensionDescriptors             { return t.m.xs.lazyInit(t, t.m.Extensions) }
+func (t messageDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
+func (t messageDesc) ProtoType(pref.MessageDescriptor)                  {}
+func (t messageDesc) ProtoInternal(pragma.DoNotImplement)               {}
+
+type fieldMeta struct {
+	inheritedMeta
+
+	js jsonName
+	dv defaultValue
+	ot oneofReference
+	mt messageReference
+	et enumReference
+}
+type fieldDesc struct{ f *Field }
+
+func (t fieldDesc) Parent() (pref.Descriptor, bool)                   { return t.f.parent, true }
+func (t fieldDesc) Syntax() pref.Syntax                               { return t.f.syntax }
+func (t fieldDesc) Name() pref.Name                                   { return t.f.Name }
+func (t fieldDesc) FullName() pref.FullName                           { return t.f.fullName }
+func (t fieldDesc) IsPlaceholder() bool                               { return false }
+func (t fieldDesc) DescriptorProto() (pref.Message, bool)             { return t.f.desc.lazyInit(t) }
+func (t fieldDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.f.opts.lazyInit(t) }
+func (t fieldDesc) Number() pref.FieldNumber                          { return t.f.Number }
+func (t fieldDesc) Cardinality() pref.Cardinality                     { return t.f.Cardinality }
+func (t fieldDesc) Kind() pref.Kind                                   { return t.f.Kind }
+func (t fieldDesc) JSONName() string                                  { return t.f.js.lazyInit(t.f) }
+func (t fieldDesc) IsPacked() bool                                    { return t.f.IsPacked }
+func (t fieldDesc) IsMap() bool                                       { return isMap(t) }
+func (t fieldDesc) IsWeak() bool                                      { return t.f.IsWeak }
+func (t fieldDesc) Default() pref.Value                               { return t.f.dv.lazyInit(t, t.f.Default) }
+func (t fieldDesc) OneofType() pref.OneofDescriptor                   { return t.f.ot.lazyInit(t, t.f.OneofName) }
+func (t fieldDesc) ExtendedType() pref.MessageDescriptor              { return nil }
+func (t fieldDesc) MessageType() pref.MessageDescriptor               { return t.f.mt.lazyInit(t, &t.f.MessageType) }
+func (t fieldDesc) EnumType() pref.EnumDescriptor                     { return t.f.et.lazyInit(t, &t.f.EnumType) }
+func (t fieldDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
+func (t fieldDesc) ProtoType(pref.FieldDescriptor)                    {}
+func (t fieldDesc) ProtoInternal(pragma.DoNotImplement)               {}
+
+func isMap(t pref.FieldDescriptor) bool {
+	if t.Cardinality() == pref.Repeated && t.Kind() == pref.MessageKind {
+		if mt := t.MessageType(); mt != nil {
+			return mt.IsMapEntry()
+		}
+	}
+	return false
+}
+
+type jsonName struct{ once sync.Once }
+
+func (p *jsonName) lazyInit(f *Field) string {
+	p.once.Do(func() {
+		// TODO: We may need to share this logic with jsonpb for implementation
+		// of the FieldMask well-known type.
+		if f.JSONName != "" {
+			return
+		}
+		var b []byte
+		var wasUnderscore bool
+		for i := 0; i < len(f.Name); i++ { // proto identifiers are always ASCII
+			c := f.Name[i]
+			if c != '_' {
+				isLower := 'a' <= c && c <= 'z'
+				if wasUnderscore && isLower {
+					c -= 'a' - 'A'
+				}
+				b = append(b, c)
+			}
+			wasUnderscore = c == '_'
+		}
+		f.JSONName = string(b)
+	})
+	return f.JSONName
+}
+
+// oneofReference resolves the name of a oneof by searching the parent
+// message for the matching OneofDescriptor declaration.
+type oneofReference struct {
+	once sync.Once
+	otyp pref.OneofDescriptor
+}
+
+func (p *oneofReference) lazyInit(parent pref.Descriptor, name pref.Name) pref.OneofDescriptor {
+	p.once.Do(func() {
+		if name != "" {
+			mtyp, _ := parent.Parent()
+			p.otyp = mtyp.(pref.MessageDescriptor).Oneofs().ByName(name)
+			// TODO: We need validate to detect this mismatch.
+		}
+	})
+	return p.otyp
+}
+
+type oneofMeta struct {
+	inheritedMeta
+
+	fs oneofFieldsMeta
+}
+type oneofDesc struct{ o *Oneof }
+
+func (t oneofDesc) Parent() (pref.Descriptor, bool)                   { return t.o.parent, true }
+func (t oneofDesc) Syntax() pref.Syntax                               { return t.o.syntax }
+func (t oneofDesc) Name() pref.Name                                   { return t.o.Name }
+func (t oneofDesc) FullName() pref.FullName                           { return t.o.fullName }
+func (t oneofDesc) IsPlaceholder() bool                               { return false }
+func (t oneofDesc) DescriptorProto() (pref.Message, bool)             { return t.o.desc.lazyInit(t) }
+func (t oneofDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.o.opts.lazyInit(t) }
+func (t oneofDesc) Fields() pref.FieldDescriptors                     { return t.o.fs.lazyInit(t) }
+func (t oneofDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
+func (t oneofDesc) ProtoType(pref.OneofDescriptor)                    {}
+func (t oneofDesc) ProtoInternal(pragma.DoNotImplement)               {}
+
+type extensionMeta struct {
+	inheritedMeta
+
+	dv defaultValue
+	xt messageReference
+	mt messageReference
+	et enumReference
+}
+type extensionDesc struct{ x *Extension }
+
+func (t extensionDesc) Parent() (pref.Descriptor, bool)                   { return t.x.parent, true }
+func (t extensionDesc) Syntax() pref.Syntax                               { return t.x.syntax }
+func (t extensionDesc) Name() pref.Name                                   { return t.x.Name }
+func (t extensionDesc) FullName() pref.FullName                           { return t.x.fullName }
+func (t extensionDesc) IsPlaceholder() bool                               { return false }
+func (t extensionDesc) DescriptorProto() (pref.Message, bool)             { return t.x.desc.lazyInit(t) }
+func (t extensionDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.x.opts.lazyInit(t) }
+func (t extensionDesc) Number() pref.FieldNumber                          { return t.x.Number }
+func (t extensionDesc) Cardinality() pref.Cardinality                     { return t.x.Cardinality }
+func (t extensionDesc) Kind() pref.Kind                                   { return t.x.Kind }
+func (t extensionDesc) JSONName() string                                  { return "" }
+func (t extensionDesc) IsPacked() bool                                    { return t.x.IsPacked }
+func (t extensionDesc) IsMap() bool                                       { return false }
+func (t extensionDesc) IsWeak() bool                                      { return false }
+func (t extensionDesc) Default() pref.Value                               { return t.x.dv.lazyInit(t, t.x.Default) }
+func (t extensionDesc) OneofType() pref.OneofDescriptor                   { return nil }
+func (t extensionDesc) ExtendedType() pref.MessageDescriptor {
+	return t.x.xt.lazyInit(t, &t.x.ExtendedType)
+}
+func (t extensionDesc) MessageType() pref.MessageDescriptor {
+	return t.x.mt.lazyInit(t, &t.x.MessageType)
+}
+func (t extensionDesc) EnumType() pref.EnumDescriptor       { return t.x.et.lazyInit(t, &t.x.EnumType) }
+func (t extensionDesc) Format(s fmt.State, r rune)          { formatDesc(s, r, t) }
+func (t extensionDesc) ProtoType(pref.FieldDescriptor)      {}
+func (t extensionDesc) ProtoInternal(pragma.DoNotImplement) {}
+
+type enumMeta struct {
+	inheritedMeta
+
+	vs enumValuesMeta
+}
+type enumDesc struct{ e *Enum }
+
+func (t enumDesc) Parent() (pref.Descriptor, bool)                   { return t.e.parent, true }
+func (t enumDesc) Syntax() pref.Syntax                               { return t.e.syntax }
+func (t enumDesc) Name() pref.Name                                   { return t.e.Name }
+func (t enumDesc) FullName() pref.FullName                           { return t.e.fullName }
+func (t enumDesc) IsPlaceholder() bool                               { return false }
+func (t enumDesc) DescriptorProto() (pref.Message, bool)             { return t.e.desc.lazyInit(t) }
+func (t enumDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.e.opts.lazyInit(t) }
+func (t enumDesc) Values() pref.EnumValueDescriptors                 { return t.e.vs.lazyInit(t, t.e.Values) }
+func (t enumDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
+func (t enumDesc) ProtoType(pref.EnumDescriptor)                     {}
+func (t enumDesc) ProtoInternal(pragma.DoNotImplement)               {}
+
+type enumValueMeta struct {
+	inheritedMeta
+}
+type enumValueDesc struct{ v *EnumValue }
+
+func (t enumValueDesc) Parent() (pref.Descriptor, bool)                   { return t.v.parent, true }
+func (t enumValueDesc) Syntax() pref.Syntax                               { return t.v.syntax }
+func (t enumValueDesc) Name() pref.Name                                   { return t.v.Name }
+func (t enumValueDesc) FullName() pref.FullName                           { return t.v.fullName }
+func (t enumValueDesc) IsPlaceholder() bool                               { return false }
+func (t enumValueDesc) DescriptorProto() (pref.Message, bool)             { return t.v.desc.lazyInit(t) }
+func (t enumValueDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.v.opts.lazyInit(t) }
+func (t enumValueDesc) Number() pref.EnumNumber                           { return t.v.Number }
+func (t enumValueDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
+func (t enumValueDesc) ProtoType(pref.EnumValueDescriptor)                {}
+func (t enumValueDesc) ProtoInternal(pragma.DoNotImplement)               {}
+
+type serviceMeta struct {
+	inheritedMeta
+
+	ms methodsMeta
+}
+type serviceDesc struct{ s *Service }
+
+func (t serviceDesc) Parent() (pref.Descriptor, bool)                   { return t.s.parent, true }
+func (t serviceDesc) Syntax() pref.Syntax                               { return t.s.syntax }
+func (t serviceDesc) Name() pref.Name                                   { return t.s.Name }
+func (t serviceDesc) FullName() pref.FullName                           { return t.s.fullName }
+func (t serviceDesc) IsPlaceholder() bool                               { return false }
+func (t serviceDesc) DescriptorProto() (pref.Message, bool)             { return t.s.desc.lazyInit(t) }
+func (t serviceDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.s.opts.lazyInit(t) }
+func (t serviceDesc) Methods() pref.MethodDescriptors                   { return t.s.ms.lazyInit(t, t.s.Methods) }
+func (t serviceDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
+func (t serviceDesc) ProtoType(pref.ServiceDescriptor)                  {}
+func (t serviceDesc) ProtoInternal(pragma.DoNotImplement)               {}
+
+type methodMeta struct {
+	inheritedMeta
+
+	mit messageReference
+	mot messageReference
+}
+type methodDesc struct{ m *Method }
+
+func (t methodDesc) Parent() (pref.Descriptor, bool)                   { return t.m.parent, true }
+func (t methodDesc) Syntax() pref.Syntax                               { return t.m.syntax }
+func (t methodDesc) Name() pref.Name                                   { return t.m.Name }
+func (t methodDesc) FullName() pref.FullName                           { return t.m.fullName }
+func (t methodDesc) IsPlaceholder() bool                               { return false }
+func (t methodDesc) DescriptorProto() (pref.Message, bool)             { return t.m.desc.lazyInit(t) }
+func (t methodDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.m.opts.lazyInit(t) }
+func (t methodDesc) InputType() pref.MessageDescriptor                 { return t.m.mit.lazyInit(t, &t.m.InputType) }
+func (t methodDesc) OutputType() pref.MessageDescriptor                { return t.m.mot.lazyInit(t, &t.m.OutputType) }
+func (t methodDesc) IsStreamingClient() bool                           { return t.m.IsStreamingClient }
+func (t methodDesc) IsStreamingServer() bool                           { return t.m.IsStreamingServer }
+func (t methodDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
+func (t methodDesc) ProtoType(pref.MethodDescriptor)                   {}
+func (t methodDesc) ProtoInternal(pragma.DoNotImplement)               {}
+
+type defaultValue struct {
+	once sync.Once
+	val  pref.Value
+	buf  []byte
+}
+
+var (
+	zeroBool    = pref.ValueOf(false)
+	zeroInt32   = pref.ValueOf(int32(0))
+	zeroInt64   = pref.ValueOf(int64(0))
+	zeroUint32  = pref.ValueOf(uint32(0))
+	zeroUint64  = pref.ValueOf(uint64(0))
+	zeroFloat32 = pref.ValueOf(float32(0))
+	zeroFloat64 = pref.ValueOf(float64(0))
+	zeroString  = pref.ValueOf(string(""))
+	zeroBytes   = pref.ValueOf([]byte(nil))
+	zeroEnum    = pref.ValueOf(pref.EnumNumber(0))
+)
+
+func (p *defaultValue) lazyInit(t pref.FieldDescriptor, v pref.Value) pref.Value {
+	p.once.Do(func() {
+		p.val = v
+		if !v.IsNull() {
+			if t.Kind() == pref.BytesKind {
+				if b, ok := v.Interface().([]byte); ok && len(b) > 0 {
+					p.buf = append([]byte(nil), b...)
+				}
+			}
+			return
+		}
+		switch t.Kind() {
+		case pref.BoolKind:
+			p.val = zeroBool
+		case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
+			p.val = zeroInt32
+		case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
+			p.val = zeroInt64
+		case pref.Uint32Kind, pref.Fixed32Kind:
+			p.val = zeroUint32
+		case pref.Uint64Kind, pref.Fixed64Kind:
+			p.val = zeroUint64
+		case pref.FloatKind:
+			p.val = zeroFloat32
+		case pref.DoubleKind:
+			p.val = zeroFloat64
+		case pref.StringKind:
+			p.val = zeroString
+		case pref.BytesKind:
+			p.val = zeroBytes
+		case pref.EnumKind:
+			p.val = zeroEnum
+			if t.Syntax() == pref.Proto2 {
+				if et := t.EnumType(); et != nil {
+					if vs := et.Values(); vs.Len() > 0 {
+						p.val = pref.ValueOf(vs.Get(0).Number())
+					}
+				}
+			}
+		}
+	})
+	if len(p.buf) > 0 && !bytes.Equal(p.buf, p.val.Bytes()) {
+		// TODO: Avoid panic if we're running with the race detector and instead
+		// spawn a goroutine that periodically resets this value back to the
+		// original to induce a race that can be detected by the detector.
+		panic(fmt.Sprintf("proto: detected mutation on the default bytes for %v", t.FullName()))
+	}
+	return p.val
+}
+
+// messageReference resolves PlaceholderMessages that reference declarations
+// within the FileDescriptor tree that parent is a member of.
+type messageReference struct{ once sync.Once }
+
+func (p *messageReference) lazyInit(parent pref.Descriptor, pt *pref.MessageDescriptor) pref.MessageDescriptor {
+	p.once.Do(func() {
+		if t := *pt; t != nil && t.IsPlaceholder() {
+			if d, ok := resolveReference(parent, t.FullName()).(pref.MessageDescriptor); ok {
+				*pt = d
+			}
+		}
+	})
+	return *pt
+}
+
+// enumReference resolves PlaceholderEnums that reference declarations
+// within the FileDescriptor tree that parent is a member of.
+type enumReference struct{ once sync.Once }
+
+func (p *enumReference) lazyInit(parent pref.Descriptor, pt *pref.EnumDescriptor) pref.EnumDescriptor {
+	p.once.Do(func() {
+		if t := *pt; t != nil && t.IsPlaceholder() {
+			if d, ok := resolveReference(parent, t.FullName()).(pref.EnumDescriptor); ok {
+				*pt = d
+			}
+		}
+	})
+	return *pt
+}
+
+// resolveReference searches parent for the MessageDescriptor or EnumDescriptor
+// declaration identified by refName. This returns nil if not found.
+func resolveReference(parent pref.Descriptor, refName pref.FullName) pref.Descriptor {
+	// Ascend upwards until a prefix match is found.
+	cur := parent
+	for cur != nil {
+		curName := cur.FullName()
+		if strings.HasPrefix(string(refName), string(curName)) {
+			if len(refName) == len(curName) {
+				refName = refName[len(curName):]
+				break // e.g., refName: foo.firetruck, curName: foo.firetruck
+			} else if refName[len(curName)] == '.' {
+				refName = refName[len(curName)+len("."):]
+				break // e.g., refName: foo.firetruck.driver, curName: foo.firetruck
+			}
+			// No match. (e.g., refName: foo.firetruck, curName: foo.fire)
+		}
+		cur, _ = cur.Parent() // nil after ascending above FileDescriptor
+	}
+
+	// Descend downwards to resolve all relative names.
+	for cur != nil && len(refName) > 0 {
+		var head pref.Name
+		head, refName = pref.Name(refName), ""
+		if i := strings.IndexByte(string(head), '.'); i >= 0 {
+			head, refName = head[:i], pref.FullName(head[i+len("."):])
+		}
+
+		// Search the current descriptor for the nested declaration.
+		var next pref.Descriptor
+		if t, ok := cur.(interface {
+			Messages() pref.MessageDescriptors
+		}); ok && next == nil {
+			if d := t.Messages().ByName(head); d != nil {
+				next = d
+			}
+		}
+		if t, ok := cur.(interface {
+			Enums() pref.EnumDescriptors
+		}); ok && next == nil {
+			if d := t.Enums().ByName(head); d != nil {
+				next = d
+			}
+		}
+		cur = next // nil if not found
+	}
+	return cur
+}
+
+var nameBuilderPool = sync.Pool{
+	New: func() interface{} { return new(nameBuilder) },
+}
+
+type nameBuilder struct {
+	sb strings.Builder
+
+	// TODO: See https://golang.org/issue/26269
+	rem int // conservative approximation for Cap-Len
+}
+
+// Append is equivalent to protoreflect.FullName.Append, but is optimized for
+// large batches of operations where each name has a shared lifetime.
+func (b *nameBuilder) Append(prefix pref.FullName, name pref.Name) pref.FullName {
+	const batchSize = 1 << 12
+	n := len(prefix) + len(".") + len(name)
+	if b.rem < n {
+		b.sb.Reset()
+		b.sb.Grow(batchSize)
+		b.rem = batchSize - n
+	}
+	if !strings.HasSuffix(b.sb.String(), string(prefix)) {
+		b.sb.WriteString(string(prefix))
+	}
+	b.sb.WriteByte('.')
+	b.sb.WriteString(string(name))
+	s := b.sb.String()
+	return pref.FullName(strings.TrimPrefix(s[len(s)-n:], "."))
+}
diff --git a/reflect/prototype/standalone.go b/reflect/prototype/standalone.go
new file mode 100644
index 0000000..84fdac1
--- /dev/null
+++ b/reflect/prototype/standalone.go
@@ -0,0 +1,85 @@
+// Copyright 2018 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 prototype
+
+import (
+	"google.golang.org/proto/reflect/protoreflect"
+)
+
+// TODO: Should the constructors take in a value rather than a pointer?
+// TODO: Support initializing StandaloneMessage from a google.protobuf.Type?
+
+// StandaloneMessage is a constructor for a protoreflect.MessageDescriptor
+// that does not have a parent and has no child declarations.
+type StandaloneMessage struct {
+	Syntax          protoreflect.Syntax
+	FullName        protoreflect.FullName
+	IsMapEntry      bool
+	Fields          []Field
+	Oneofs          []Oneof
+	ExtensionRanges [][2]protoreflect.FieldNumber
+
+	fields fieldsMeta
+	oneofs oneofsMeta
+	nums   numbersMeta
+}
+
+// NewMessage creates a new protoreflect.MessageDescriptor.
+// The caller must relinquish full ownership of the input t and must not
+// access or mutate any fields.
+func NewMessage(t *StandaloneMessage) (protoreflect.MessageDescriptor, error) {
+	mt := standaloneMessage{t}
+	if err := validateMessage(mt); err != nil {
+		return nil, err
+	}
+	return mt, nil
+}
+
+// StandaloneEnum is a constructor for a protoreflect.EnumDescriptor
+// that does not have a parent.
+type StandaloneEnum struct {
+	Syntax   protoreflect.Syntax
+	FullName protoreflect.FullName
+	Values   []EnumValue
+
+	vals enumValuesMeta
+}
+
+// NewEnum creates a new protoreflect.EnumDescriptor.
+// The caller must relinquish full ownership of the input t and must not
+// access or mutate any fields.
+func NewEnum(t *StandaloneEnum) (protoreflect.EnumDescriptor, error) {
+	et := standaloneEnum{t}
+	if err := validateEnum(et); err != nil {
+		return nil, err
+	}
+	return et, nil
+}
+
+// StandaloneExtension is a constructor for a protoreflect.ExtensionDescriptor
+// that does not have a parent.
+type StandaloneExtension struct {
+	Syntax       protoreflect.Syntax
+	FullName     protoreflect.FullName
+	Number       protoreflect.FieldNumber
+	Cardinality  protoreflect.Cardinality
+	Kind         protoreflect.Kind
+	IsPacked     bool
+	Default      protoreflect.Value
+	MessageType  protoreflect.MessageDescriptor
+	EnumType     protoreflect.EnumDescriptor
+	ExtendedType protoreflect.MessageDescriptor
+}
+
+// NewExtension creates a new protoreflect.ExtensionDescriptor.
+// The caller must relinquish full ownership of the input t and must not
+// access or mutate any fields.
+func NewExtension(t *StandaloneExtension) (protoreflect.ExtensionDescriptor, error) {
+	xt := standaloneExtension{t}
+	if err := validateExtension(xt); err != nil {
+		return nil, err
+	}
+	return xt, nil
+}
diff --git a/reflect/prototype/standalone_type.go b/reflect/prototype/standalone_type.go
new file mode 100644
index 0000000..0009ebe
--- /dev/null
+++ b/reflect/prototype/standalone_type.go
@@ -0,0 +1,72 @@
+// Copyright 2018 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 prototype
+
+import (
+	"fmt"
+
+	"google.golang.org/proto/internal/pragma"
+	pref "google.golang.org/proto/reflect/protoreflect"
+)
+
+type standaloneMessage struct{ m *StandaloneMessage }
+
+func (t standaloneMessage) Parent() (pref.Descriptor, bool)                   { return nil, false }
+func (t standaloneMessage) Syntax() pref.Syntax                               { return t.m.Syntax }
+func (t standaloneMessage) Name() pref.Name                                   { return t.m.FullName.Name() }
+func (t standaloneMessage) FullName() pref.FullName                           { return t.m.FullName }
+func (t standaloneMessage) IsPlaceholder() bool                               { return false }
+func (t standaloneMessage) DescriptorProto() (pref.Message, bool)             { return nil, false }
+func (t standaloneMessage) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
+func (t standaloneMessage) IsMapEntry() bool                                  { return t.m.IsMapEntry }
+func (t standaloneMessage) Fields() pref.FieldDescriptors                     { return t.m.fields.lazyInit(t, t.m.Fields) }
+func (t standaloneMessage) Oneofs() pref.OneofDescriptors                     { return t.m.oneofs.lazyInit(t, t.m.Oneofs) }
+func (t standaloneMessage) RequiredNumbers() pref.FieldNumbers                { return t.m.nums.lazyInit(t.m.Fields) }
+func (t standaloneMessage) ExtensionRanges() pref.FieldRanges                 { return (*ranges)(&t.m.ExtensionRanges) }
+func (t standaloneMessage) Messages() pref.MessageDescriptors                 { return &emptyMessages }
+func (t standaloneMessage) Enums() pref.EnumDescriptors                       { return &emptyEnums }
+func (t standaloneMessage) Extensions() pref.ExtensionDescriptors             { return &emptyExtensions }
+func (t standaloneMessage) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
+func (t standaloneMessage) ProtoType(pref.MessageDescriptor)                  {}
+func (t standaloneMessage) ProtoInternal(pragma.DoNotImplement)               {}
+
+type standaloneEnum struct{ e *StandaloneEnum }
+
+func (t standaloneEnum) Parent() (pref.Descriptor, bool)                   { return nil, false }
+func (t standaloneEnum) Syntax() pref.Syntax                               { return t.e.Syntax }
+func (t standaloneEnum) Name() pref.Name                                   { return t.e.FullName.Name() }
+func (t standaloneEnum) FullName() pref.FullName                           { return t.e.FullName }
+func (t standaloneEnum) IsPlaceholder() bool                               { return false }
+func (t standaloneEnum) DescriptorProto() (pref.Message, bool)             { return nil, false }
+func (t standaloneEnum) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
+func (t standaloneEnum) Values() pref.EnumValueDescriptors                 { return t.e.vals.lazyInit(t, t.e.Values) }
+func (t standaloneEnum) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
+func (t standaloneEnum) ProtoType(pref.EnumDescriptor)                     {}
+func (t standaloneEnum) ProtoInternal(pragma.DoNotImplement)               {}
+
+type standaloneExtension struct{ x *StandaloneExtension }
+
+func (t standaloneExtension) Parent() (pref.Descriptor, bool)                   { return nil, false }
+func (t standaloneExtension) Syntax() pref.Syntax                               { return t.x.Syntax }
+func (t standaloneExtension) Name() pref.Name                                   { return t.x.FullName.Name() }
+func (t standaloneExtension) FullName() pref.FullName                           { return t.x.FullName }
+func (t standaloneExtension) IsPlaceholder() bool                               { return false }
+func (t standaloneExtension) DescriptorProto() (pref.Message, bool)             { return nil, false }
+func (t standaloneExtension) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
+func (t standaloneExtension) Number() pref.FieldNumber                          { return t.x.Number }
+func (t standaloneExtension) Cardinality() pref.Cardinality                     { return t.x.Cardinality }
+func (t standaloneExtension) Kind() pref.Kind                                   { return t.x.Kind }
+func (t standaloneExtension) JSONName() string                                  { return "" }
+func (t standaloneExtension) IsPacked() bool                                    { return t.x.IsPacked }
+func (t standaloneExtension) IsMap() bool                                       { return false }
+func (t standaloneExtension) IsWeak() bool                                      { return false }
+func (t standaloneExtension) Default() pref.Value                               { return t.x.Default }
+func (t standaloneExtension) OneofType() pref.OneofDescriptor                   { return nil }
+func (t standaloneExtension) MessageType() pref.MessageDescriptor               { return t.x.MessageType }
+func (t standaloneExtension) EnumType() pref.EnumDescriptor                     { return t.x.EnumType }
+func (t standaloneExtension) ExtendedType() pref.MessageDescriptor              { return t.x.ExtendedType }
+func (t standaloneExtension) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
+func (t standaloneExtension) ProtoType(pref.FieldDescriptor)                    {}
+func (t standaloneExtension) ProtoInternal(pragma.DoNotImplement)               {}
diff --git a/reflect/prototype/stringer.go b/reflect/prototype/stringer.go
new file mode 100644
index 0000000..7541b8d
--- /dev/null
+++ b/reflect/prototype/stringer.go
@@ -0,0 +1,26 @@
+// Copyright 2018 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 prototype
+
+import (
+	"fmt"
+
+	"google.golang.org/proto/internal/pragma"
+	pref "google.golang.org/proto/reflect/protoreflect"
+)
+
+// TODO: This is useful for print descriptor types in a human readable way.
+// This is not strictly necessary.
+
+// list is an interface that matches any of the list interfaces defined in the
+// protoreflect package.
+type list interface {
+	Len() int
+	pragma.DoNotImplement
+}
+
+func formatList(s fmt.State, r rune, vs list) {}
+
+func formatDesc(s fmt.State, r rune, t pref.Descriptor) {}
diff --git a/reflect/prototype/type_test.go b/reflect/prototype/type_test.go
new file mode 100644
index 0000000..5ecfc1a
--- /dev/null
+++ b/reflect/prototype/type_test.go
@@ -0,0 +1,585 @@
+// Copyright 2018 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 prototype
+
+import (
+	"reflect"
+	"strconv"
+	"strings"
+	"sync"
+	"testing"
+
+	pref "google.golang.org/proto/reflect/protoreflect"
+)
+
+// TestDescriptors tests that the implementations do not declare additional
+// methods that do not exist on the interface types.
+func TestDescriptors(t *testing.T) {
+	tests := []interface{}{
+		[]pref.FileDescriptor{placeholderFile{}, fileDesc{}},
+		[]pref.MessageDescriptor{placeholderMessage{}, standaloneMessage{}, messageDesc{}},
+		[]pref.FieldDescriptor{standaloneExtension{}, fieldDesc{}, extensionDesc{}},
+		[]pref.OneofDescriptor{oneofDesc{}},
+		[]pref.EnumDescriptor{placeholderEnum{}, standaloneEnum{}, enumDesc{}},
+		[]pref.EnumValueDescriptor{enumValueDesc{}},
+		[]pref.ServiceDescriptor{serviceDesc{}},
+		[]pref.MethodDescriptor{methodDesc{}},
+
+		[]pref.FileImports{(*fileImports)(nil)},
+		[]pref.MessageDescriptors{(*messages)(nil)},
+		[]pref.FieldNumbers{(*numbers)(nil)},
+		[]pref.FieldRanges{(*ranges)(nil)},
+		[]pref.FieldDescriptors{(*fields)(nil), (*oneofFields)(nil)},
+		[]pref.OneofDescriptors{(*oneofs)(nil)},
+		[]pref.ExtensionDescriptors{(*extensions)(nil)},
+		[]pref.EnumDescriptors{(*enums)(nil)},
+		[]pref.EnumValueDescriptors{(*enumValues)(nil)},
+		[]pref.ServiceDescriptors{(*services)(nil)},
+		[]pref.MethodDescriptors{(*methods)(nil)},
+	}
+
+	for _, tt := range tests {
+		v := reflect.ValueOf(tt) // []T where T is an interface
+		ifaceType := v.Type().Elem()
+		for i := 0; i < v.Len(); i++ {
+			implType := v.Index(i).Elem().Type()
+
+			var hasName bool
+			for j := 0; j < implType.NumMethod(); j++ {
+				if name := implType.Method(j).Name; name == "Format" {
+					hasName = true
+				} else if _, ok := ifaceType.MethodByName(name); !ok {
+					t.Errorf("spurious method: %v.%v", implType, name)
+				}
+			}
+			if !hasName {
+				t.Errorf("missing method: %v.Format", implType)
+			}
+		}
+	}
+}
+
+func TestFile(t *testing.T) {
+	f := &File{
+		Syntax:  pref.Proto2,
+		Path:    "path/to/file.proto",
+		Package: "test",
+		Messages: []Message{{
+			Name:       "A", // "test.A"
+			IsMapEntry: true,
+			Fields: []Field{{
+				Name:        "key", // "test.A.key"
+				Number:      1,
+				Cardinality: pref.Optional,
+				Kind:        pref.StringKind,
+			}, {
+				Name:        "value", // "test.A.value"
+				Number:      2,
+				Cardinality: pref.Optional,
+				Kind:        pref.MessageKind,
+				MessageType: PlaceholderMessage("test.B"),
+			}},
+		}, {
+			Name: "B", // "test.B"
+			Fields: []Field{{
+				Name:        "field_one",
+				Number:      1,
+				Cardinality: pref.Optional,
+				Kind:        pref.StringKind,
+				Default:     pref.ValueOf("hello"),
+				OneofName:   "O1",
+			}, {
+				Name:        "field_two",
+				JSONName:    "Field2",
+				Number:      2,
+				Cardinality: pref.Optional,
+				Kind:        pref.EnumKind,
+				Default:     pref.ValueOf(pref.EnumNumber(1)),
+				EnumType:    PlaceholderEnum("test.E1"),
+				OneofName:   "O2",
+			}, {
+				Name:        "field_three",
+				Number:      3,
+				Cardinality: pref.Optional,
+				Kind:        pref.MessageKind,
+				MessageType: PlaceholderMessage("test.C"),
+				OneofName:   "O2",
+			}, {
+				Name:        "field_four",
+				JSONName:    "Field4",
+				Number:      4,
+				Cardinality: pref.Repeated,
+				Kind:        pref.MessageKind,
+				MessageType: PlaceholderMessage("test.A"),
+			}, {
+				Name:        "field_five",
+				Number:      5,
+				Cardinality: pref.Repeated,
+				Kind:        pref.Int32Kind,
+				IsPacked:    true,
+			}, {
+				Name:        "field_six",
+				Number:      6,
+				Cardinality: pref.Required,
+				Kind:        pref.StringKind,
+			}},
+			Oneofs:          []Oneof{{Name: "O1"}, {Name: "O2"}},
+			ExtensionRanges: [][2]pref.FieldNumber{{1000, 2000}},
+		}, {
+			Name: "C", // "test.C"
+			Messages: []Message{{
+				Name:   "A", // "test.C.A"
+				Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Required, Kind: pref.BytesKind}},
+			}},
+			Enums: []Enum{{
+				Name:   "E1", // "test.C.E1"
+				Values: []EnumValue{{Name: "FOO", Number: 0}, {Name: "BAR", Number: 1}},
+			}},
+			Extensions: []Extension{{
+				Name:         "X", // "test.C.X"
+				Number:       1000,
+				Cardinality:  pref.Repeated,
+				Kind:         pref.MessageKind,
+				IsPacked:     false,
+				MessageType:  PlaceholderMessage("test.C"),
+				ExtendedType: PlaceholderMessage("test.B"),
+			}},
+		}},
+		Enums: []Enum{{
+			Name:   "E1", // "test.E1"
+			Values: []EnumValue{{Name: "FOO", Number: 0}, {Name: "BAR", Number: 1}},
+		}},
+		Extensions: []Extension{{
+			Name:         "X", // "test.X"
+			Number:       1000,
+			Cardinality:  pref.Repeated,
+			Kind:         pref.MessageKind,
+			IsPacked:     false,
+			MessageType:  PlaceholderMessage("test.C"),
+			ExtendedType: PlaceholderMessage("test.B"),
+		}},
+		Services: []Service{{
+			Name: "S", // "test.S"
+			Methods: []Method{{
+				Name:              "M", // "test.S.M"
+				InputType:         PlaceholderMessage("test.A"),
+				OutputType:        PlaceholderMessage("test.C.A"),
+				IsStreamingClient: true,
+				IsStreamingServer: true,
+			}},
+		}},
+	}
+
+	fd, err := NewFile(f)
+	if err != nil {
+		t.Fatalf("NewFile() error: %v", err)
+	}
+
+	// Represent the descriptor as a map where each key is an accessor method
+	// and the value is either the wanted tail value or another accessor map.
+	type M = map[string]interface{}
+	want := M{
+		"Parent":        nil,
+		"Syntax":        pref.Proto2,
+		"Name":          pref.Name("test"),
+		"FullName":      pref.FullName("test"),
+		"Path":          "path/to/file.proto",
+		"Package":       pref.FullName("test"),
+		"IsPlaceholder": false,
+		"Messages": M{
+			"Len": 3,
+			"Get:0": M{
+				"Parent":        M{"FullName": pref.FullName("test")},
+				"Syntax":        pref.Proto2,
+				"Name":          pref.Name("A"),
+				"FullName":      pref.FullName("test.A"),
+				"IsPlaceholder": false,
+				"IsMapEntry":    true,
+				"Fields": M{
+					"Len": 2,
+					"ByNumber:1": M{
+						"Parent":       M{"FullName": pref.FullName("test.A")},
+						"Name":         pref.Name("key"),
+						"FullName":     pref.FullName("test.A.key"),
+						"Number":       pref.FieldNumber(1),
+						"Cardinality":  pref.Optional,
+						"Kind":         pref.StringKind,
+						"JSONName":     "key",
+						"IsPacked":     false,
+						"IsMap":        false,
+						"IsWeak":       false,
+						"Default":      "",
+						"OneofType":    nil,
+						"ExtendedType": nil,
+						"MessageType":  nil,
+						"EnumType":     nil,
+					},
+					"ByNumber:2": M{
+						"Parent":       M{"FullName": pref.FullName("test.A")},
+						"Name":         pref.Name("value"),
+						"FullName":     pref.FullName("test.A.value"),
+						"Number":       pref.FieldNumber(2),
+						"Cardinality":  pref.Optional,
+						"Kind":         pref.MessageKind,
+						"JSONName":     "value",
+						"IsPacked":     false,
+						"IsMap":        false,
+						"IsWeak":       false,
+						"Default":      nil,
+						"OneofType":    nil,
+						"ExtendedType": nil,
+						"MessageType":  M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
+						"EnumType":     nil,
+					},
+					"ByNumber:3": nil,
+				},
+				"Oneofs":          M{"Len": 0},
+				"RequiredNumbers": M{"Len": 0},
+				"ExtensionRanges": M{"Len": 0},
+				"Messages":        M{"Len": 0},
+				"Enums":           M{"Len": 0},
+				"Extensions":      M{"Len": 0},
+			},
+			"ByName:B": M{
+				"Name": pref.Name("B"),
+				"Fields": M{
+					"Len":                  6,
+					"ByJSONName:field_one": nil,
+					"ByJSONName:fieldOne": M{
+						"Name":      pref.Name("field_one"),
+						"JSONName":  "fieldOne",
+						"Default":   "hello",
+						"OneofType": M{"Name": pref.Name("O1"), "IsPlaceholder": false},
+					},
+					"ByJSONName:fieldTwo": nil,
+					"ByJSONName:Field2": M{
+						"Name":      pref.Name("field_two"),
+						"JSONName":  "Field2",
+						"Default":   pref.EnumNumber(1),
+						"OneofType": M{"Name": pref.Name("O2"), "IsPlaceholder": false},
+					},
+					"ByName:fieldThree": nil,
+					"ByName:field_three": M{
+						"IsMap":       false,
+						"MessageType": M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
+						"OneofType":   M{"Name": pref.Name("O2"), "IsPlaceholder": false},
+					},
+					"ByNumber:12": nil,
+					"ByNumber:4": M{
+						"Cardinality": pref.Repeated,
+						"IsMap":       true,
+						"Default":     nil,
+						"MessageType": M{"FullName": pref.FullName("test.A"), "IsPlaceholder": false},
+					},
+					"ByNumber:5": M{
+						"Cardinality": pref.Repeated,
+						"Kind":        pref.Int32Kind,
+						"IsPacked":    true,
+						"Default":     int32(0),
+					},
+					"ByNumber:6": M{
+						"Cardinality": pref.Required,
+						"Default":     "",
+						"OneofType":   nil,
+					},
+				},
+				"Oneofs": M{
+					"Len":       2,
+					"ByName:O0": nil,
+					"ByName:O1": M{
+						"FullName": pref.FullName("test.B.O1"),
+						"Fields": M{
+							"Len":   1,
+							"Get:0": M{"FullName": pref.FullName("test.B.field_one")},
+						},
+					},
+					"Get:1": M{
+						"FullName": pref.FullName("test.B.O2"),
+						"Fields": M{
+							"Len":              2,
+							"ByName:field_two": M{"Name": pref.Name("field_two")},
+							"Get:1":            M{"Name": pref.Name("field_three")},
+						},
+					},
+				},
+				"RequiredNumbers": M{
+					"Len":   1,
+					"Get:0": pref.FieldNumber(6),
+					"Has:1": false,
+					"Has:6": true,
+				},
+				"ExtensionRanges": M{
+					"Len":      1,
+					"Get:0":    [2]pref.FieldNumber{1000, 2000},
+					"Has:999":  false,
+					"Has:1000": true,
+					"Has:1500": true,
+					"Has:1999": true,
+					"Has:2000": false,
+				},
+			},
+			"Get:2": M{
+				"Name": pref.Name("C"),
+				"Messages": M{
+					"Len":   1,
+					"Get:0": M{"FullName": pref.FullName("test.C.A")},
+				},
+				"Enums": M{
+					"Len":   1,
+					"Get:0": M{"FullName": pref.FullName("test.C.E1")},
+				},
+				"Extensions": M{
+					"Len":   1,
+					"Get:0": M{"FullName": pref.FullName("test.C.X")},
+				},
+			},
+		},
+		"Enums": M{
+			"Len": 1,
+			"Get:0": M{
+				"Name": pref.Name("E1"),
+				"Values": M{
+					"Len":        2,
+					"ByName:Foo": nil,
+					"ByName:FOO": M{"FullName": pref.FullName("test.FOO")},
+					"ByNumber:2": nil,
+					"ByNumber:1": M{"FullName": pref.FullName("test.BAR")},
+				},
+			},
+		},
+		"Extensions": M{
+			"Len": 1,
+			"ByName:X": M{
+				"Name":         pref.Name("X"),
+				"Number":       pref.FieldNumber(1000),
+				"Cardinality":  pref.Repeated,
+				"Kind":         pref.MessageKind,
+				"IsPacked":     false,
+				"MessageType":  M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
+				"ExtendedType": M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
+			},
+		},
+		"Services": M{
+			"Len":      1,
+			"ByName:s": nil,
+			"ByName:S": M{
+				"Parent":   M{"FullName": pref.FullName("test")},
+				"Name":     pref.Name("S"),
+				"FullName": pref.FullName("test.S"),
+				"Methods": M{
+					"Len": 1,
+					"Get:0": M{
+						"Parent":            M{"FullName": pref.FullName("test.S")},
+						"Name":              pref.Name("M"),
+						"FullName":          pref.FullName("test.S.M"),
+						"InputType":         M{"FullName": pref.FullName("test.A"), "IsPlaceholder": false},
+						"OutputType":        M{"FullName": pref.FullName("test.C.A"), "IsPlaceholder": false},
+						"IsStreamingClient": true,
+						"IsStreamingServer": true,
+					},
+				},
+			},
+		},
+	}
+
+	// Concurrently explore the file tree to induce races.
+	const numGoRoutines = 2
+	var wg sync.WaitGroup
+	defer wg.Wait()
+	for i := 0; i < numGoRoutines; i++ {
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			checkAccessors(t, "", reflect.ValueOf(fd), want)
+		}()
+	}
+}
+
+func checkAccessors(t *testing.T, p string, rv reflect.Value, want map[string]interface{}) {
+	if rv.Interface() == nil {
+		t.Errorf("%v is nil, want non-nil", p)
+		return
+	}
+	for s, v := range want {
+		// Call the accessor method.
+		p := p + "." + s
+		var rets []reflect.Value
+		if i := strings.IndexByte(s, ':'); i >= 0 {
+			// Accessor method takes in a single argument, which is encoded
+			// after the accessor name, separated by a ':' delimiter.
+			fnc := rv.MethodByName(s[:i])
+			arg := reflect.New(fnc.Type().In(0)).Elem()
+			s = s[i+len(":"):]
+			switch arg.Kind() {
+			case reflect.String:
+				arg.SetString(s)
+			case reflect.Int32, reflect.Int:
+				n, _ := strconv.ParseInt(s, 0, 64)
+				arg.SetInt(n)
+			}
+			rets = fnc.Call([]reflect.Value{arg})
+		} else {
+			rets = rv.MethodByName(s).Call(nil)
+		}
+
+		// Check that (val, ok) pattern is internally consistent.
+		if len(rets) == 2 {
+			if rets[0].IsNil() && rets[1].Bool() {
+				t.Errorf("%v = (nil, true), want (nil, false)", p)
+			}
+			if !rets[0].IsNil() && !rets[1].Bool() {
+				t.Errorf("%v = (non-nil, false), want (non-nil, true)", p)
+			}
+		}
+
+		// Check that the accessor output matches.
+		if want, ok := v.(map[string]interface{}); ok {
+			checkAccessors(t, p, rets[0], want)
+		} else {
+			got := rets[0].Interface()
+			if pv, ok := got.(pref.Value); ok {
+				got = pv.Interface()
+			}
+			if want := v; !reflect.DeepEqual(got, want) {
+				t.Errorf("%v = %v, want %v", p, got, want)
+			}
+		}
+	}
+}
+
+func TestResolve(t *testing.T) {
+	f := &File{
+		Syntax:  pref.Proto2,
+		Package: "test",
+		Messages: []Message{{
+			Name:   "FooMessage",
+			Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
+			Messages: []Message{{
+				Name:   "FooMessage",
+				Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
+			}, {
+				Name:   "BarMessage",
+				Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
+			}},
+			Enums: []Enum{{
+				Name:   "FooEnum",
+				Values: []EnumValue{{Name: "E", Number: 0}},
+			}, {
+				Name:   "BarEnum",
+				Values: []EnumValue{{Name: "E", Number: 0}},
+			}},
+		}, {
+			Name:   "BarMessage",
+			Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
+		}},
+		Enums: []Enum{{
+			Name:   "FooEnum",
+			Values: []EnumValue{{Name: "E", Number: 0}},
+		}, {
+			Name:   "BarEnum",
+			Values: []EnumValue{{Name: "E", Number: 0}},
+		}},
+	}
+
+	fd, err := NewFile(f)
+	if err != nil {
+		t.Fatalf("NewFile() error: %v", err)
+	}
+
+	tests := []struct {
+		parent pref.Descriptor
+		name   pref.FullName
+		want   pref.Descriptor
+	}{{
+		parent: fd.Enums().Get(0),
+		name:   "test.Foo",
+		want:   nil,
+	}, {
+		parent: fd.Enums().Get(0),
+		name:   "test.FooEnum",
+		want:   fd.Enums().Get(0),
+	}, {
+		parent: fd.Enums().Get(0),
+		name:   "test.BarEnum",
+		want:   fd.Enums().Get(1),
+	}, {
+		parent: fd.Enums().Get(0),
+		name:   "test.BarMessage",
+		want:   fd.Messages().Get(1),
+	}, {
+		parent: fd.Enums().Get(0),
+		name:   "test.FooMessage.BarMessage",
+		want:   fd.Messages().Get(0).Messages().Get(1),
+	}, {
+		parent: fd.Enums().Get(0),
+		name:   "test.FooMessage.Bar",
+		want:   nil,
+	}, {
+		parent: fd.Messages().Get(1),
+		name:   "test.FooMessage.BarEnum",
+		want:   fd.Messages().Get(0).Enums().Get(1),
+	}, {
+		parent: fd.Messages().Get(1),
+		name:   "test.FooEnum",
+		want:   fd.Enums().Get(0),
+	}, {
+		parent: fd.Messages().Get(0),
+		name:   "test.FooEnum",
+		want:   fd.Enums().Get(0),
+	}, {
+		parent: fd.Messages().Get(0),
+		name:   "test.FooEnum.NonExistent",
+		want:   nil,
+	}, {
+		parent: fd.Messages().Get(0),
+		name:   "test.FooMessage.FooEnum",
+		want:   fd.Messages().Get(0).Enums().Get(0),
+	}, {
+		parent: fd.Messages().Get(0),
+		name:   "test.FooMessage",
+		want:   fd.Messages().Get(0),
+	}, {
+		parent: fd.Messages().Get(0),
+		name:   "test.FooMessage.Fizz",
+		want:   nil,
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "test.FooMessage.FooMessage",
+		want:   fd.Messages().Get(0).Messages().Get(0),
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "test.FooMessage.BarMessage",
+		want:   fd.Messages().Get(0).Messages().Get(1),
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "test.BarMessage.FooMessage",
+		want:   nil,
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "test.BarMessage",
+		want:   fd.Messages().Get(1),
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "test.BarMessageExtra",
+		want:   nil,
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "taste.BarMessage",
+		want:   nil,
+	}}
+
+	for _, tt := range tests {
+		got := resolveReference(tt.parent, tt.name)
+		if got != tt.want {
+			fullName := func(d pref.Descriptor) string {
+				if d == nil {
+					return "<nil>"
+				}
+				return string(d.FullName())
+			}
+			t.Errorf("resolveReference(%v, %v) = %v, want %v", fullName(tt.parent), tt.name, fullName(got), fullName(tt.want))
+		}
+	}
+}
diff --git a/reflect/prototype/validate.go b/reflect/prototype/validate.go
new file mode 100644
index 0000000..adbb35c
--- /dev/null
+++ b/reflect/prototype/validate.go
@@ -0,0 +1,28 @@
+// Copyright 2018 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 prototype
+
+import (
+	pref "google.golang.org/proto/reflect/protoreflect"
+)
+
+// TODO: This is important to prevent users from creating invalid types,
+// but is not functionality needed now.
+
+func validateFile(t pref.FileDescriptor) error {
+	return nil
+}
+
+func validateMessage(t pref.MessageDescriptor) error {
+	return nil
+}
+
+func validateExtension(t pref.ExtensionDescriptor) error {
+	return nil
+}
+
+func validateEnum(t pref.EnumDescriptor) error {
+	return nil
+}
diff --git a/test.bash b/test.bash
index c010f94..2c65d76 100755
--- a/test.bash
+++ b/test.bash
@@ -29,8 +29,8 @@
 export PATH=$TEST_DIR/$PROTOBUF_DIR/src:$TEST_DIR/$PROTOBUF_DIR/conformance:$PATH
 
 # Download each Go toolchain version.
-GO_LATEST=1.11beta3
-GO_VERSIONS=(1.9.7 1.10.3 $GO_LATEST)
+GO_LATEST=1.11rc1
+GO_VERSIONS=(1.10.3 $GO_LATEST)
 for GO_VERSION in ${GO_VERSIONS[@]}; do
 	GO_DIR=go$GO_VERSION
 	if [ ! -d $GO_DIR ]; then
@@ -91,7 +91,13 @@
 	fi
 done
 
-# TODO: Check for stale generated Go source files.
+# Check for stale generated source files.
+GEN_DIFF=$(cd $REPO_ROOT && ${GO_LATEST_BIN} run ./internal/cmd/generate-types 2>&1)
+if [ ! -z "$GEN_DIFF" ]; then
+	print "go run ./internal/cmd/generate-types"
+	echo "$GEN_DIFF"
+	RET=1
+fi
 
 # Check for unformatted Go source files.
 FMT_DIFF=$(cd $REPO_ROOT && ${GO_LATEST_BIN}fmt -d . 2>&1)