encoding/protobuf: always include type as second argument

This significanty simplifies finding the right mapping from
CUE to protobuf.

Also changed map type from map<A,B> to map[A]B. The former
caused issues as the comma caused it to be a separate
attribute argument (<> is not matched). Encosing it in
quotes was also annoying.

We considered using [A]:B, but using the map notation allows
for more extendibility and less ambiguity in case we must
support lists for some reason.

This is a backwards incompatible change.

Issue #5

Change-Id: Ief64d0c91d481fd7b03a4a8842faf377fd364a26
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9368
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
diff --git a/cmd/cue/cmd/get_go.go b/cmd/cue/cmd/get_go.go
index b76e967..4d493d3 100644
--- a/cmd/cue/cmd/get_go.go
+++ b/cmd/cue/cmd/get_go.go
@@ -1210,7 +1210,10 @@
 		}
 
 		// Carry over protobuf field tags with modifications.
-		if t := reflect.StructTag(tag).Get("protobuf"); t != "" {
+		// TODO: consider trashing the protobuf tag, as the Go versions are
+		// lossy and will not allow for an accurate translation in some cases.
+		tags := reflect.StructTag(tag)
+		if t := tags.Get("protobuf"); t != "" {
 			split := strings.Split(t, ",")
 			k := 0
 			for _, s := range split {
@@ -1227,6 +1230,18 @@
 			if len(split) >= 2 {
 				split[0], split[1] = split[1], split[0]
 			}
+
+			// Interpret as map?
+			if len(split) > 2 && split[1] == "bytes" {
+				tk := tags.Get("protobuf_key")
+				tv := tags.Get("protobuf_val")
+				if tk != "" && tv != "" {
+					tk = strings.SplitN(tk, ",", 2)[0]
+					tv = strings.SplitN(tv, ",", 2)[0]
+					split[1] = fmt.Sprintf("map[%s]%s", tk, tv)
+				}
+			}
+
 			e.addAttr(field, "protobuf", strings.Join(split, ","))
 		}
 
diff --git a/cmd/cue/cmd/testdata/script/def_proto.txt b/cmd/cue/cmd/testdata/script/def_proto.txt
index 816e9c9..0d23064 100644
--- a/cmd/cue/cmd/testdata/script/def_proto.txt
+++ b/cmd/cue/cmd/testdata/script/def_proto.txt
@@ -11,24 +11,24 @@
 	// A map of attribute name to its value.
 	attributes?: {
 		[string]: #AttributeValue
-	} @protobuf(1,type=map<string,AttributeValue>)
+	} @protobuf(1,map[string]AttributeValue)
 
 	// Specifies one attribute value with different type.
 	#AttributeValue: {} | {
-		stringValue: string @protobuf(2,name=string_value)
+		stringValue: string @protobuf(2,string,name=string_value)
 	} | {
-		int64Value: int64 @protobuf(3,name=int64_value)
+		int64Value: int64 @protobuf(3,int64,name=int64_value)
 	} | {
-		doubleValue: float64 @protobuf(4,type=double,name=double_value)
+		doubleValue: float64 @protobuf(4,double,name=double_value)
 	} | {
-		boolValue: bool @protobuf(5,name=bool_value)
+		boolValue: bool @protobuf(5,bool,name=bool_value)
 	} | {
-		bytesValue: bytes @protobuf(6,name=bytes_value)
+		bytesValue: bytes @protobuf(6,bytes,name=bytes_value)
 	} | {
-		timestampValue: time.Time @protobuf(7,type=google.protobuf.Timestamp,name=timestamp_value)
+		timestampValue: time.Time @protobuf(7,google.protobuf.Timestamp,name=timestamp_value)
 	} | {
 		// Used for values of type STRING_MAP
-		stringMapValue: #StringMap @protobuf(9,name=string_map_value)
+		stringMapValue: #StringMap @protobuf(9,StringMap,name=string_map_value)
 	}
 
 	// Defines a string map.
@@ -36,7 +36,7 @@
 		// Holds a set of name/value pairs.
 		entries?: {
 			[string]: string
-		} @protobuf(1,type=map<string,string>)
+		} @protobuf(1,map[string]string)
 	}
 }
 -- policy.proto --
diff --git a/cmd/cue/cmd/testdata/script/get_go_types.txt b/cmd/cue/cmd/testdata/script/get_go_types.txt
index 9a99a24..a22cc66 100644
--- a/cmd/cue/cmd/testdata/script/get_go_types.txt
+++ b/cmd/cue/cmd/testdata/script/get_go_types.txt
@@ -70,7 +70,9 @@
 
 	Alias1 *MyBarzer
 
-	Map    map[string]*CustomJSON
+	// Note: Go encodings of protobuf tags are lossy. So this is a best-effort
+	// thing.
+	Map    map[string]*CustomJSON `protobuf:"bytes,1,name=intf" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
 	Slice1 []int
 	Slice2 []interface{}
 	Slice3 *[]json.Unmarshaler
@@ -222,52 +224,52 @@
 package pkg1
 
 import (
- "time"
- p2 "example.com/pkg2:pkgtwo"
+	"time"
+	p2 "example.com/pkg2:pkgtwo"
 )
 
 // Foozer foozes a jaman.
 #Foozer: {
- Int:    int
- String: string
+	Int:    int
+	String: string
 
- #Inline
- NoInline:    #NoInline
- CustomJSON:  #CustomJSON
- CustomYAML?: null | #CustomYAML @go(,*CustomYAML)
- AnyJSON:     _                  @go(,json.Marshaler)
- AnyText:     string             @go(,encoding.TextMarshaler)
- bar?:        int & >10          @go(Bar)
+	#Inline
+	NoInline:    #NoInline
+	CustomJSON:  #CustomJSON
+	CustomYAML?: null | #CustomYAML @go(,*CustomYAML)
+	AnyJSON:     _                  @go(,json.Marshaler)
+	AnyText:     string             @go(,encoding.TextMarshaler)
+	bar?:        int & >10          @go(Bar)
 
- // Time is mapped to CUE's internal type.
- Time:    time.Time
- Barzer:  p2.#Barzer
- Alias1?: null | p2.#Barzer @go(,*p2.Barzer)
- Map: {[string]: null | #CustomJSON} @go(,map[string]*CustomJSON)
- Slice1: [...int] @go(,[]int)
- Slice2: [...] @go(,[]interface{})
- Slice3?: null | [...] @go(,*[]json.Unmarshaler)
- Array1:  5 * [int]    @go(,[5]int)
- Array2:  5 * [_]      @go(,[5]interface{})
- Array3?: null | 5*[_] @go(,*[5]json.Marshaler)
- Intf:    #Interface   @protobuf(2,varint,name=intf)
- Intf2:   _            @go(,interface{})
- Intf3: {
-  Interface: #Interface
- } @go(,struct{Interface})
- Intf4: _ @go(,"interface{Foo()}")
+	// Time is mapped to CUE's internal type.
+	Time:    time.Time
+	Barzer:  p2.#Barzer
+	Alias1?: null | p2.#Barzer @go(,*p2.Barzer)
+	Map: {[string]: null | #CustomJSON} @go(,map[string]*CustomJSON)
+	Slice1: [...int] @go(,[]int)
+	Slice2: [...] @go(,[]interface{})
+	Slice3?: null | [...] @go(,*[]json.Unmarshaler)
+	Array1:  5 * [int]    @go(,[5]int)
+	Array2:  5 * [_]      @go(,[5]interface{})
+	Array3?: null | 5*[_] @go(,*[5]json.Marshaler)
+	Intf:    #Interface   @protobuf(2,varint,name=intf)
+	Intf2:   _            @go(,interface{})
+	Intf3: {
+		Interface: #Interface
+	} @go(,struct{Interface})
+	Intf4: _ @go(,"interface{Foo()}")
 
- // Even though this struct as a type implements MarshalJSON, it is known
- // that it is really only implemented by the embedded field.
- Embed: {
-  CustomJSON: #CustomJSON
- } @go(,struct{CustomJSON})
+	// Even though this struct as a type implements MarshalJSON, it is known
+	// that it is really only implemented by the embedded field.
+	Embed: {
+		CustomJSON: #CustomJSON
+	} @go(,struct{CustomJSON})
 }
 
 #Identifier: string // #enumIdentifier
 
 #enumIdentifier:
- _#internalIdentifier
+	_#internalIdentifier
 
 _#internalIdentifier: #Identifier & "internal"
 
@@ -275,10 +277,10 @@
 #Level: int // #enumLevel
 
 #enumLevel:
- #Unknown |
- #Low |
- #Medium |
- #High
+	#Unknown |
+	#Low |
+	#Medium |
+	#High
 
 // Block comment.
 //  Indented.
@@ -317,17 +319,17 @@
 
 // A Barzer barzes.
 #Barzer: {
- a:     int @go(A) @protobuf(2,varint,)
- T:     t.Time
- B?:    null | int    @go(,*big.Int)
- C:     int           @go(,big.Int)
- F:     string        @go(,big.Float) @xml(,attr)
- G?:    null | string @go(,*big.Float)
- S:     string
- "x-y": bool @go(XY)
- Err:   _    @go(,error)
+	a:     int @go(A) @protobuf(2,varint,)
+	T:     t.Time
+	B?:    null | int    @go(,*big.Int)
+	C:     int           @go(,big.Int)
+	F:     string        @go(,big.Float) @xml(,attr)
+	G?:    null | string @go(,*big.Float)
+	S:     string
+	"x-y": bool @go(XY)
+	Err:   _    @go(,error)
 
- #Inline
+	#Inline
 }
 
 #Perm: 0o755
@@ -336,6 +338,8 @@
 
 #Couple: int & 2
 
+#LongStringConst: "This is a really long string. Why are we using a long string? Because that way it ensures we are using go/constant.Value.ExactString() instead of go/constant.Value.String()"
+
 #Inline: A: int
 -- pkg3/pkg3_go_gen.cue --
 // Code generated by cue get go. DO NOT EDIT.
@@ -388,7 +392,10 @@
 	Time:    time.Time
 	Barzer:  p2.#Barzer
 	Alias1?: null | p2.#Barzer @go(,*p2.Barzer)
-	Map: {[string]: null | #CustomJSON} @go(,map[string]*CustomJSON)
+
+	// Note: Go encodings of protobuf tags are lossy. So this is a best-effort
+	// thing.
+	Map: {[string]: null | #CustomJSON} @go(,map[string]*CustomJSON) @protobuf(1,map[bytes]bytes,name=intf)
 	Slice1: [...int] @go(,[]int)
 	Slice2: [...] @go(,[]interface{})
 	Slice3?: null | [...] @go(,*[]json.Unmarshaler)
diff --git a/cmd/cue/cmd/testdata/script/import_proto.txt b/cmd/cue/cmd/testdata/script/import_proto.txt
index 6383895..dd21a60 100644
--- a/cmd/cue/cmd/testdata/script/import_proto.txt
+++ b/cmd/cue/cmd/testdata/script/import_proto.txt
@@ -4,9 +4,9 @@
 
 cmp stderr expect-stderr
 cmp stdout expect-stdout
-cmp expect-attributes_proto_gen.cue root/mixer/v1/attributes_proto_gen.cue
-cmp expect-client_config_proto_gen.cue root/mixer/v1/config/client/client_config_proto_gen.cue
-cmp expect-test_proto_gen.cue root/cue.mod/gen/googleapis.com/acme/test/test_proto_gen.cue
+cmp root/mixer/v1/attributes_proto_gen.cue expect-attributes_proto_gen.cue
+cmp root/mixer/v1/config/client/client_config_proto_gen.cue expect-client_config_proto_gen.cue
+cmp root/cue.mod/gen/googleapis.com/acme/test/test_proto_gen.cue expect-test_proto_gen.cue
 
 -- expect-stdout --
 -- expect-stderr --
@@ -174,30 +174,30 @@
 	// A map of attribute name to its value.
 	attributes?: {
 		[string]: #AttributeValue
-	} @protobuf(1,type=map<string,AttributeValue>)
+	} @protobuf(1,map[string]AttributeValue)
 
 	// Specifies one attribute value with different type.
 	#AttributeValue: {
 		// The attribute value.
 		{} | {
-			stringValue: string @protobuf(2,name=string_value)
+			stringValue: string @protobuf(2,string,name=string_value)
 		} | {
-			int64Value: int64 @protobuf(3,name=int64_value)
+			int64Value: int64 @protobuf(3,int64,name=int64_value)
 		} | {
-			doubleValue: float64 @protobuf(4,type=double,name=double_value)
+			doubleValue: float64 @protobuf(4,double,name=double_value)
 		} | {
-			boolValue: bool @protobuf(5,name=bool_value)
+			boolValue: bool @protobuf(5,bool,name=bool_value)
 		} | {
-			bytesValue: bytes @protobuf(6,name=bytes_value)
+			bytesValue: bytes @protobuf(6,bytes,name=bytes_value)
 		} | {
-			timestampValue: time.Time @protobuf(7,type=google.protobuf.Timestamp,name=timestamp_value)
+			timestampValue: time.Time @protobuf(7,google.protobuf.Timestamp,name=timestamp_value)
 		} | {
 			// Used for values of type STRING_MAP
-			stringMapValue: #StringMap @protobuf(9,name=string_map_value)
+			stringMapValue: #StringMap @protobuf(9,StringMap,name=string_map_value)
 		} | {
-			testValue: test.#Test @protobuf(10,type=acme.test.Test,name=test_value)
+			testValue: test.#Test @protobuf(10,acme.test.Test,name=test_value)
 		} | {
-			testValue: test_test.#AnotherTest @protobuf(11,type=acme.test.test.AnotherTest,name=test_value)
+			testValue: test_test.#AnotherTest @protobuf(11,acme.test.test.AnotherTest,name=test_value)
 		}
 	}
 
@@ -206,7 +206,7 @@
 		// Holds a set of name/value pairs.
 		entries?: {
 			[string]: string
-		} @protobuf(1,type=map<string,string>)
+		} @protobuf(1,map[string]string)
 	}
 }
 -- expect-client_config_proto_gen.cue --
@@ -217,13 +217,13 @@
 
 // Defines the per-service client configuration.
 #ServiceConfig: {
-	disableCheckCalls?:  bool           @protobuf(1,name=disable_check_calls)
-	disableReportCalls?: bool           @protobuf(2,name=disable_report_calls)
-	mixerAttributes?:    v1.#Attributes @protobuf(3,type=Attributes,name=mixer_attributes)
+	disableCheckCalls?:  bool           @protobuf(1,bool,name=disable_check_calls)
+	disableReportCalls?: bool           @protobuf(2,bool,name=disable_report_calls)
+	mixerAttributes?:    v1.#Attributes @protobuf(3,Attributes,name=mixer_attributes)
 }
 -- expect-test_proto_gen.cue --
 package test
 
 #Test: {
-	test?: int32 @protobuf(1)
+	test?: int32 @protobuf(1,int32)
 }
diff --git a/cue/types.go b/cue/types.go
index a0e3d95..4287d0c 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -2275,7 +2275,15 @@
 // and reports the value if found. It reports an error if the attribute is
 // invalid or if the first pos-1 entries are not defined.
 func (a *Attribute) Lookup(pos int, key string) (val string, found bool, err error) {
-	return a.attr.Lookup(pos, key)
+	val, found, err = a.attr.Lookup(pos, key)
+
+	// TODO: remove at some point. This is an ugly hack to simulate the old
+	// behavior of protobufs.
+	if !found && a.attr.Name == "protobuf" && key == "type" {
+		val, err = a.String(1)
+		found = err == nil
+	}
+	return val, found, err
 }
 
 // Expr reports the operation of the underlying expression and the values it
diff --git a/encoding/protobuf/examples_test.go b/encoding/protobuf/examples_test.go
index a0a7f5d..fe37380 100644
--- a/encoding/protobuf/examples_test.go
+++ b/encoding/protobuf/examples_test.go
@@ -47,13 +47,13 @@
 	//
 	// // This is my type.
 	// #MyType: {
-	// 	stringValue?: string @protobuf(1,name=string_value) // just any 'ole string
+	// 	stringValue?: string @protobuf(1,string,name=string_value) // just any 'ole string
 	//
 	// 	// A method must start with a capital letter.
-	// 	method?: [...string] @protobuf(2)
+	// 	method?: [...string] @protobuf(2,string)
 	// 	method?: [...=~"^[A-Z]"]
 	// 	exampleMap?: {
 	// 		[string]: string
-	// 	} @protobuf(3,type=map<string,string>,example_map)
+	// 	} @protobuf(3,map[string]string,example_map)
 	// }
 }
diff --git a/encoding/protobuf/parse.go b/encoding/protobuf/parse.go
index 276baed..a272c34 100644
--- a/encoding/protobuf/parse.go
+++ b/encoding/protobuf/parse.go
@@ -30,7 +30,6 @@
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/ast/astutil"
 	"cuelang.org/go/cue/errors"
-	"cuelang.org/go/cue/format"
 	"cuelang.org/go/cue/literal"
 	"cuelang.org/go/cue/parser"
 	"cuelang.org/go/cue/token"
@@ -520,7 +519,7 @@
 		addComments(f, i, x.Comment, x.InlineComment)
 
 		o := optionParser{message: s, field: f}
-		o.tags = fmt.Sprintf("%d,type=map<%s,%s>", x.Sequence, x.KeyType, x.Type)
+		o.tags = fmt.Sprintf(`%d,map[%s]%s`, x.Sequence, x.KeyType, x.Type)
 		if x.Name != name.Name {
 			o.tags += "," + x.Name
 		}
@@ -718,13 +717,8 @@
 
 	o := optionParser{message: s, field: f}
 
-	// body of @protobuf tag: sequence[,type][,name=<name>][,...]
-	o.tags += fmt.Sprint(x.Sequence)
-	b, _ := format.Node(typ)
-	str := string(b)
-	if x.Type != strings.TrimLeft(str, "#") {
-		o.tags += ",type=" + x.Type
-	}
+	// body of @protobuf tag: sequence,type[,name=<name>][,...]
+	o.tags += fmt.Sprintf("%v,%s", x.Sequence, x.Type)
 	if x.Name != name.Name {
 		o.tags += ",name=" + x.Name
 	}
diff --git a/encoding/protobuf/testdata/attributes.proto.out.cue b/encoding/protobuf/testdata/attributes.proto.out.cue
index 92a3e38..5d8217b 100644
--- a/encoding/protobuf/testdata/attributes.proto.out.cue
+++ b/encoding/protobuf/testdata/attributes.proto.out.cue
@@ -21,12 +21,12 @@
 )
 
 #StructWrap: {
-	struct?: {} @protobuf(1,type=google.protobuf.Struct)
-	any?: _ @protobuf(2,type=google.protobuf.Value)
-	listVal?: [...] @protobuf(3,type=google.protobuf.ListValue)
-	boolVal?:   bool   @protobuf(4,type=google.protobuf.BoolValue)
-	stringVal?: string @protobuf(5,type=google.protobuf.StringValue)
-	numberVal?: number @protobuf(6,type=google.protobuf.NumberValue)
+	struct?: {} @protobuf(1,google.protobuf.Struct)
+	any?: _ @protobuf(2,google.protobuf.Value)
+	listVal?: [...] @protobuf(3,google.protobuf.ListValue)
+	boolVal?:   bool   @protobuf(4,google.protobuf.BoolValue)
+	stringVal?: string @protobuf(5,google.protobuf.StringValue)
+	numberVal?: number @protobuf(6,google.protobuf.NumberValue)
 }
 
 // Attributes represents a set of typed name/value pairs. Many of Mixer's
@@ -50,39 +50,39 @@
 	// A map of attribute name to its value.
 	attributes?: {
 		[string]: #AttributeValue
-	} @protobuf(1,type=map<string,AttributeValue>)
+	} @protobuf(1,map[string]AttributeValue)
 
 	// Specifies one attribute value with different type.
 	#AttributeValue: {
 		// The attribute value.
 		{} | {
 			// Used for values of type STRING, DNS_NAME, EMAIL_ADDRESS, and URI
-			stringValue: string @protobuf(2,name=string_value)
+			stringValue: string @protobuf(2,string,name=string_value)
 		} | {
 			// Used for values of type INT64
-			int64Value: int64 @protobuf(3,name=int64_value)
+			int64Value: int64 @protobuf(3,int64,name=int64_value)
 		} | {
 			// Used for values of type DOUBLE
-			doubleValue: float64 @protobuf(4,type=double,name=double_value)
+			doubleValue: float64 @protobuf(4,double,name=double_value)
 		} | {
 			// Used for values of type BOOL
-			boolValue: bool @protobuf(5,name=bool_value)
+			boolValue: bool @protobuf(5,bool,name=bool_value)
 		} | {
 			// Used for values of type BYTES
-			bytesValue: bytes @protobuf(6,name=bytes_value)
+			bytesValue: bytes @protobuf(6,bytes,name=bytes_value)
 		} | {
 			// Used for values of type TIMESTAMP
-			timestampValue: time.Time @protobuf(7,type=google.protobuf.Timestamp,name=timestamp_value)
+			timestampValue: time.Time @protobuf(7,google.protobuf.Timestamp,name=timestamp_value)
 		} | {
 			// Used for values of type DURATION
-			durationValue: time.Duration @protobuf(8,type=google.protobuf.Duration,name=duration_value)
+			durationValue: time.Duration @protobuf(8,google.protobuf.Duration,name=duration_value)
 		} | {
 			// Used for values of type STRING_MAP
-			stringMapValue: #StringMap @protobuf(9,name=string_map_value)
+			stringMapValue: #StringMap @protobuf(9,StringMap,name=string_map_value)
 		} | {
-			testValue: test.#Test @protobuf(10,type=acme.test.Test,name=test_value)
+			testValue: test.#Test @protobuf(10,acme.test.Test,name=test_value)
 		} | {
-			testValue: test_test.#AnotherTest @protobuf(11,type=acme.test.test.AnotherTest,name=test_value)
+			testValue: test_test.#AnotherTest @protobuf(11,acme.test.test.AnotherTest,name=test_value)
 		}
 	}
 
@@ -91,7 +91,7 @@
 		// Holds a set of name/value pairs.
 		entries?: {
 			[string]: string
-		} @protobuf(1,type=map<string,string>)
+		} @protobuf(1,map[string]string)
 	}
 }
 
@@ -104,47 +104,47 @@
 // configuration.
 #CompressedAttributes: {
 	// The message-level dictionary.
-	words?: [...string] @protobuf(1)
+	words?: [...string] @protobuf(1,string)
 
 	// Holds attributes of type STRING, DNS_NAME, EMAIL_ADDRESS, URI
 	strings?: {
 		[string]: int32
-	} @protobuf(2,type=map<sint32,sint32>)
+	} @protobuf(2,map[sint32]sint32)
 
 	// Holds attributes of type INT64
 	int64s?: {
 		[string]: int64
-	} @protobuf(3,type=map<sint32,int64>)
+	} @protobuf(3,map[sint32]int64)
 
 	// Holds attributes of type DOUBLE
 	doubles?: {
 		[string]: float64
-	} @protobuf(4,type=map<sint32,double>)
+	} @protobuf(4,map[sint32]double)
 
 	// Holds attributes of type BOOL
 	bools?: {
 		[string]: bool
-	} @protobuf(5,type=map<sint32,bool>)
+	} @protobuf(5,map[sint32]bool)
 
 	// Holds attributes of type TIMESTAMP
 	time?: {
 		[string]: time_1.Time
-	} @protobuf(6,type=map<sint32,google.protobuf.Timestamp>,"(gogoproto.nullable)=false","(gogoproto.stdtime)")
+	} @protobuf(6,map[sint32]google.protobuf.Timestamp,"(gogoproto.nullable)=false","(gogoproto.stdtime)")
 
 	// Holds attributes of type DURATION
 	durations?: {
 		[string]: time_1.Duration
-	} @protobuf(7,type=map<sint32,google.protobuf.Duration>,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
+	} @protobuf(7,map[sint32]google.protobuf.Duration,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
 
 	// Holds attributes of type BYTES
 	bytes?: {
 		[string]: bytes_5
-	} @protobuf(8,type=map<sint32,bytes>)
+	} @protobuf(8,map[sint32]bytes)
 
 	// Holds attributes of type STRING_MAP
 	stringMaps?: {
 		[string]: #StringMap
-	} @protobuf(9,type=map<sint32,StringMap>,string_maps,"(gogoproto.nullable)=false")
+	} @protobuf(9,map[sint32]StringMap,string_maps,"(gogoproto.nullable)=false")
 }
 
 // A map of string to string. The keys and values in this map are dictionary
@@ -153,7 +153,7 @@
 	// Holds a set of name/value pairs.
 	entries?: {
 		[string]: int32
-	} @protobuf(1,type=map<sint32,sint32>)
+	} @protobuf(1,map[sint32]sint32)
 }
 
 let bytes_5 = bytes
diff --git a/encoding/protobuf/testdata/client_config.proto.out.cue b/encoding/protobuf/testdata/client_config.proto.out.cue
index febae34..c57dbeb 100644
--- a/encoding/protobuf/testdata/client_config.proto.out.cue
+++ b/encoding/protobuf/testdata/client_config.proto.out.cue
@@ -35,44 +35,44 @@
 	#FailPolicy_value: FAIL_OPEN: 0
 
 	// Specifies the behavior when the client is unable to connect to Mixer.
-	policy?: #FailPolicy @protobuf(1)
+	policy?: #FailPolicy @protobuf(1,FailPolicy)
 
 	// Max retries on transport error.
-	maxRetry?: uint32 @protobuf(2,name=max_retry)
+	maxRetry?: uint32 @protobuf(2,uint32,name=max_retry)
 
 	// Base time to wait between retries.  Will be adjusted by exponential
 	// backoff and jitter.
-	baseRetryWait?: time.Duration @protobuf(3,type=google.protobuf.Duration,name=base_retry_wait)
+	baseRetryWait?: time.Duration @protobuf(3,google.protobuf.Duration,name=base_retry_wait)
 
 	// Max time to wait between retries.
-	maxRetryWait?: time.Duration @protobuf(4,type=google.protobuf.Duration,name=max_retry_wait)
+	maxRetryWait?: time.Duration @protobuf(4,google.protobuf.Duration,name=max_retry_wait)
 }
 
 // Defines the per-service client configuration.
 #ServiceConfig: {
 	// If true, do not call Mixer Check.
-	disableCheckCalls?: bool @protobuf(1,name=disable_check_calls)
+	disableCheckCalls?: bool @protobuf(1,bool,name=disable_check_calls)
 
 	// If true, do not call Mixer Report.
-	disableReportCalls?: bool @protobuf(2,name=disable_report_calls)
+	disableReportCalls?: bool @protobuf(2,bool,name=disable_report_calls)
 
 	// Send these attributes to Mixer in both Check and Report. This
 	// typically includes the "destination.service" attribute.
 	// In case of a per-route override, per-route attributes take precedence
 	// over the attributes supplied in the client configuration.
-	mixerAttributes?: v1.#Attributes @protobuf(3,type=Attributes,name=mixer_attributes)
+	mixerAttributes?: v1.#Attributes @protobuf(3,Attributes,name=mixer_attributes)
 
 	// HTTP API specifications to generate API attributes.
-	httpApiSpec?: [...#HTTPAPISpec] @protobuf(4,name=http_api_spec)
+	httpApiSpec?: [...#HTTPAPISpec] @protobuf(4,HTTPAPISpec,name=http_api_spec)
 
 	// Quota specifications to generate quota requirements.
-	quotaSpec?: [...#QuotaSpec] @protobuf(5,name=quota_spec)
+	quotaSpec?: [...#QuotaSpec] @protobuf(5,QuotaSpec,name=quota_spec)
 
 	// Specifies the behavior when the client is unable to connect to Mixer.
 	// This is the service-level policy. It overrides
 	// [mesh-level
 	// policy][istio.mixer.v1.config.client.TransportConfig.network_fail_policy].
-	networkFailPolicy?: #NetworkFailPolicy @protobuf(7,name=network_fail_policy)
+	networkFailPolicy?: #NetworkFailPolicy @protobuf(7,NetworkFailPolicy,name=network_fail_policy)
 
 	// Default attributes to forward to upstream. This typically
 	// includes the "source.ip" and "source.uid" attributes.
@@ -86,27 +86,27 @@
 	// 3. forwarded attributes from the source filter config (if any);
 	// 4. forwarded attributes from the source route config (if any);
 	// 5. derived attributes from the request metadata.
-	forwardAttributes?: v1.#Attributes @protobuf(8,type=Attributes,name=forward_attributes)
+	forwardAttributes?: v1.#Attributes @protobuf(8,Attributes,name=forward_attributes)
 }
 
 // Defines the transport config on how to call Mixer.
 #TransportConfig: {
 	// The flag to disable check cache.
-	disableCheckCache?: bool @protobuf(1,name=disable_check_cache)
+	disableCheckCache?: bool @protobuf(1,bool,name=disable_check_cache)
 
 	// The flag to disable quota cache.
-	disableQuotaCache?: bool @protobuf(2,name=disable_quota_cache)
+	disableQuotaCache?: bool @protobuf(2,bool,name=disable_quota_cache)
 
 	// The flag to disable report batch.
-	disableReportBatch?: bool @protobuf(3,name=disable_report_batch)
+	disableReportBatch?: bool @protobuf(3,bool,name=disable_report_batch)
 
 	// Specifies the behavior when the client is unable to connect to Mixer.
 	// This is the mesh level policy. The default value for policy is FAIL_OPEN.
-	networkFailPolicy?: #NetworkFailPolicy @protobuf(4,name=network_fail_policy)
+	networkFailPolicy?: #NetworkFailPolicy @protobuf(4,NetworkFailPolicy,name=network_fail_policy)
 
 	// Specify refresh interval to write Mixer client statistics to Envoy share
 	// memory. If not specified, the interval is 10 seconds.
-	statsUpdateInterval?: time.Duration @protobuf(5,type=google.protobuf.Duration,name=stats_update_interval)
+	statsUpdateInterval?: time.Duration @protobuf(5,google.protobuf.Duration,name=stats_update_interval)
 
 	// Name of the cluster that will forward check calls to a pool of mixer
 	// servers. Defaults to "mixer_server". By using different names for
@@ -116,7 +116,7 @@
 	//
 	// NOTE: Any value other than the default "mixer_server" will require the
 	// Istio Grafana dashboards to be reconfigured to use the new name.
-	checkCluster?: string @protobuf(6,name=check_cluster)
+	checkCluster?: string @protobuf(6,string,name=check_cluster)
 
 	// Name of the cluster that will forward report calls to a pool of mixer
 	// servers. Defaults to "mixer_server". By using different names for
@@ -126,62 +126,62 @@
 	//
 	// NOTE: Any value other than the default "mixer_server" will require the
 	// Istio Grafana dashboards to be reconfigured to use the new name.
-	reportCluster?: string @protobuf(7,name=report_cluster)
+	reportCluster?: string @protobuf(7,string,name=report_cluster)
 
 	// Default attributes to forward to Mixer upstream. This typically
 	// includes the "source.ip" and "source.uid" attributes. These
 	// attributes are consumed by the proxy in front of mixer.
-	attributesForMixerProxy?: v1.#Attributes @protobuf(8,type=Attributes,name=attributes_for_mixer_proxy)
+	attributesForMixerProxy?: v1.#Attributes @protobuf(8,Attributes,name=attributes_for_mixer_proxy)
 }
 
 // Defines the client config for HTTP.
 #HttpClientConfig: {
 	// The transport config.
-	transport?: #TransportConfig @protobuf(1)
+	transport?: #TransportConfig @protobuf(1,TransportConfig)
 
 	// Map of control configuration indexed by destination.service. This
 	// is used to support per-service configuration for cases where a
 	// mixerclient serves multiple services.
 	serviceConfigs?: {
 		[string]: #ServiceConfig
-	} @protobuf(2,type=map<string,ServiceConfig>,service_configs)
+	} @protobuf(2,map[string]ServiceConfig,service_configs)
 
 	// Default destination service name if none was specified in the
 	// client request.
-	defaultDestinationService?: string @protobuf(3,name=default_destination_service)
+	defaultDestinationService?: string @protobuf(3,string,name=default_destination_service)
 
 	// Default attributes to send to Mixer in both Check and
 	// Report. This typically includes "destination.ip" and
 	// "destination.uid" attributes.
-	mixerAttributes?: v1.#Attributes @protobuf(4,type=Attributes,name=mixer_attributes)
+	mixerAttributes?: v1.#Attributes @protobuf(4,Attributes,name=mixer_attributes)
 
 	// Default attributes to forward to upstream. This typically
 	// includes the "source.ip" and "source.uid" attributes.
-	forwardAttributes?: v1.#Attributes @protobuf(5,type=Attributes,name=forward_attributes)
+	forwardAttributes?: v1.#Attributes @protobuf(5,Attributes,name=forward_attributes)
 }
 
 // Defines the client config for TCP.
 #TcpClientConfig: {
 	// The transport config.
-	transport?: #TransportConfig @protobuf(1)
+	transport?: #TransportConfig @protobuf(1,TransportConfig)
 
 	// Default attributes to send to Mixer in both Check and
 	// Report. This typically includes "destination.ip" and
 	// "destination.uid" attributes.
-	mixerAttributes?: v1.#Attributes @protobuf(2,type=Attributes,name=mixer_attributes)
+	mixerAttributes?: v1.#Attributes @protobuf(2,Attributes,name=mixer_attributes)
 
 	// If set to true, disables Mixer check calls.
-	disableCheckCalls?: bool @protobuf(3,name=disable_check_calls)
+	disableCheckCalls?: bool @protobuf(3,bool,name=disable_check_calls)
 
 	// If set to true, disables Mixer check calls.
-	disableReportCalls?: bool @protobuf(4,name=disable_report_calls)
+	disableReportCalls?: bool @protobuf(4,bool,name=disable_report_calls)
 
 	// Quota specifications to generate quota requirements.
 	// It applies on the new TCP connections.
-	connectionQuotaSpec?: #QuotaSpec @protobuf(5,name=connection_quota_spec)
+	connectionQuotaSpec?: #QuotaSpec @protobuf(5,QuotaSpec,name=connection_quota_spec)
 
 	// Specify report interval to send periodical reports for long TCP
 	// connections. If not specified, the interval is 10 seconds. This interval
 	// should not be less than 1 second, otherwise it will be reset to 1 second.
-	reportInterval?: time.Duration @protobuf(6,type=google.protobuf.Duration,name=report_interval)
+	reportInterval?: time.Duration @protobuf(6,google.protobuf.Duration,name=report_interval)
 }
diff --git a/encoding/protobuf/testdata/gateway.proto.out.cue b/encoding/protobuf/testdata/gateway.proto.out.cue
index b2992d8..80ed7a1 100644
--- a/encoding/protobuf/testdata/gateway.proto.out.cue
+++ b/encoding/protobuf/testdata/gateway.proto.out.cue
@@ -205,7 +205,7 @@
 
 #Gateway: {
 	// REQUIRED: A list of server specifications.
-	servers?: [...#Server] @protobuf(1)
+	servers?: [...#Server] @protobuf(1,Server)
 
 	// REQUIRED: One or more labels that indicate a specific set of pods/VMs
 	// on which this gateway configuration should be applied. The scope of
@@ -214,7 +214,7 @@
 	// reside in the same namespace as the gateway workload instance.
 	selector?: {
 		[string]: string
-	} @protobuf(2,type=map<string,string>)
+	} @protobuf(2,map[string]string)
 	selector?: [name=_]: name
 }
 
@@ -282,7 +282,7 @@
 #Server: {
 	// REQUIRED: The Port on which the proxy should listen for incoming
 	// connections.
-	port?: #Port @protobuf(1)
+	port?: #Port @protobuf(1,Port)
 	port?: >10 & <100
 
 	// $hide_from_docs
@@ -290,7 +290,7 @@
 	// to. Format: `x.x.x.x` or `unix:///path/to/uds` or `unix://@foobar`
 	// (Linux abstract namespace). When using Unix domain sockets, the port
 	// number should be 0.
-	bind?: string @protobuf(4)
+	bind?: string @protobuf(4,string)
 
 	// REQUIRED. One or more hosts exposed by this gateway.
 	// While typically applicable to
@@ -316,12 +316,12 @@
 	// Private configurations (e.g., `exportTo` set to `.`) will not be
 	// available. Refer to the `exportTo` setting in `VirtualService`,
 	// `DestinationRule`, and `ServiceEntry` configurations for details.
-	hosts?: [...string] @protobuf(2)
+	hosts?: [...string] @protobuf(2,string)
 
 	#TLSOptions: {
 		// If set to true, the load balancer will send a 301 redirect for all
 		// http connections, asking the clients to use HTTPS.
-		httpsRedirect?: bool @protobuf(1,name=https_redirect)
+		httpsRedirect?: bool @protobuf(1,bool,name=https_redirect)
 
 		// TLS modes enforced by the proxy
 		#TLSmode:
@@ -359,21 +359,21 @@
 		// Optional: Indicates whether connections to this port should be
 		// secured using TLS. The value of this field determines how TLS is
 		// enforced.
-		mode?: #TLSmode @protobuf(2)
+		mode?: #TLSmode @protobuf(2,TLSmode)
 		// Extra comment.
 
 		// REQUIRED if mode is `SIMPLE` or `MUTUAL`. The path to the file
 		// holding the server-side TLS certificate to use.
-		serverCertificate?: string @protobuf(3,name=server_certificate)
+		serverCertificate?: string @protobuf(3,string,name=server_certificate)
 
 		// REQUIRED if mode is `SIMPLE` or `MUTUAL`. The path to the file
 		// holding the server's private key.
-		privateKey?: string @protobuf(4,name=private_key)
+		privateKey?: string @protobuf(4,string,name=private_key)
 
 		// REQUIRED if mode is `MUTUAL`. The path to a file containing
 		// certificate authority certificates to use in verifying a presented
 		// client side certificate.
-		caCertificates?: string @protobuf(5,name=ca_certificates)
+		caCertificates?: string @protobuf(5,string,name=ca_certificates)
 
 		// The credentialName stands for a unique identifier that can be used
 		// to identify the serverCertificate and the privateKey. The
@@ -391,11 +391,11 @@
 		// key, and the CA certificate (if using mutual TLS). Set the
 		// `ISTIO_META_USER_SDS` metadata variable in the gateway's proxy to
 		// enable the dynamic credential fetching feature.
-		credentialName?: string @protobuf(10,name=credential_name)
+		credentialName?: string @protobuf(10,string,name=credential_name)
 
 		// A list of alternate names to verify the subject identity in the
 		// certificate presented by the client.
-		subjectAltNames?: [...string] @protobuf(6,name=subject_alt_names)
+		subjectAltNames?: [...string] @protobuf(6,string,name=subject_alt_names)
 
 		// TLS protocol versions.
 		#TLSProtocol: "TLS_AUTO" | // Automatically choose the optimal TLS version.
@@ -413,38 +413,38 @@
 		}
 
 		// Optional: Minimum TLS protocol version.
-		minProtocolVersion?: #TLSProtocol @protobuf(7,name=min_protocol_version)
+		minProtocolVersion?: #TLSProtocol @protobuf(7,TLSProtocol,name=min_protocol_version)
 
 		// Optional: Maximum TLS protocol version.
-		maxProtocolVersion?: #TLSProtocol @protobuf(8,name=max_protocol_version)
+		maxProtocolVersion?: #TLSProtocol @protobuf(8,TLSProtocol,name=max_protocol_version)
 
 		// Optional: If specified, only support the specified cipher list.
 		// Otherwise default to the default cipher list supported by Envoy.
-		cipherSuites?: [...string] @protobuf(9,name=cipher_suites)
+		cipherSuites?: [...string] @protobuf(9,string,name=cipher_suites)
 	}
 
 	// Set of TLS related options that govern the server's behavior. Use
 	// these options to control if all http requests should be redirected to
 	// https, and the TLS modes to use.
-	tls?: #TLSOptions @protobuf(3)
+	tls?: #TLSOptions @protobuf(3,TLSOptions)
 
 	// The loopback IP endpoint or Unix domain socket to which traffic should
 	// be forwarded to by default. Format should be `127.0.0.1:PORT` or
 	// `unix:///path/to/socket` or `unix://@foobar` (Linux abstract namespace).
-	defaultEndpoint?: string @protobuf(5,name=default_endpoint)
+	defaultEndpoint?: string @protobuf(5,string,name=default_endpoint)
 }
 
 // Port describes the properties of a specific port of a service.
 #Port: {
 	// REQUIRED: A valid non-negative integer port number.
-	number?: uint32 @protobuf(1)
+	number?: uint32 @protobuf(1,uint32)
 
 	// REQUIRED: The protocol exposed on the port.
 	// MUST BE one of HTTP|HTTPS|GRPC|HTTP2|MONGO|TCP|TLS.
 	// TLS implies the connection will be routed based on the SNI header to
 	// the destination without terminating the TLS connection.
-	protocol?: string @protobuf(2)
+	protocol?: string @protobuf(2,string)
 
 	// Label assigned to the port.
-	name?: string @protobuf(3)
+	name?: string @protobuf(3,string)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor_proto_gen.cue
index 4cd99e6..3d39a19 100644
--- a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor_proto_gen.cue
@@ -75,76 +75,76 @@
 // The protocol compiler can output a FileDescriptorSet containing the .proto
 // files it parses.
 #FileDescriptorSet: {
-	file?: [...#FileDescriptorProto] @protobuf(1)
+	file?: [...#FileDescriptorProto] @protobuf(1,FileDescriptorProto)
 }
 
 // Describes a complete .proto file.
 #FileDescriptorProto: {
-	name?:    string @protobuf(1) // file name, relative to root of source tree
-	package?: string @protobuf(2) // e.g. "foo", "foo.bar", etc.
+	name?:    string @protobuf(1,string) // file name, relative to root of source tree
+	package?: string @protobuf(2,string) // e.g. "foo", "foo.bar", etc.
 
 	// Names of files imported by this file.
-	dependency?: [...string] @protobuf(3)
+	dependency?: [...string] @protobuf(3,string)
 
 	// Indexes of the public imported files in the dependency list above.
-	publicDependency?: [...int32] @protobuf(10,name=public_dependency)
+	publicDependency?: [...int32] @protobuf(10,int32,name=public_dependency)
 
 	// Indexes of the weak imported files in the dependency list.
 	// For Google-internal migration only. Do not use.
-	weakDependency?: [...int32] @protobuf(11,name=weak_dependency)
+	weakDependency?: [...int32] @protobuf(11,int32,name=weak_dependency)
 
 	// All top-level definitions in this file.
-	messageType?: [...#DescriptorProto] @protobuf(4,name=message_type)
-	enumType?: [...#EnumDescriptorProto] @protobuf(5,name=enum_type)
-	service?: [...#ServiceDescriptorProto] @protobuf(6)
-	extension?: [...#FieldDescriptorProto] @protobuf(7)
-	options?: #FileOptions @protobuf(8)
+	messageType?: [...#DescriptorProto] @protobuf(4,DescriptorProto,name=message_type)
+	enumType?: [...#EnumDescriptorProto] @protobuf(5,EnumDescriptorProto,name=enum_type)
+	service?: [...#ServiceDescriptorProto] @protobuf(6,ServiceDescriptorProto)
+	extension?: [...#FieldDescriptorProto] @protobuf(7,FieldDescriptorProto)
+	options?: #FileOptions @protobuf(8,FileOptions)
 
 	// This field contains optional information about the original source code.
 	// You may safely remove this entire field without harming runtime
 	// functionality of the descriptors -- the information is needed only by
 	// development tools.
-	sourceCodeInfo?: #SourceCodeInfo @protobuf(9,name=source_code_info)
+	sourceCodeInfo?: #SourceCodeInfo @protobuf(9,SourceCodeInfo,name=source_code_info)
 
 	// The syntax of the proto file.
 	// The supported values are "proto2" and "proto3".
-	syntax?: string @protobuf(12)
+	syntax?: string @protobuf(12,string)
 }
 
 // Describes a message type.
 #DescriptorProto: {
-	name?: string @protobuf(1)
-	field?: [...#FieldDescriptorProto] @protobuf(2)
-	extension?: [...#FieldDescriptorProto] @protobuf(6)
-	nestedType?: [...#DescriptorProto] @protobuf(3,name=nested_type)
-	enumType?: [...#EnumDescriptorProto] @protobuf(4,name=enum_type)
+	name?: string @protobuf(1,string)
+	field?: [...#FieldDescriptorProto] @protobuf(2,FieldDescriptorProto)
+	extension?: [...#FieldDescriptorProto] @protobuf(6,FieldDescriptorProto)
+	nestedType?: [...#DescriptorProto] @protobuf(3,DescriptorProto,name=nested_type)
+	enumType?: [...#EnumDescriptorProto] @protobuf(4,EnumDescriptorProto,name=enum_type)
 
 	#ExtensionRange: {
-		start?:   int32                  @protobuf(1) // Inclusive.
-		end?:     int32                  @protobuf(2) // Exclusive.
-		options?: #ExtensionRangeOptions @protobuf(3)
+		start?:   int32                  @protobuf(1,int32) // Inclusive.
+		end?:     int32                  @protobuf(2,int32) // Exclusive.
+		options?: #ExtensionRangeOptions @protobuf(3,ExtensionRangeOptions)
 	}
-	extensionRange?: [...#ExtensionRange] @protobuf(5,name=extension_range)
-	oneofDecl?: [...#OneofDescriptorProto] @protobuf(8,name=oneof_decl)
-	options?: #MessageOptions @protobuf(7)
+	extensionRange?: [...#ExtensionRange] @protobuf(5,ExtensionRange,name=extension_range)
+	oneofDecl?: [...#OneofDescriptorProto] @protobuf(8,OneofDescriptorProto,name=oneof_decl)
+	options?: #MessageOptions @protobuf(7,MessageOptions)
 
 	// Range of reserved tag numbers. Reserved tag numbers may not be used by
 	// fields or extension ranges in the same message. Reserved ranges may
 	// not overlap.
 	#ReservedRange: {
-		start?: int32 @protobuf(1) // Inclusive.
-		end?:   int32 @protobuf(2) // Exclusive.
+		start?: int32 @protobuf(1,int32) // Inclusive.
+		end?:   int32 @protobuf(2,int32) // Exclusive.
 	}
-	reservedRange?: [...#ReservedRange] @protobuf(9,name=reserved_range)
+	reservedRange?: [...#ReservedRange] @protobuf(9,ReservedRange,name=reserved_range)
 
 	// Reserved field names, which may not be used by fields in the same message.
 	// A given name may only be reserved once.
-	reservedName?: [...string] @protobuf(10,name=reserved_name)
+	reservedName?: [...string] @protobuf(10,string,name=reserved_name)
 }
 
 #ExtensionRangeOptions: {
 	// The parser stores options it doesn't recognize here. See above.
-	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,name=uninterpreted_option)
+	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,UninterpretedOption,name=uninterpreted_option)
 }
 
 // Describes a field within a message.
@@ -215,55 +215,55 @@
 		"LABEL_REQUIRED": 2
 		"LABEL_REPEATED": 3
 	}
-	name?:   string @protobuf(1)
-	number?: int32  @protobuf(3)
-	label?:  #Label @protobuf(4)
+	name?:   string @protobuf(1,string)
+	number?: int32  @protobuf(3,int32)
+	label?:  #Label @protobuf(4,Label)
 
 	// If type_name is set, this need not be set.  If both this and type_name
 	// are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
-	type?: #Type @protobuf(5)
+	type?: #Type @protobuf(5,Type)
 
 	// For message and enum types, this is the name of the type.  If the name
 	// starts with a '.', it is fully-qualified.  Otherwise, C++-like scoping
 	// rules are used to find the type (i.e. first the nested types within this
 	// message are searched, then within the parent, on up to the root
 	// namespace).
-	typeName?: string @protobuf(6,name=type_name)
+	typeName?: string @protobuf(6,string,name=type_name)
 
 	// For extensions, this is the name of the type being extended.  It is
 	// resolved in the same manner as type_name.
-	extendee?: string @protobuf(2)
+	extendee?: string @protobuf(2,string)
 
 	// For numeric types, contains the original text representation of the value.
 	// For booleans, "true" or "false".
 	// For strings, contains the default text contents (not escaped in any way).
 	// For bytes, contains the C escaped value.  All bytes >= 128 are escaped.
 	// TODO(kenton):  Base-64 encode?
-	defaultValue?: string @protobuf(7,name=default_value)
+	defaultValue?: string @protobuf(7,string,name=default_value)
 
 	// If set, gives the index of a oneof in the containing type's oneof_decl
 	// list.  This field is a member of that oneof.
-	oneofIndex?: int32 @protobuf(9,name=oneof_index)
+	oneofIndex?: int32 @protobuf(9,int32,name=oneof_index)
 
 	// JSON name of this field. The value is set by protocol compiler. If the
 	// user has set a "json_name" option on this field, that option's value
 	// will be used. Otherwise, it's deduced from the field's name by converting
 	// it to camelCase.
-	jsonName?: string        @protobuf(10,name=json_name)
-	options?:  #FieldOptions @protobuf(8)
+	jsonName?: string        @protobuf(10,string,name=json_name)
+	options?:  #FieldOptions @protobuf(8,FieldOptions)
 }
 
 // Describes a oneof.
 #OneofDescriptorProto: {
-	name?:    string        @protobuf(1)
-	options?: #OneofOptions @protobuf(2)
+	name?:    string        @protobuf(1,string)
+	options?: #OneofOptions @protobuf(2,OneofOptions)
 }
 
 // Describes an enum type.
 #EnumDescriptorProto: {
-	name?: string @protobuf(1)
-	value?: [...#EnumValueDescriptorProto] @protobuf(2)
-	options?: #EnumOptions @protobuf(3)
+	name?: string @protobuf(1,string)
+	value?: [...#EnumValueDescriptorProto] @protobuf(2,EnumValueDescriptorProto)
+	options?: #EnumOptions @protobuf(3,EnumOptions)
 
 	// Range of reserved numeric values. Reserved values may not be used by
 	// entries in the same enum. Reserved ranges may not overlap.
@@ -272,49 +272,49 @@
 	// is inclusive such that it can appropriately represent the entire int32
 	// domain.
 	#EnumReservedRange: {
-		start?: int32 @protobuf(1) // Inclusive.
-		end?:   int32 @protobuf(2) // Inclusive.
+		start?: int32 @protobuf(1,int32) // Inclusive.
+		end?:   int32 @protobuf(2,int32) // Inclusive.
 	}
 
 	// Range of reserved numeric values. Reserved numeric values may not be used
 	// by enum values in the same enum declaration. Reserved ranges may not
 	// overlap.
-	reservedRange?: [...#EnumReservedRange] @protobuf(4,name=reserved_range)
+	reservedRange?: [...#EnumReservedRange] @protobuf(4,EnumReservedRange,name=reserved_range)
 
 	// Reserved enum value names, which may not be reused. A given name may only
 	// be reserved once.
-	reservedName?: [...string] @protobuf(5,name=reserved_name)
+	reservedName?: [...string] @protobuf(5,string,name=reserved_name)
 }
 
 // Describes a value within an enum.
 #EnumValueDescriptorProto: {
-	name?:    string            @protobuf(1)
-	number?:  int32             @protobuf(2)
-	options?: #EnumValueOptions @protobuf(3)
+	name?:    string            @protobuf(1,string)
+	number?:  int32             @protobuf(2,int32)
+	options?: #EnumValueOptions @protobuf(3,EnumValueOptions)
 }
 
 // Describes a service.
 #ServiceDescriptorProto: {
-	name?: string @protobuf(1)
-	method?: [...#MethodDescriptorProto] @protobuf(2)
-	options?: #ServiceOptions @protobuf(3)
+	name?: string @protobuf(1,string)
+	method?: [...#MethodDescriptorProto] @protobuf(2,MethodDescriptorProto)
+	options?: #ServiceOptions @protobuf(3,ServiceOptions)
 }
 
 // Describes a method of a service.
 #MethodDescriptorProto: {
-	name?: string @protobuf(1)
+	name?: string @protobuf(1,string)
 
 	// Input and output type names.  These are resolved in the same way as
 	// FieldDescriptorProto.type_name, but must refer to a message type.
-	inputType?:  string         @protobuf(2,name=input_type)
-	outputType?: string         @protobuf(3,name=output_type)
-	options?:    #MethodOptions @protobuf(4)
+	inputType?:  string         @protobuf(2,string,name=input_type)
+	outputType?: string         @protobuf(3,string,name=output_type)
+	options?:    #MethodOptions @protobuf(4,MethodOptions)
 
 	// Identifies if client streams multiple client messages
-	clientStreaming?: bool @protobuf(5,name=client_streaming,"default=false")
+	clientStreaming?: bool @protobuf(5,bool,name=client_streaming,"default=false")
 
 	// Identifies if server streams multiple server messages
-	serverStreaming?: bool @protobuf(6,name=server_streaming,"default=false")
+	serverStreaming?: bool @protobuf(6,bool,name=server_streaming,"default=false")
 }
 
 #FileOptions: {
@@ -322,14 +322,14 @@
 	// placed.  By default, the proto package is used, but this is often
 	// inappropriate because proto packages do not normally start with backwards
 	// domain names.
-	javaPackage?: string @protobuf(1,name=java_package)
+	javaPackage?: string @protobuf(1,string,name=java_package)
 
 	// If set, all the classes from the .proto file are wrapped in a single
 	// outer class with the given name.  This applies to both Proto1
 	// (equivalent to the old "--one_java_file" option) and Proto2 (where
 	// a .proto always translates to a single class, but you may want to
 	// explicitly choose the class name).
-	javaOuterClassname?: string @protobuf(8,name=java_outer_classname)
+	javaOuterClassname?: string @protobuf(8,string,name=java_outer_classname)
 
 	// If set true, then the Java code generator will generate a separate .java
 	// file for each top-level message, enum, and service defined in the .proto
@@ -337,10 +337,10 @@
 	// named by java_outer_classname.  However, the outer class will still be
 	// generated to contain the file's getDescriptor() method as well as any
 	// top-level extensions defined in the file.
-	javaMultipleFiles?: bool @protobuf(10,name=java_multiple_files,"default=false")
+	javaMultipleFiles?: bool @protobuf(10,bool,name=java_multiple_files,"default=false")
 
 	// This option does nothing.
-	javaGenerateEqualsAndHash?: bool @protobuf(20,name=java_generate_equals_and_hash,deprecated)
+	javaGenerateEqualsAndHash?: bool @protobuf(20,bool,name=java_generate_equals_and_hash,deprecated)
 
 	// If set true, then the Java2 code generator will generate code that
 	// throws an exception whenever an attempt is made to assign a non-UTF-8
@@ -348,7 +348,7 @@
 	// Message reflection will do the same.
 	// However, an extension field still accepts non-UTF-8 byte sequences.
 	// This option has no effect on when used with the lite runtime.
-	javaStringCheckUtf8?: bool @protobuf(27,name=java_string_check_utf8,"default=false")
+	javaStringCheckUtf8?: bool @protobuf(27,bool,name=java_string_check_utf8,"default=false")
 
 	// Generated classes can be optimized for speed or code size.
 	#OptimizeMode: "SPEED" | // Generate complete code for parsing, serialization,
@@ -362,14 +362,14 @@
 		"CODE_SIZE":    2 // Use ReflectionOps to implement these methods.
 		"LITE_RUNTIME": 3
 	}
-	optimizeFor?: #OptimizeMode @protobuf(9,name=optimize_for,"default=SPEED")
+	optimizeFor?: #OptimizeMode @protobuf(9,OptimizeMode,name=optimize_for,"default=SPEED")
 
 	// Sets the Go package where structs generated from this .proto will be
 	// placed. If omitted, the Go package will be derived from the following:
 	//   - The basename of the package import path, if provided.
 	//   - Otherwise, the package statement in the .proto file, if present.
 	//   - Otherwise, the basename of the .proto file, without extension.
-	goPackage?: string @protobuf(11,name=go_package)
+	goPackage?: string @protobuf(11,string,name=go_package)
 
 	// Should generic services be generated in each language?  "Generic" services
 	// are not specific to any particular RPC system.  They are generated by the
@@ -381,56 +381,56 @@
 	// that generate code specific to your particular RPC system.  Therefore,
 	// these default to false.  Old code which depends on generic services should
 	// explicitly set them to true.
-	ccGenericServices?:   bool @protobuf(16,name=cc_generic_services,"default=false")
-	javaGenericServices?: bool @protobuf(17,name=java_generic_services,"default=false")
-	pyGenericServices?:   bool @protobuf(18,name=py_generic_services,"default=false")
-	phpGenericServices?:  bool @protobuf(42,name=php_generic_services,"default=false")
+	ccGenericServices?:   bool @protobuf(16,bool,name=cc_generic_services,"default=false")
+	javaGenericServices?: bool @protobuf(17,bool,name=java_generic_services,"default=false")
+	pyGenericServices?:   bool @protobuf(18,bool,name=py_generic_services,"default=false")
+	phpGenericServices?:  bool @protobuf(42,bool,name=php_generic_services,"default=false")
 
 	// Is this file deprecated?
 	// Depending on the target platform, this can emit Deprecated annotations
 	// for everything in the file, or it will be completely ignored; in the very
 	// least, this is a formalization for deprecating files.
-	deprecated?: bool @protobuf(23,"default=false")
+	deprecated?: bool @protobuf(23,bool,"default=false")
 
 	// Enables the use of arenas for the proto messages in this file. This applies
 	// only to generated classes for C++.
-	ccEnableArenas?: bool @protobuf(31,name=cc_enable_arenas,"default=false")
+	ccEnableArenas?: bool @protobuf(31,bool,name=cc_enable_arenas,"default=false")
 
 	// Sets the objective c class prefix which is prepended to all objective c
 	// generated classes from this .proto. There is no default.
-	objcClassPrefix?: string @protobuf(36,name=objc_class_prefix)
+	objcClassPrefix?: string @protobuf(36,string,name=objc_class_prefix)
 
 	// Namespace for generated classes; defaults to the package.
-	csharpNamespace?: string @protobuf(37,name=csharp_namespace)
+	csharpNamespace?: string @protobuf(37,string,name=csharp_namespace)
 
 	// By default Swift generators will take the proto package and CamelCase it
 	// replacing '.' with underscore and use that to prefix the types/symbols
 	// defined. When this options is provided, they will use this value instead
 	// to prefix the types/symbols defined.
-	swiftPrefix?: string @protobuf(39,name=swift_prefix)
+	swiftPrefix?: string @protobuf(39,string,name=swift_prefix)
 
 	// Sets the php class prefix which is prepended to all php generated classes
 	// from this .proto. Default is empty.
-	phpClassPrefix?: string @protobuf(40,name=php_class_prefix)
+	phpClassPrefix?: string @protobuf(40,string,name=php_class_prefix)
 
 	// Use this option to change the namespace of php generated classes. Default
 	// is empty. When this option is empty, the package name will be used for
 	// determining the namespace.
-	phpNamespace?: string @protobuf(41,name=php_namespace)
+	phpNamespace?: string @protobuf(41,string,name=php_namespace)
 
 	// Use this option to change the namespace of php generated metadata classes.
 	// Default is empty. When this option is empty, the proto file name will be
 	// used for determining the namespace.
-	phpMetadataNamespace?: string @protobuf(44,name=php_metadata_namespace)
+	phpMetadataNamespace?: string @protobuf(44,string,name=php_metadata_namespace)
 
 	// Use this option to change the package of ruby generated classes. Default
 	// is empty. When this option is not set, the package name will be used for
 	// determining the ruby package.
-	rubyPackage?: string @protobuf(45,name=ruby_package)
+	rubyPackage?: string @protobuf(45,string,name=ruby_package)
 
 	// The parser stores options it doesn't recognize here.
 	// See the documentation for the "Options" section above.
-	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,name=uninterpreted_option)
+	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,UninterpretedOption,name=uninterpreted_option)
 }
 
 #MessageOptions: {
@@ -452,18 +452,18 @@
 	//
 	// Because this is an option, the above two restrictions are not enforced by
 	// the protocol compiler.
-	messageSetWireFormat?: bool @protobuf(1,name=message_set_wire_format,"default=false")
+	messageSetWireFormat?: bool @protobuf(1,bool,name=message_set_wire_format,"default=false")
 
 	// Disables the generation of the standard "descriptor()" accessor, which can
 	// conflict with a field of the same name.  This is meant to make migration
 	// from proto1 easier; new code should avoid fields named "descriptor".
-	noStandardDescriptorAccessor?: bool @protobuf(2,name=no_standard_descriptor_accessor,"default=false")
+	noStandardDescriptorAccessor?: bool @protobuf(2,bool,name=no_standard_descriptor_accessor,"default=false")
 
 	// Is this message deprecated?
 	// Depending on the target platform, this can emit Deprecated annotations
 	// for the message, or it will be completely ignored; in the very least,
 	// this is a formalization for deprecating messages.
-	deprecated?: bool @protobuf(3,"default=false")
+	deprecated?: bool @protobuf(3,bool,"default=false")
 
 	// Whether the message is an automatically generated map entry type for the
 	// maps field.
@@ -486,10 +486,10 @@
 	// NOTE: Do not set the option in .proto files. Always use the maps syntax
 	// instead. The option should only be implicitly set by the proto compiler
 	// parser.
-	mapEntry?: bool @protobuf(7,name=map_entry)
+	mapEntry?: bool @protobuf(7,bool,name=map_entry)
 
 	// The parser stores options it doesn't recognize here. See above.
-	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,name=uninterpreted_option)
+	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,UninterpretedOption,name=uninterpreted_option)
 }
 
 #FieldOptions: {
@@ -497,7 +497,7 @@
 	// representation of the field than it normally would.  See the specific
 	// options below.  This option is not yet implemented in the open source
 	// release -- sorry, we'll try to include it in a future version!
-	ctype?: #CType @protobuf(1,"default=STRING")
+	ctype?: #CType @protobuf(1,CType,"default=STRING")
 	#CType:
 		// Default mode.
 		"STRING" |
@@ -515,7 +515,7 @@
 	// writing the tag and type for each element, the entire array is encoded as
 	// a single length-delimited blob. In proto3, only explicit setting it to
 	// false will avoid using packed encoding.
-	packed?: bool @protobuf(2)
+	packed?: bool @protobuf(2,bool)
 
 	// The jstype option determines the JavaScript type used for values of the
 	// field.  The option is permitted only for 64 bit integral and fixed types
@@ -528,7 +528,7 @@
 	//
 	// This option is an enum to permit additional types to be added, e.g.
 	// goog.math.Integer.
-	jstype?: #JSType @protobuf(6,"default=JS_NORMAL")
+	jstype?: #JSType @protobuf(6,JSType,"default=JS_NORMAL")
 	#JSType:
 		// Use the default type.
 		"JS_NORMAL" |
@@ -573,39 +573,39 @@
 	// implementation must either *always* check its required fields, or *never*
 	// check its required fields, regardless of whether or not the message has
 	// been parsed.
-	lazy?: bool @protobuf(5,"default=false")
+	lazy?: bool @protobuf(5,bool,"default=false")
 
 	// Is this field deprecated?
 	// Depending on the target platform, this can emit Deprecated annotations
 	// for accessors, or it will be completely ignored; in the very least, this
 	// is a formalization for deprecating fields.
-	deprecated?: bool @protobuf(3,"default=false")
+	deprecated?: bool @protobuf(3,bool,"default=false")
 
 	// For Google-internal migration only. Do not use.
-	weak?: bool @protobuf(10,"default=false")
+	weak?: bool @protobuf(10,bool,"default=false")
 
 	// The parser stores options it doesn't recognize here. See above.
-	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,name=uninterpreted_option)
+	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,UninterpretedOption,name=uninterpreted_option)
 }
 
 #OneofOptions: {
 	// The parser stores options it doesn't recognize here. See above.
-	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,name=uninterpreted_option)
+	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,UninterpretedOption,name=uninterpreted_option)
 }
 
 #EnumOptions: {
 	// Set this option to true to allow mapping different tag names to the same
 	// value.
-	allowAlias?: bool @protobuf(2,name=allow_alias)
+	allowAlias?: bool @protobuf(2,bool,name=allow_alias)
 
 	// Is this enum deprecated?
 	// Depending on the target platform, this can emit Deprecated annotations
 	// for the enum, or it will be completely ignored; in the very least, this
 	// is a formalization for deprecating enums.
-	deprecated?: bool @protobuf(3,"default=false")
+	deprecated?: bool @protobuf(3,bool,"default=false")
 
 	// The parser stores options it doesn't recognize here. See above.
-	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,name=uninterpreted_option)
+	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,UninterpretedOption,name=uninterpreted_option)
 }
 
 #EnumValueOptions: {
@@ -613,10 +613,10 @@
 	// Depending on the target platform, this can emit Deprecated annotations
 	// for the enum value, or it will be completely ignored; in the very least,
 	// this is a formalization for deprecating enum values.
-	deprecated?: bool @protobuf(1,"default=false")
+	deprecated?: bool @protobuf(1,bool,"default=false")
 
 	// The parser stores options it doesn't recognize here. See above.
-	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,name=uninterpreted_option)
+	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,UninterpretedOption,name=uninterpreted_option)
 }
 
 #ServiceOptions: {
@@ -629,10 +629,10 @@
 	// Depending on the target platform, this can emit Deprecated annotations
 	// for the service, or it will be completely ignored; in the very least,
 	// this is a formalization for deprecating services.
-	deprecated?: bool @protobuf(33,"default=false")
+	deprecated?: bool @protobuf(33,bool,"default=false")
 
 	// The parser stores options it doesn't recognize here. See above.
-	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,name=uninterpreted_option)
+	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,UninterpretedOption,name=uninterpreted_option)
 }
 
 #MethodOptions: {
@@ -645,7 +645,7 @@
 	// Depending on the target platform, this can emit Deprecated annotations
 	// for the method, or it will be completely ignored; in the very least,
 	// this is a formalization for deprecating methods.
-	deprecated?: bool @protobuf(33,"default=false")
+	deprecated?: bool @protobuf(33,bool,"default=false")
 
 	// Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
 	// or neither? HTTP based RPC implementation may choose GET verb for safe
@@ -659,10 +659,10 @@
 		"NO_SIDE_EFFECTS":     1
 		"IDEMPOTENT":          2
 	}
-	idempotencyLevel?: #IdempotencyLevel @protobuf(34,name=idempotency_level,"default=IDEMPOTENCY_UNKNOWN")
+	idempotencyLevel?: #IdempotencyLevel @protobuf(34,IdempotencyLevel,name=idempotency_level,"default=IDEMPOTENCY_UNKNOWN")
 
 	// The parser stores options it doesn't recognize here. See above.
-	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,name=uninterpreted_option)
+	uninterpretedOption?: [...#UninterpretedOption] @protobuf(999,UninterpretedOption,name=uninterpreted_option)
 }
 
 // A message representing a option the parser does not recognize. This only
@@ -678,19 +678,19 @@
 	// E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
 	// "foo.(bar.baz).qux".
 	#NamePart: {
-		namePart?:    string @protobuf(1,name=name_part)
-		isExtension?: bool   @protobuf(2,name=is_extension)
+		namePart?:    string @protobuf(1,string,name=name_part)
+		isExtension?: bool   @protobuf(2,bool,name=is_extension)
 	}
-	name?: [...#NamePart] @protobuf(2)
+	name?: [...#NamePart] @protobuf(2,NamePart)
 
 	// The value of the uninterpreted option, in whatever type the tokenizer
 	// identified it as during parsing. Exactly one of these should be set.
-	identifierValue?:  string  @protobuf(3,name=identifier_value)
-	positiveIntValue?: uint64  @protobuf(4,name=positive_int_value)
-	negativeIntValue?: int64   @protobuf(5,name=negative_int_value)
-	doubleValue?:      float64 @protobuf(6,type=double,name=double_value)
-	stringValue?:      bytes   @protobuf(7,name=string_value)
-	aggregateValue?:   string  @protobuf(8,name=aggregate_value)
+	identifierValue?:  string  @protobuf(3,string,name=identifier_value)
+	positiveIntValue?: uint64  @protobuf(4,uint64,name=positive_int_value)
+	negativeIntValue?: int64   @protobuf(5,int64,name=negative_int_value)
+	doubleValue?:      float64 @protobuf(6,double,name=double_value)
+	stringValue?:      bytes   @protobuf(7,bytes,name=string_value)
+	aggregateValue?:   string  @protobuf(8,string,name=aggregate_value)
 }
 
 // Encapsulates information about the original source file from which a
@@ -739,7 +739,7 @@
 	// - Code which tries to interpret locations should probably be designed to
 	//   ignore those that it doesn't understand, as more types of locations could
 	//   be recorded in the future.
-	location?: [...#Location] @protobuf(1)
+	location?: [...#Location] @protobuf(1,Location)
 
 	#Location: {
 		// Identifies which part of the FileDescriptorProto was defined at this
@@ -765,14 +765,14 @@
 		//   [ 4, 3, 2, 7 ]
 		// this path refers to the whole field declaration (from the beginning
 		// of the label to the terminating semicolon).
-		path?: [...int32] @protobuf(1,packed)
+		path?: [...int32] @protobuf(1,int32,packed)
 
 		// Always has exactly three or four elements: start line, start column,
 		// end line (optional, otherwise assumed same as start line), end column.
 		// These are packed into a single field for efficiency.  Note that line
 		// and column numbers are zero-based -- typically you will want to add
 		// 1 to each before displaying to a user.
-		span?: [...int32] @protobuf(2,packed)
+		span?: [...int32] @protobuf(2,int32,packed)
 
 		// If this SourceCodeInfo represents a complete declaration, these are any
 		// comments appearing before and after the declaration which appear to be
@@ -821,9 +821,9 @@
 		//   optional int32 grault = 6;
 		//
 		//   // ignored detached comments.
-		leadingComments?:  string @protobuf(3,name=leading_comments)
-		trailingComments?: string @protobuf(4,name=trailing_comments)
-		leadingDetachedComments?: [...string] @protobuf(6,name=leading_detached_comments)
+		leadingComments?:  string @protobuf(3,string,name=leading_comments)
+		trailingComments?: string @protobuf(4,string,name=trailing_comments)
+		leadingDetachedComments?: [...string] @protobuf(6,string,name=leading_detached_comments)
 	}
 }
 
@@ -833,23 +833,23 @@
 #GeneratedCodeInfo: {
 	// An Annotation connects some span of text in generated code to an element
 	// of its generating .proto file.
-	annotation?: [...#Annotation] @protobuf(1)
+	annotation?: [...#Annotation] @protobuf(1,Annotation)
 
 	#Annotation: {
 		// Identifies the element in the original source .proto file. This field
 		// is formatted the same as SourceCodeInfo.Location.path.
-		path?: [...int32] @protobuf(1,packed)
+		path?: [...int32] @protobuf(1,int32,packed)
 
 		// Identifies the filesystem path to the original source .proto.
-		sourceFile?: string @protobuf(2,name=source_file)
+		sourceFile?: string @protobuf(2,string,name=source_file)
 
 		// Identifies the starting offset in bytes in the generated code
 		// that relates to the identified object.
-		begin?: int32 @protobuf(3)
+		begin?: int32 @protobuf(3,int32)
 
 		// Identifies the ending offset in bytes in the generated code that
 		// relates to the identified offset. The end offset should be one past
 		// the last relevant byte (so the length of the text = end - begin).
-		end?: int32 @protobuf(4)
+		end?: int32 @protobuf(4,int32)
 	}
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/google.golang.org/genproto/googleapis/rpc/status/status_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/google.golang.org/genproto/googleapis/rpc/status/status_proto_gen.cue
index 2bfc322..e65ebb8 100644
--- a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/google.golang.org/genproto/googleapis/rpc/status/status_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/google.golang.org/genproto/googleapis/rpc/status/status_proto_gen.cue
@@ -69,13 +69,13 @@
 #Status: {
 	// The status code, which should be an enum value of
 	// [google.rpc.Code][google.rpc.Code].
-	code?: int32 @protobuf(1)
+	code?: int32 @protobuf(1,int32)
 
 	// A developer-facing error message, which should be in English. Any
 	// user-facing error message should be localized and sent in the
 	// [google.rpc.Status.details][google.rpc.Status.details] field, or localized
 	// by the client.
-	message?: string @protobuf(2)
+	message?: string @protobuf(2,string)
 
 	// A list of messages that carry the error details.  There is a common set of
 	// message types for APIs to use.
@@ -83,5 +83,5 @@
 		// A URL/resource name that uniquely identifies the type of the serialized protocol buffer message. This string must contain at least one "/" character. The last segment of the URL's path must represent the fully qualified name of the type (as in `type.googleapis.com/google.protobuf.Duration`). The name should be in a canonical form (e.g., leading "." is not accepted).
 		// The remaining fields of this object correspond to fields of the proto messsage. If the embedded message is well-known and has a custom JSON representation, that representation is assigned to the 'value' field.
 		"@type": string
-	}] @protobuf(3,type=google.protobuf.Any)
+	}] @protobuf(3,google.protobuf.Any)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test/test_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test/test_proto_gen.cue
index 7946c97..f822f29 100644
--- a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test/test_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test/test_proto_gen.cue
@@ -1,5 +1,5 @@
 package test_test
 
 #AnotherTest: {
-	test?: int32 @protobuf(1)
+	test?: int32 @protobuf(1,int32)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test_proto_gen.cue
index 6963a32..88b0846 100644
--- a/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/cue.mod/gen/googleapis.com/acme/test/test_proto_gen.cue
@@ -4,5 +4,5 @@
 	// doc comment
 	@protobuf(option (yoyo.foo)=true) // line comment
 	@protobuf(option (yoyo.bar)=false)
-	test?: int32 @protobuf(1)
+	test?: int32 @protobuf(1,int32)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
index 92a3e38..5d8217b 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
@@ -21,12 +21,12 @@
 )
 
 #StructWrap: {
-	struct?: {} @protobuf(1,type=google.protobuf.Struct)
-	any?: _ @protobuf(2,type=google.protobuf.Value)
-	listVal?: [...] @protobuf(3,type=google.protobuf.ListValue)
-	boolVal?:   bool   @protobuf(4,type=google.protobuf.BoolValue)
-	stringVal?: string @protobuf(5,type=google.protobuf.StringValue)
-	numberVal?: number @protobuf(6,type=google.protobuf.NumberValue)
+	struct?: {} @protobuf(1,google.protobuf.Struct)
+	any?: _ @protobuf(2,google.protobuf.Value)
+	listVal?: [...] @protobuf(3,google.protobuf.ListValue)
+	boolVal?:   bool   @protobuf(4,google.protobuf.BoolValue)
+	stringVal?: string @protobuf(5,google.protobuf.StringValue)
+	numberVal?: number @protobuf(6,google.protobuf.NumberValue)
 }
 
 // Attributes represents a set of typed name/value pairs. Many of Mixer's
@@ -50,39 +50,39 @@
 	// A map of attribute name to its value.
 	attributes?: {
 		[string]: #AttributeValue
-	} @protobuf(1,type=map<string,AttributeValue>)
+	} @protobuf(1,map[string]AttributeValue)
 
 	// Specifies one attribute value with different type.
 	#AttributeValue: {
 		// The attribute value.
 		{} | {
 			// Used for values of type STRING, DNS_NAME, EMAIL_ADDRESS, and URI
-			stringValue: string @protobuf(2,name=string_value)
+			stringValue: string @protobuf(2,string,name=string_value)
 		} | {
 			// Used for values of type INT64
-			int64Value: int64 @protobuf(3,name=int64_value)
+			int64Value: int64 @protobuf(3,int64,name=int64_value)
 		} | {
 			// Used for values of type DOUBLE
-			doubleValue: float64 @protobuf(4,type=double,name=double_value)
+			doubleValue: float64 @protobuf(4,double,name=double_value)
 		} | {
 			// Used for values of type BOOL
-			boolValue: bool @protobuf(5,name=bool_value)
+			boolValue: bool @protobuf(5,bool,name=bool_value)
 		} | {
 			// Used for values of type BYTES
-			bytesValue: bytes @protobuf(6,name=bytes_value)
+			bytesValue: bytes @protobuf(6,bytes,name=bytes_value)
 		} | {
 			// Used for values of type TIMESTAMP
-			timestampValue: time.Time @protobuf(7,type=google.protobuf.Timestamp,name=timestamp_value)
+			timestampValue: time.Time @protobuf(7,google.protobuf.Timestamp,name=timestamp_value)
 		} | {
 			// Used for values of type DURATION
-			durationValue: time.Duration @protobuf(8,type=google.protobuf.Duration,name=duration_value)
+			durationValue: time.Duration @protobuf(8,google.protobuf.Duration,name=duration_value)
 		} | {
 			// Used for values of type STRING_MAP
-			stringMapValue: #StringMap @protobuf(9,name=string_map_value)
+			stringMapValue: #StringMap @protobuf(9,StringMap,name=string_map_value)
 		} | {
-			testValue: test.#Test @protobuf(10,type=acme.test.Test,name=test_value)
+			testValue: test.#Test @protobuf(10,acme.test.Test,name=test_value)
 		} | {
-			testValue: test_test.#AnotherTest @protobuf(11,type=acme.test.test.AnotherTest,name=test_value)
+			testValue: test_test.#AnotherTest @protobuf(11,acme.test.test.AnotherTest,name=test_value)
 		}
 	}
 
@@ -91,7 +91,7 @@
 		// Holds a set of name/value pairs.
 		entries?: {
 			[string]: string
-		} @protobuf(1,type=map<string,string>)
+		} @protobuf(1,map[string]string)
 	}
 }
 
@@ -104,47 +104,47 @@
 // configuration.
 #CompressedAttributes: {
 	// The message-level dictionary.
-	words?: [...string] @protobuf(1)
+	words?: [...string] @protobuf(1,string)
 
 	// Holds attributes of type STRING, DNS_NAME, EMAIL_ADDRESS, URI
 	strings?: {
 		[string]: int32
-	} @protobuf(2,type=map<sint32,sint32>)
+	} @protobuf(2,map[sint32]sint32)
 
 	// Holds attributes of type INT64
 	int64s?: {
 		[string]: int64
-	} @protobuf(3,type=map<sint32,int64>)
+	} @protobuf(3,map[sint32]int64)
 
 	// Holds attributes of type DOUBLE
 	doubles?: {
 		[string]: float64
-	} @protobuf(4,type=map<sint32,double>)
+	} @protobuf(4,map[sint32]double)
 
 	// Holds attributes of type BOOL
 	bools?: {
 		[string]: bool
-	} @protobuf(5,type=map<sint32,bool>)
+	} @protobuf(5,map[sint32]bool)
 
 	// Holds attributes of type TIMESTAMP
 	time?: {
 		[string]: time_1.Time
-	} @protobuf(6,type=map<sint32,google.protobuf.Timestamp>,"(gogoproto.nullable)=false","(gogoproto.stdtime)")
+	} @protobuf(6,map[sint32]google.protobuf.Timestamp,"(gogoproto.nullable)=false","(gogoproto.stdtime)")
 
 	// Holds attributes of type DURATION
 	durations?: {
 		[string]: time_1.Duration
-	} @protobuf(7,type=map<sint32,google.protobuf.Duration>,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
+	} @protobuf(7,map[sint32]google.protobuf.Duration,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
 
 	// Holds attributes of type BYTES
 	bytes?: {
 		[string]: bytes_5
-	} @protobuf(8,type=map<sint32,bytes>)
+	} @protobuf(8,map[sint32]bytes)
 
 	// Holds attributes of type STRING_MAP
 	stringMaps?: {
 		[string]: #StringMap
-	} @protobuf(9,type=map<sint32,StringMap>,string_maps,"(gogoproto.nullable)=false")
+	} @protobuf(9,map[sint32]StringMap,string_maps,"(gogoproto.nullable)=false")
 }
 
 // A map of string to string. The keys and values in this map are dictionary
@@ -153,7 +153,7 @@
 	// Holds a set of name/value pairs.
 	entries?: {
 		[string]: int32
-	} @protobuf(1,type=map<sint32,sint32>)
+	} @protobuf(1,map[sint32]sint32)
 }
 
 let bytes_5 = bytes
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/api_spec_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/api_spec_proto_gen.cue
index 02a9b1a..e94dda1 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/api_spec_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/api_spec_proto_gen.cue
@@ -74,10 +74,10 @@
 	// List of attributes that are generated when *any* of the HTTP
 	// patterns match. This list typically includes the "api.service"
 	// and "api.version" attributes.
-	attributes?: v1.#Attributes @protobuf(1,type=Attributes)
+	attributes?: v1.#Attributes @protobuf(1,Attributes)
 
 	// List of HTTP patterns to match.
-	patterns?: [...#HTTPAPISpecPattern] @protobuf(2)
+	patterns?: [...#HTTPAPISpecPattern] @protobuf(2,HTTPAPISpecPattern)
 
 	// List of APIKey that describes how to extract an API-KEY from an
 	// HTTP request. The first API-Key match found in the list is used,
@@ -88,7 +88,7 @@
 	//
 	//     `query: key, `query: api_key`, and then `header: x-api-key`
 	//
-	apiKeys?: [...#APIKey] @protobuf(3,name=api_keys)
+	apiKeys?: [...#APIKey] @protobuf(3,APIKey,name=api_keys)
 }
 
 // HTTPAPISpecPattern defines a single pattern to match against
@@ -108,12 +108,12 @@
 	// List of attributes that are generated if the HTTP request matches
 	// the specified http_method and uri_template. This typically
 	// includes the "api.operation" attribute.
-	attributes?: v1.#Attributes @protobuf(1,type=Attributes)
+	attributes?: v1.#Attributes @protobuf(1,Attributes)
 
 	// HTTP request method to match against as defined by
 	// [rfc7231](https://tools.ietf.org/html/rfc7231#page-21). For
 	// example: GET, HEAD, POST, PUT, DELETE.
-	httpMethod?: string @protobuf(2,name=http_method)
+	httpMethod?: string @protobuf(2,string,name=http_method)
 	{} | {
 		// URI template to match against as defined by
 		// [rfc6570](https://tools.ietf.org/html/rfc6570). For example, the
@@ -124,7 +124,7 @@
 		//     /dictionary/{term:1}/{term}
 		//     /search{?q*,lang}
 		//
-		uriTemplate: string @protobuf(3,name=uri_template)
+		uriTemplate: string @protobuf(3,string,name=uri_template)
 	} | {
 		// EXPERIMENTAL:
 		//
@@ -134,7 +134,7 @@
 		//
 		//     "^/pets/(.*?)?"
 		//
-		regex: string @protobuf(4)
+		regex: string @protobuf(4,string)
 	}
 }
 
@@ -153,7 +153,7 @@
 		//
 		//     GET /something?api_key=abcdef12345
 		//
-		query: string @protobuf(1)
+		query: string @protobuf(1,string)
 	} | {
 		// API key is sent in a request header. `header` represents the
 		// header name.
@@ -164,7 +164,7 @@
 		//     GET /something HTTP/1.1
 		//     X-API-Key: abcdef12345
 		//
-		header: string @protobuf(2)
+		header: string @protobuf(2,string)
 	} | {
 		// API key is sent in a
 		// [cookie](https://swagger.io/docs/specification/authentication/cookie-authentication),
@@ -175,7 +175,7 @@
 		//     GET /something HTTP/1.1
 		//     Cookie: X-API-KEY=abcdef12345
 		//
-		cookie: string @protobuf(3)
+		cookie: string @protobuf(3,string)
 	}
 }
 
@@ -191,11 +191,11 @@
 #HTTPAPISpecReference: {
 	// REQUIRED. The short name of the HTTPAPISpec. This is the resource
 	// name defined by the metadata name field.
-	name?: string @protobuf(1)
+	name?: string @protobuf(1,string)
 
 	// Optional namespace of the HTTPAPISpec. Defaults to the encompassing
 	// HTTPAPISpecBinding's metadata namespace field.
-	namespace?: string @protobuf(2)
+	namespace?: string @protobuf(2,string)
 }
 
 // HTTPAPISpecBinding defines the binding between HTTPAPISpecs and one or more
@@ -218,10 +218,10 @@
 // ```
 #HTTPAPISpecBinding: {
 	// REQUIRED. One or more services to map the listed HTTPAPISpec onto.
-	services?: [...#IstioService] @protobuf(1)
+	services?: [...#IstioService] @protobuf(1,IstioService)
 
 	// REQUIRED. One or more HTTPAPISpec references that should be mapped to
 	// the specified service(s). The aggregate collection of match
 	// conditions defined in the HTTPAPISpecs should not overlap.
-	apiSpecs?: [...#HTTPAPISpecReference] @protobuf(2,name=api_specs)
+	apiSpecs?: [...#HTTPAPISpecReference] @protobuf(2,HTTPAPISpecReference,name=api_specs)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue
index 626345a..8ae32c2 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/client_config_proto_gen.cue
@@ -35,44 +35,44 @@
 	#FailPolicy_value: "FAIL_OPEN": 0
 
 	// Specifies the behavior when the client is unable to connect to Mixer.
-	policy?: #FailPolicy @protobuf(1)
+	policy?: #FailPolicy @protobuf(1,FailPolicy)
 
 	// Max retries on transport error.
-	maxRetry?: uint32 @protobuf(2,name=max_retry)
+	maxRetry?: uint32 @protobuf(2,uint32,name=max_retry)
 
 	// Base time to wait between retries.  Will be adjusted by exponential
 	// backoff and jitter.
-	baseRetryWait?: time.Duration @protobuf(3,type=google.protobuf.Duration,name=base_retry_wait)
+	baseRetryWait?: time.Duration @protobuf(3,google.protobuf.Duration,name=base_retry_wait)
 
 	// Max time to wait between retries.
-	maxRetryWait?: time.Duration @protobuf(4,type=google.protobuf.Duration,name=max_retry_wait)
+	maxRetryWait?: time.Duration @protobuf(4,google.protobuf.Duration,name=max_retry_wait)
 }
 
 // Defines the per-service client configuration.
 #ServiceConfig: {
 	// If true, do not call Mixer Check.
-	disableCheckCalls?: bool @protobuf(1,name=disable_check_calls)
+	disableCheckCalls?: bool @protobuf(1,bool,name=disable_check_calls)
 
 	// If true, do not call Mixer Report.
-	disableReportCalls?: bool @protobuf(2,name=disable_report_calls)
+	disableReportCalls?: bool @protobuf(2,bool,name=disable_report_calls)
 
 	// Send these attributes to Mixer in both Check and Report. This
 	// typically includes the "destination.service" attribute.
 	// In case of a per-route override, per-route attributes take precedence
 	// over the attributes supplied in the client configuration.
-	mixerAttributes?: v1.#Attributes @protobuf(3,type=Attributes,name=mixer_attributes)
+	mixerAttributes?: v1.#Attributes @protobuf(3,Attributes,name=mixer_attributes)
 
 	// HTTP API specifications to generate API attributes.
-	httpApiSpec?: [...#HTTPAPISpec] @protobuf(4,name=http_api_spec)
+	httpApiSpec?: [...#HTTPAPISpec] @protobuf(4,HTTPAPISpec,name=http_api_spec)
 
 	// Quota specifications to generate quota requirements.
-	quotaSpec?: [...#QuotaSpec] @protobuf(5,name=quota_spec)
+	quotaSpec?: [...#QuotaSpec] @protobuf(5,QuotaSpec,name=quota_spec)
 
 	// Specifies the behavior when the client is unable to connect to Mixer.
 	// This is the service-level policy. It overrides
 	// [mesh-level
 	// policy][istio.mixer.v1.config.client.TransportConfig.network_fail_policy].
-	networkFailPolicy?: #NetworkFailPolicy @protobuf(7,name=network_fail_policy)
+	networkFailPolicy?: #NetworkFailPolicy @protobuf(7,NetworkFailPolicy,name=network_fail_policy)
 
 	// Default attributes to forward to upstream. This typically
 	// includes the "source.ip" and "source.uid" attributes.
@@ -86,27 +86,27 @@
 	// 3. forwarded attributes from the source filter config (if any);
 	// 4. forwarded attributes from the source route config (if any);
 	// 5. derived attributes from the request metadata.
-	forwardAttributes?: v1.#Attributes @protobuf(8,type=Attributes,name=forward_attributes)
+	forwardAttributes?: v1.#Attributes @protobuf(8,Attributes,name=forward_attributes)
 }
 
 // Defines the transport config on how to call Mixer.
 #TransportConfig: {
 	// The flag to disable check cache.
-	disableCheckCache?: bool @protobuf(1,name=disable_check_cache)
+	disableCheckCache?: bool @protobuf(1,bool,name=disable_check_cache)
 
 	// The flag to disable quota cache.
-	disableQuotaCache?: bool @protobuf(2,name=disable_quota_cache)
+	disableQuotaCache?: bool @protobuf(2,bool,name=disable_quota_cache)
 
 	// The flag to disable report batch.
-	disableReportBatch?: bool @protobuf(3,name=disable_report_batch)
+	disableReportBatch?: bool @protobuf(3,bool,name=disable_report_batch)
 
 	// Specifies the behavior when the client is unable to connect to Mixer.
 	// This is the mesh level policy. The default value for policy is FAIL_OPEN.
-	networkFailPolicy?: #NetworkFailPolicy @protobuf(4,name=network_fail_policy)
+	networkFailPolicy?: #NetworkFailPolicy @protobuf(4,NetworkFailPolicy,name=network_fail_policy)
 
 	// Specify refresh interval to write Mixer client statistics to Envoy share
 	// memory. If not specified, the interval is 10 seconds.
-	statsUpdateInterval?: time.Duration @protobuf(5,type=google.protobuf.Duration,name=stats_update_interval)
+	statsUpdateInterval?: time.Duration @protobuf(5,google.protobuf.Duration,name=stats_update_interval)
 
 	// Name of the cluster that will forward check calls to a pool of mixer
 	// servers. Defaults to "mixer_server". By using different names for
@@ -116,7 +116,7 @@
 	//
 	// NOTE: Any value other than the default "mixer_server" will require the
 	// Istio Grafana dashboards to be reconfigured to use the new name.
-	checkCluster?: string @protobuf(6,name=check_cluster)
+	checkCluster?: string @protobuf(6,string,name=check_cluster)
 
 	// Name of the cluster that will forward report calls to a pool of mixer
 	// servers. Defaults to "mixer_server". By using different names for
@@ -126,62 +126,62 @@
 	//
 	// NOTE: Any value other than the default "mixer_server" will require the
 	// Istio Grafana dashboards to be reconfigured to use the new name.
-	reportCluster?: string @protobuf(7,name=report_cluster)
+	reportCluster?: string @protobuf(7,string,name=report_cluster)
 
 	// Default attributes to forward to Mixer upstream. This typically
 	// includes the "source.ip" and "source.uid" attributes. These
 	// attributes are consumed by the proxy in front of mixer.
-	attributesForMixerProxy?: v1.#Attributes @protobuf(8,type=Attributes,name=attributes_for_mixer_proxy)
+	attributesForMixerProxy?: v1.#Attributes @protobuf(8,Attributes,name=attributes_for_mixer_proxy)
 }
 
 // Defines the client config for HTTP.
 #HttpClientConfig: {
 	// The transport config.
-	transport?: #TransportConfig @protobuf(1)
+	transport?: #TransportConfig @protobuf(1,TransportConfig)
 
 	// Map of control configuration indexed by destination.service. This
 	// is used to support per-service configuration for cases where a
 	// mixerclient serves multiple services.
 	serviceConfigs?: {
 		[string]: #ServiceConfig
-	} @protobuf(2,type=map<string,ServiceConfig>,service_configs)
+	} @protobuf(2,map[string]ServiceConfig,service_configs)
 
 	// Default destination service name if none was specified in the
 	// client request.
-	defaultDestinationService?: string @protobuf(3,name=default_destination_service)
+	defaultDestinationService?: string @protobuf(3,string,name=default_destination_service)
 
 	// Default attributes to send to Mixer in both Check and
 	// Report. This typically includes "destination.ip" and
 	// "destination.uid" attributes.
-	mixerAttributes?: v1.#Attributes @protobuf(4,type=Attributes,name=mixer_attributes)
+	mixerAttributes?: v1.#Attributes @protobuf(4,Attributes,name=mixer_attributes)
 
 	// Default attributes to forward to upstream. This typically
 	// includes the "source.ip" and "source.uid" attributes.
-	forwardAttributes?: v1.#Attributes @protobuf(5,type=Attributes,name=forward_attributes)
+	forwardAttributes?: v1.#Attributes @protobuf(5,Attributes,name=forward_attributes)
 }
 
 // Defines the client config for TCP.
 #TcpClientConfig: {
 	// The transport config.
-	transport?: #TransportConfig @protobuf(1)
+	transport?: #TransportConfig @protobuf(1,TransportConfig)
 
 	// Default attributes to send to Mixer in both Check and
 	// Report. This typically includes "destination.ip" and
 	// "destination.uid" attributes.
-	mixerAttributes?: v1.#Attributes @protobuf(2,type=Attributes,name=mixer_attributes)
+	mixerAttributes?: v1.#Attributes @protobuf(2,Attributes,name=mixer_attributes)
 
 	// If set to true, disables Mixer check calls.
-	disableCheckCalls?: bool @protobuf(3,name=disable_check_calls)
+	disableCheckCalls?: bool @protobuf(3,bool,name=disable_check_calls)
 
 	// If set to true, disables Mixer check calls.
-	disableReportCalls?: bool @protobuf(4,name=disable_report_calls)
+	disableReportCalls?: bool @protobuf(4,bool,name=disable_report_calls)
 
 	// Quota specifications to generate quota requirements.
 	// It applies on the new TCP connections.
-	connectionQuotaSpec?: #QuotaSpec @protobuf(5,name=connection_quota_spec)
+	connectionQuotaSpec?: #QuotaSpec @protobuf(5,QuotaSpec,name=connection_quota_spec)
 
 	// Specify report interval to send periodical reports for long TCP
 	// connections. If not specified, the interval is 10 seconds. This interval
 	// should not be less than 1 second, otherwise it will be reset to 1 second.
-	reportInterval?: time.Duration @protobuf(6,type=google.protobuf.Duration,name=report_interval)
+	reportInterval?: time.Duration @protobuf(6,google.protobuf.Duration,name=report_interval)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/quota_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/quota_proto_gen.cue
index f72d4bd..98f0bd5 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/quota_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/quota_proto_gen.cue
@@ -56,7 +56,7 @@
 // Determines the quotas used for individual requests.
 #QuotaSpec: {
 	// A list of Quota rules.
-	rules?: [...#QuotaRule] @protobuf(1)
+	rules?: [...#QuotaRule] @protobuf(1,QuotaRule)
 }
 
 // Specifies a rule with list of matches and list of quotas.
@@ -64,10 +64,10 @@
 #QuotaRule: {
 	// If empty, match all request.
 	// If any of match is true, it is matched.
-	match?: [...#AttributeMatch] @protobuf(1)
+	match?: [...#AttributeMatch] @protobuf(1,AttributeMatch)
 
 	// The list of quotas to charge.
-	quotas?: [...#Quota] @protobuf(2)
+	quotas?: [...#Quota] @protobuf(2,Quota)
 }
 
 // Describes how to match a given string in HTTP headers. Match is
@@ -75,13 +75,13 @@
 #StringMatch: {
 	{} | {
 		// exact string match
-		exact: string @protobuf(1)
+		exact: string @protobuf(1,string)
 	} | {
 		// prefix-based match
-		prefix: string @protobuf(2)
+		prefix: string @protobuf(2,string)
 	} | {
 		// ECMAscript style regex-based match
-		regex: string @protobuf(3)
+		regex: string @protobuf(3,string)
 	}
 }
 
@@ -99,38 +99,38 @@
 	//       exact: POST
 	clause?: {
 		[string]: #StringMatch
-	} @protobuf(1,type=map<string,StringMatch>)
+	} @protobuf(1,map[string]StringMatch)
 }
 
 // Specifies a quota to use with quota name and amount.
 #Quota: {
 	// The quota name to charge
-	quota?: string @protobuf(1)
+	quota?: string @protobuf(1,string)
 
 	// The quota amount to charge
-	charge?: int64 @protobuf(2)
+	charge?: int64 @protobuf(2,int64)
 }
 
 // QuotaSpecBinding defines the binding between QuotaSpecs and one or more
 // IstioService.
 #QuotaSpecBinding: {
 	// REQUIRED. One or more services to map the listed QuotaSpec onto.
-	services?: [...#IstioService] @protobuf(1)
+	services?: [...#IstioService] @protobuf(1,IstioService)
 
 	// QuotaSpecReference uniquely identifies the QuotaSpec used in the
 	// Binding.
 	#QuotaSpecReference: {
 		// REQUIRED. The short name of the QuotaSpec. This is the resource
 		// name defined by the metadata name field.
-		name?: string @protobuf(1)
+		name?: string @protobuf(1,string)
 
 		// Optional namespace of the QuotaSpec. Defaults to the value of the
 		// metadata namespace field.
-		namespace?: string @protobuf(2)
+		namespace?: string @protobuf(2,string)
 	}
 
 	// REQUIRED. One or more QuotaSpec references that should be mapped to
 	// the specified service(s). The aggregate collection of match
 	// conditions defined in the QuotaSpecs should not overlap.
-	quotaSpecs?: [...#QuotaSpecReference] @protobuf(2,name=quota_specs)
+	quotaSpecs?: [...#QuotaSpecReference] @protobuf(2,QuotaSpecReference,name=quota_specs)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/service_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/service_proto_gen.cue
index 726c181..185c68f 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/service_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/config/client/service_proto_gen.cue
@@ -27,16 +27,16 @@
 // (e.g. on Kubernetes, "reviews" + "default" + "svc.cluster.local" -> "reviews.default.svc.cluster.local").
 #IstioService: {
 	// The short name of the service such as "foo".
-	name?: string @protobuf(1)
+	name?: string @protobuf(1,string)
 
 	// Optional namespace of the service. Defaults to value of metadata namespace field.
-	namespace?: string @protobuf(2)
+	namespace?: string @protobuf(2,string)
 
 	// Domain suffix used to construct the service FQDN in implementations that support such specification.
-	domain?: string @protobuf(3)
+	domain?: string @protobuf(3,string)
 
 	// The service FQDN.
-	service?: string @protobuf(4)
+	service?: string @protobuf(4,string)
 
 	// Optional one or more labels that uniquely identify the service version.
 	//
@@ -44,5 +44,5 @@
 	//
 	labels?: {
 		[string]: string
-	} @protobuf(5,type=map<string,string>)
+	} @protobuf(5,map[string]string)
 }
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue
index b3f24df..1e3fac4 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/mixer_proto_gen.cue
@@ -26,31 +26,31 @@
 	// parameters for a quota allocation
 	#QuotaParams: {
 		// Amount of quota to allocate
-		amount?: int64 @protobuf(1)
+		amount?: int64 @protobuf(1,int64)
 
 		// When true, supports returning less quota than what was requested.
-		bestEffort?: bool @protobuf(2,name=best_effort)
+		bestEffort?: bool @protobuf(2,bool,name=best_effort)
 	}
 
 	// The attributes to use for this request.
 	//
 	// Mixer's configuration determines how these attributes are used to
 	// establish the result returned in the response.
-	attributes?: #CompressedAttributes @protobuf(1,"(gogoproto.nullable)=false")
+	attributes?: #CompressedAttributes @protobuf(1,CompressedAttributes,"(gogoproto.nullable)=false")
 
 	// The number of words in the global dictionary, used with to populate the attributes.
 	// This value is used as a quick way to determine whether the client is using a dictionary that
 	// the server understands.
-	globalWordCount?: uint32 @protobuf(2,name=global_word_count)
+	globalWordCount?: uint32 @protobuf(2,uint32,name=global_word_count)
 
 	// Used for deduplicating `Check` calls in the case of failed RPCs and retries. This should be a UUID
 	// per call, where the same UUID is used for retries of the same call.
-	deduplicationId?: string @protobuf(3,name=deduplication_id)
+	deduplicationId?: string @protobuf(3,string,name=deduplication_id)
 
 	// The individual quotas to allocate
 	quotas?: {
 		[string]: #QuotaParams
-	} @protobuf(4,type=map<string,QuotaParams>,"(gogoproto.nullable)=false")
+	} @protobuf(4,map[string]QuotaParams,"(gogoproto.nullable)=false")
 }
 
 // The response generated by the Check method.
@@ -59,44 +59,44 @@
 	#PreconditionResult: {
 		// A status code of OK indicates all preconditions were satisfied. Any other code indicates not
 		// all preconditions were satisfied and details describe why.
-		status?: status_1.#Status @protobuf(1,type=google.rpc.Status,"(gogoproto.nullable)=false")
+		status?: status_1.#Status @protobuf(1,google.rpc.Status,"(gogoproto.nullable)=false")
 
 		// The amount of time for which this result can be considered valid.
-		validDuration?: time.Duration @protobuf(2,type=google.protobuf.Duration,name=valid_duration,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
+		validDuration?: time.Duration @protobuf(2,google.protobuf.Duration,name=valid_duration,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
 
 		// The number of uses for which this result can be considered valid.
-		validUseCount?: int32 @protobuf(3,name=valid_use_count)
+		validUseCount?: int32 @protobuf(3,int32,name=valid_use_count)
 
 		// The total set of attributes that were used in producing the result
 		// along with matching conditions.
-		referencedAttributes?: #ReferencedAttributes @protobuf(5,name=referenced_attributes)
+		referencedAttributes?: #ReferencedAttributes @protobuf(5,ReferencedAttributes,name=referenced_attributes)
 
 		// An optional routing directive, used to manipulate the traffic metadata
 		// whenever all preconditions are satisfied.
-		routeDirective?: #RouteDirective @protobuf(6,name=route_directive)
+		routeDirective?: #RouteDirective @protobuf(6,RouteDirective,name=route_directive)
 	}
 
 	// Expresses the result of a quota allocation.
 	#QuotaResult: {
 		// The amount of time for which this result can be considered valid.
-		validDuration?: time.Duration @protobuf(1,type=google.protobuf.Duration,name=valid_duration,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
+		validDuration?: time.Duration @protobuf(1,google.protobuf.Duration,name=valid_duration,"(gogoproto.nullable)=false","(gogoproto.stdduration)")
 
 		// The amount of granted quota. When `QuotaParams.best_effort` is true, this will be >= 0.
 		// If `QuotaParams.best_effort` is false, this will be either 0 or >= `QuotaParams.amount`.
-		grantedAmount?: int64 @protobuf(2,name=granted_amount)
+		grantedAmount?: int64 @protobuf(2,int64,name=granted_amount)
 
 		// The total set of attributes that were used in producing the result
 		// along with matching conditions.
-		referencedAttributes?: #ReferencedAttributes @protobuf(5,name=referenced_attributes,"(gogoproto.nullable)=false")
+		referencedAttributes?: #ReferencedAttributes @protobuf(5,ReferencedAttributes,name=referenced_attributes,"(gogoproto.nullable)=false")
 	}
 
 	// The precondition check results.
-	precondition?: #PreconditionResult @protobuf(2,"(gogoproto.nullable)=false")
+	precondition?: #PreconditionResult @protobuf(2,PreconditionResult,"(gogoproto.nullable)=false")
 
 	// The resulting quota, one entry per requested quota.
 	quotas?: {
 		[string]: #QuotaResult
-	} @protobuf(3,type=map<string,QuotaResult>,"(gogoproto.nullable)=false")
+	} @protobuf(3,map[string]QuotaResult,"(gogoproto.nullable)=false")
 }
 
 // Describes the attributes that were used to determine the response.
@@ -119,14 +119,14 @@
 	#AttributeMatch: {
 		// The name of the attribute. This is a dictionary index encoded in a manner identical
 		// to all strings in the [CompressedAttributes][istio.mixer.v1.CompressedAttributes] message.
-		name?: int32 @protobuf(1,type=sint32)
+		name?: int32 @protobuf(1,sint32)
 
 		// The kind of match against the attribute value.
-		condition?: #Condition @protobuf(2)
+		condition?: #Condition @protobuf(2,Condition)
 
 		// If a REGEX condition is provided for a STRING_MAP attribute,
 		// clients should use the regex value to match against map keys.
-		regex?: string @protobuf(3)
+		regex?: string @protobuf(3,string)
 
 		// A key in a STRING_MAP. When multiple keys from a STRING_MAP
 		// attribute were referenced, there will be multiple AttributeMatch
@@ -138,15 +138,15 @@
 		//
 		// If no map_key value is provided for a STRING_MAP attribute, the
 		// entire STRING_MAP will be used.
-		mapKey?: int32 @protobuf(4,type=sint32,name=map_key)
+		mapKey?: int32 @protobuf(4,sint32,name=map_key)
 	}
 
 	// The message-level dictionary. Refer to [CompressedAttributes][istio.mixer.v1.CompressedAttributes] for information
 	// on using dictionaries.
-	words?: [...string] @protobuf(1)
+	words?: [...string] @protobuf(1,string)
 
 	// Describes a set of attributes.
-	attributeMatches?: [...#AttributeMatch] @protobuf(2,name=attribute_matches,"(gogoproto.nullable)=false")
+	attributeMatches?: [...#AttributeMatch] @protobuf(2,AttributeMatch,name=attribute_matches,"(gogoproto.nullable)=false")
 }
 
 // Operation on HTTP headers to replace, append, or remove a header. Header
@@ -166,31 +166,31 @@
 	}
 
 	// Header name.
-	name?: string @protobuf(1)
+	name?: string @protobuf(1,string)
 
 	// Header value.
-	value?: string @protobuf(2)
+	value?: string @protobuf(2,string)
 
 	// Header operation.
-	operation?: #Operation @protobuf(3)
+	operation?: #Operation @protobuf(3,Operation)
 }
 
 // Expresses the routing manipulation actions to be performed on behalf of
 // Mixer in response to a precondition check.
 #RouteDirective: {
 	// Operations on the request headers.
-	requestHeaderOperations?: [...#HeaderOperation] @protobuf(1,name=request_header_operations,"(gogoproto.nullable)=false")
+	requestHeaderOperations?: [...#HeaderOperation] @protobuf(1,HeaderOperation,name=request_header_operations,"(gogoproto.nullable)=false")
 
 	// Operations on the response headers.
-	responseHeaderOperations?: [...#HeaderOperation] @protobuf(2,name=response_header_operations,"(gogoproto.nullable)=false")
+	responseHeaderOperations?: [...#HeaderOperation] @protobuf(2,HeaderOperation,name=response_header_operations,"(gogoproto.nullable)=false")
 
 	// If set, enables a direct response without proxying the request to the routing
 	// destination. Required to be a value in the 2xx or 3xx range.
-	directResponseCode?: uint32 @protobuf(3,name=direct_response_code)
+	directResponseCode?: uint32 @protobuf(3,uint32,name=direct_response_code)
 
 	// Supplies the response body for the direct response.
 	// If this setting is omitted, no body is included in the generated response.
-	directResponseBody?: string @protobuf(4,name=direct_response_body)
+	directResponseBody?: string @protobuf(4,string,name=direct_response_body)
 }
 
 // Used to report telemetry after performing one or more actions.
@@ -220,10 +220,10 @@
 	// Each `Attributes` element represents the state of a single action. Multiple actions
 	// can be provided in a single message in order to improve communication efficiency. The
 	// client can accumulate a set of actions and send them all in one single message.
-	attributes?: [...#CompressedAttributes] @protobuf(1,"(gogoproto.nullable)=false")
+	attributes?: [...#CompressedAttributes] @protobuf(1,CompressedAttributes,"(gogoproto.nullable)=false")
 
 	// Indicates how to decode the attributes sets in this request.
-	repeatedAttributesSemantics?: #RepeatedAttributesSemantics @protobuf(4,name=repeated_attributes_semantics)
+	repeatedAttributesSemantics?: #RepeatedAttributesSemantics @protobuf(4,RepeatedAttributesSemantics,name=repeated_attributes_semantics)
 
 	// The default message-level dictionary for all the attributes.
 	// Individual attribute messages can have their own dictionaries, but if they don't
@@ -231,11 +231,11 @@
 	//
 	// This makes it possible to share the same dictionary for all attributes in this
 	// request, which can substantially reduce the overall request size.
-	defaultWords?: [...string] @protobuf(2,name=default_words)
+	defaultWords?: [...string] @protobuf(2,string,name=default_words)
 
 	// The number of words in the global dictionary.
 	// To detect global dictionary out of sync between client and server.
-	globalWordCount?: uint32 @protobuf(3,name=global_word_count)
+	globalWordCount?: uint32 @protobuf(3,uint32,name=global_word_count)
 }
 
 // Used to carry responses to telemetry reports
diff --git a/encoding/protobuf/testdata/istio.io/api/networking/v1alpha3/gateway_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/networking/v1alpha3/gateway_proto_gen.cue
index 35fd9b1..80dd5cd 100644
--- a/encoding/protobuf/testdata/istio.io/api/networking/v1alpha3/gateway_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/networking/v1alpha3/gateway_proto_gen.cue
@@ -205,7 +205,7 @@
 
 #Gateway: {
 	// REQUIRED: A list of server specifications.
-	servers?: [...#Server] @protobuf(1)
+	servers?: [...#Server] @protobuf(1,Server)
 
 	// REQUIRED: One or more labels that indicate a specific set of pods/VMs
 	// on which this gateway configuration should be applied. The scope of
@@ -214,7 +214,7 @@
 	// reside in the same namespace as the gateway workload instance.
 	selector?: {
 		[string]: string
-	} @protobuf(2,type=map<string,string>)
+	} @protobuf(2,map[string]string)
 	selector?: {[name=_]: name}
 }
 
@@ -282,7 +282,7 @@
 #Server: {
 	// REQUIRED: The Port on which the proxy should listen for incoming
 	// connections.
-	port?: #Port @protobuf(1)
+	port?: #Port @protobuf(1,Port)
 	port?: >10 & <100
 
 	// $hide_from_docs
@@ -290,7 +290,7 @@
 	// to. Format: `x.x.x.x` or `unix:///path/to/uds` or `unix://@foobar`
 	// (Linux abstract namespace). When using Unix domain sockets, the port
 	// number should be 0.
-	bind?: string @protobuf(4)
+	bind?: string @protobuf(4,string)
 
 	// REQUIRED. One or more hosts exposed by this gateway.
 	// While typically applicable to
@@ -316,12 +316,12 @@
 	// Private configurations (e.g., `exportTo` set to `.`) will not be
 	// available. Refer to the `exportTo` setting in `VirtualService`,
 	// `DestinationRule`, and `ServiceEntry` configurations for details.
-	hosts?: [...string] @protobuf(2)
+	hosts?: [...string] @protobuf(2,string)
 
 	#TLSOptions: {
 		// If set to true, the load balancer will send a 301 redirect for all
 		// http connections, asking the clients to use HTTPS.
-		httpsRedirect?: bool @protobuf(1,name=https_redirect)
+		httpsRedirect?: bool @protobuf(1,bool,name=https_redirect)
 
 		// TLS modes enforced by the proxy
 		#TLSmode:
@@ -359,21 +359,21 @@
 		// Optional: Indicates whether connections to this port should be
 		// secured using TLS. The value of this field determines how TLS is
 		// enforced.
-		mode?: #TLSmode @protobuf(2)
+		mode?: #TLSmode @protobuf(2,TLSmode)
 		// Extra comment.
 
 		// REQUIRED if mode is `SIMPLE` or `MUTUAL`. The path to the file
 		// holding the server-side TLS certificate to use.
-		serverCertificate?: string @protobuf(3,name=server_certificate)
+		serverCertificate?: string @protobuf(3,string,name=server_certificate)
 
 		// REQUIRED if mode is `SIMPLE` or `MUTUAL`. The path to the file
 		// holding the server's private key.
-		privateKey?: string @protobuf(4,name=private_key)
+		privateKey?: string @protobuf(4,string,name=private_key)
 
 		// REQUIRED if mode is `MUTUAL`. The path to a file containing
 		// certificate authority certificates to use in verifying a presented
 		// client side certificate.
-		caCertificates?: string @protobuf(5,name=ca_certificates)
+		caCertificates?: string @protobuf(5,string,name=ca_certificates)
 
 		// The credentialName stands for a unique identifier that can be used
 		// to identify the serverCertificate and the privateKey. The
@@ -391,11 +391,11 @@
 		// key, and the CA certificate (if using mutual TLS). Set the
 		// `ISTIO_META_USER_SDS` metadata variable in the gateway's proxy to
 		// enable the dynamic credential fetching feature.
-		credentialName?: string @protobuf(10,name=credential_name)
+		credentialName?: string @protobuf(10,string,name=credential_name)
 
 		// A list of alternate names to verify the subject identity in the
 		// certificate presented by the client.
-		subjectAltNames?: [...string] @protobuf(6,name=subject_alt_names)
+		subjectAltNames?: [...string] @protobuf(6,string,name=subject_alt_names)
 
 		// TLS protocol versions.
 		#TLSProtocol: "TLS_AUTO" | // Automatically choose the optimal TLS version.
@@ -413,38 +413,38 @@
 		}
 
 		// Optional: Minimum TLS protocol version.
-		minProtocolVersion?: #TLSProtocol @protobuf(7,name=min_protocol_version)
+		minProtocolVersion?: #TLSProtocol @protobuf(7,TLSProtocol,name=min_protocol_version)
 
 		// Optional: Maximum TLS protocol version.
-		maxProtocolVersion?: #TLSProtocol @protobuf(8,name=max_protocol_version)
+		maxProtocolVersion?: #TLSProtocol @protobuf(8,TLSProtocol,name=max_protocol_version)
 
 		// Optional: If specified, only support the specified cipher list.
 		// Otherwise default to the default cipher list supported by Envoy.
-		cipherSuites?: [...string] @protobuf(9,name=cipher_suites)
+		cipherSuites?: [...string] @protobuf(9,string,name=cipher_suites)
 	}
 
 	// Set of TLS related options that govern the server's behavior. Use
 	// these options to control if all http requests should be redirected to
 	// https, and the TLS modes to use.
-	tls?: #TLSOptions @protobuf(3)
+	tls?: #TLSOptions @protobuf(3,TLSOptions)
 
 	// The loopback IP endpoint or Unix domain socket to which traffic should
 	// be forwarded to by default. Format should be `127.0.0.1:PORT` or
 	// `unix:///path/to/socket` or `unix://@foobar` (Linux abstract namespace).
-	defaultEndpoint?: string @protobuf(5,name=default_endpoint)
+	defaultEndpoint?: string @protobuf(5,string,name=default_endpoint)
 }
 
 // Port describes the properties of a specific port of a service.
 #Port: {
 	// REQUIRED: A valid non-negative integer port number.
-	number?: uint32 @protobuf(1)
+	number?: uint32 @protobuf(1,uint32)
 
 	// REQUIRED: The protocol exposed on the port.
 	// MUST BE one of HTTP|HTTPS|GRPC|HTTP2|MONGO|TCP|TLS.
 	// TLS implies the connection will be routed based on the SNI header to
 	// the destination without terminating the TLS connection.
-	protocol?: string @protobuf(2)
+	protocol?: string @protobuf(2,string)
 
 	// Label assigned to the port.
-	name?: string @protobuf(3)
+	name?: string @protobuf(3,string)
 }
diff --git a/encoding/protobuf/testdata/trailcomment.proto.out.cue b/encoding/protobuf/testdata/trailcomment.proto.out.cue
index 43228f1..1ed600c 100644
--- a/encoding/protobuf/testdata/trailcomment.proto.out.cue
+++ b/encoding/protobuf/testdata/trailcomment.proto.out.cue
@@ -3,17 +3,17 @@
 
 #Bar: {
 	{} | {
-		a: string @protobuf(1)
+		a: string @protobuf(1,string)
 
 		// hello world
 
 	} | {
-		b: string @protobuf(2)
+		b: string @protobuf(2,string)
 
 		// hello world
 
 	}
-	c?: int32 @protobuf(3)
+	c?: int32 @protobuf(3,int32)
 	// hello world
 
 }