aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/syscall_descriptions_syntax.md22
-rw-r--r--pkg/ast/ast.go14
-rw-r--r--pkg/ast/clone.go131
-rw-r--r--pkg/ast/format.go4
-rw-r--r--pkg/ast/parser.go14
-rw-r--r--pkg/ast/testdata/all.txt5
-rw-r--r--pkg/ast/walk.go3
-rw-r--r--pkg/compiler/check.go64
-rw-r--r--pkg/compiler/compiler.go5
-rw-r--r--pkg/compiler/compiler_test.go49
-rw-r--r--pkg/compiler/consts.go4
-rw-r--r--pkg/compiler/testdata/errors.txt63
-rw-r--r--pkg/compiler/types.go62
13 files changed, 324 insertions, 116 deletions
diff --git a/docs/syscall_descriptions_syntax.md b/docs/syscall_descriptions_syntax.md
index 8bf6b64ee..bafe0480c 100644
--- a/docs/syscall_descriptions_syntax.md
+++ b/docs/syscall_descriptions_syntax.md
@@ -137,6 +137,28 @@ accept(fd sock, ...) sock
listen(fd sock, backlog int32)
```
+## Type Aliases
+
+Complex types that are often repeated can be given short type aliases using the
+following syntax:
+
+```
+type identifier underlying_type
+```
+
+For example:
+
+```
+type signalno int32[0:65]
+type net_port proc[20000, 4, int16be]
+```
+
+Then, type alias can be used instead of the underlying type in any contexts.
+Underlying type needs to be described as if it's a struct field, that is,
+with the base type if it's required. However, type alias can be used as syscall
+arguments as well. Underlying types are currently restricted to integer types,
+`ptr`, `ptr64`, `const`, `flags` and `proc` types.
+
## Length
You can specify length of a particular field in struct or a named argument by using `len`, `bytesize` and `bitsize` types, for example:
diff --git a/pkg/ast/ast.go b/pkg/ast/ast.go
index 4c9101f79..9e87a1b81 100644
--- a/pkg/ast/ast.go
+++ b/pkg/ast/ast.go
@@ -20,6 +20,10 @@ type Description struct {
// Node is AST node interface.
type Node interface {
Info() (pos Pos, typ string, name string)
+ // Clone makes a deep copy of the node.
+ // If newPos is not zero, sets Pos of all nodes to newPos.
+ // If newPos is zero, Pos of nodes is left intact.
+ Clone(newPos Pos) Node
}
// Top-level AST nodes:
@@ -130,6 +134,16 @@ func (n *StrFlags) Info() (Pos, string, string) {
return n.Pos, "string flags", n.Name.Name
}
+type TypeDef struct {
+ Pos Pos
+ Name *Ident
+ Type *Type
+}
+
+func (n *TypeDef) Info() (Pos, string, string) {
+ return n.Pos, "type", n.Name.Name
+}
+
// Not top-level AST nodes:
type Ident struct {
diff --git a/pkg/ast/clone.go b/pkg/ast/clone.go
index 5c2d773f7..044b6c17d 100644
--- a/pkg/ast/clone.go
+++ b/pkg/ast/clone.go
@@ -3,86 +3,89 @@
package ast
-import (
- "fmt"
-)
-
func Clone(desc *Description) *Description {
desc1 := &Description{}
for _, n := range desc.Nodes {
- c, ok := n.(cloner)
- if !ok {
- panic(fmt.Sprintf("unknown top level decl: %#v", n))
- }
- desc1.Nodes = append(desc1.Nodes, c.clone())
+ desc1.Nodes = append(desc1.Nodes, n.Clone(Pos{}))
}
return desc1
}
-type cloner interface {
- clone() Node
+func selectPos(newPos, oldPos Pos) Pos {
+ if newPos.File != "" || newPos.Off != 0 || newPos.Line != 0 || newPos.Col != 0 {
+ return newPos
+ }
+ return oldPos
}
-func (n *NewLine) clone() Node {
+func (n *NewLine) Clone(newPos Pos) Node {
return &NewLine{
- Pos: n.Pos,
+ Pos: selectPos(newPos, n.Pos),
}
}
-func (n *Comment) clone() Node {
+func (n *Comment) Clone(newPos Pos) Node {
return &Comment{
- Pos: n.Pos,
+ Pos: selectPos(newPos, n.Pos),
Text: n.Text,
}
}
-func (n *Include) clone() Node {
+func (n *Include) Clone(newPos Pos) Node {
return &Include{
- Pos: n.Pos,
- File: n.File.clone(),
+ Pos: selectPos(newPos, n.Pos),
+ File: n.File.Clone(newPos).(*String),
}
}
-func (n *Incdir) clone() Node {
+func (n *Incdir) Clone(newPos Pos) Node {
return &Incdir{
- Pos: n.Pos,
- Dir: n.Dir.clone(),
+ Pos: selectPos(newPos, n.Pos),
+ Dir: n.Dir.Clone(newPos).(*String),
}
}
-func (n *Define) clone() Node {
+func (n *Define) Clone(newPos Pos) Node {
return &Define{
- Pos: n.Pos,
- Name: n.Name.clone(),
- Value: n.Value.clone(),
+ Pos: selectPos(newPos, n.Pos),
+ Name: n.Name.Clone(newPos).(*Ident),
+ Value: n.Value.Clone(newPos).(*Int),
}
}
-func (n *Resource) clone() Node {
+func (n *Resource) Clone(newPos Pos) Node {
var values []*Int
for _, v := range n.Values {
- values = append(values, v.clone())
+ values = append(values, v.Clone(newPos).(*Int))
}
return &Resource{
- Pos: n.Pos,
- Name: n.Name.clone(),
- Base: n.Base.clone(),
+ Pos: selectPos(newPos, n.Pos),
+ Name: n.Name.Clone(newPos).(*Ident),
+ Base: n.Base.Clone(newPos).(*Type),
Values: values,
}
}
-func (n *Call) clone() Node {
+func (n *TypeDef) Clone(newPos Pos) Node {
+ return &TypeDef{
+ Pos: selectPos(newPos, n.Pos),
+ Name: n.Name.Clone(newPos).(*Ident),
+ Type: n.Type.Clone(newPos).(*Type),
+ }
+}
+
+func (n *Call) Clone(newPos Pos) Node {
var args []*Field
for _, a := range n.Args {
- args = append(args, a.clone())
+ args = append(args, a.Clone(newPos).(*Field))
}
var ret *Type
if n.Ret != nil {
- ret = n.Ret.clone()
+ ret = n.Ret.Clone(newPos).(*Type)
}
return &Call{
- Pos: n.Pos,
- Name: n.Name.clone(),
+ Pos: selectPos(newPos, n.Pos),
+ Name: n.Name.Clone(newPos).(*Ident),
CallName: n.CallName,
NR: n.NR,
Args: args,
@@ -90,22 +93,22 @@ func (n *Call) clone() Node {
}
}
-func (n *Struct) clone() Node {
+func (n *Struct) Clone(newPos Pos) Node {
var fields []*Field
for _, f := range n.Fields {
- fields = append(fields, f.clone())
+ fields = append(fields, f.Clone(newPos).(*Field))
}
var attrs []*Ident
for _, a := range n.Attrs {
- attrs = append(attrs, a.clone())
+ attrs = append(attrs, a.Clone(newPos).(*Ident))
}
var comments []*Comment
for _, c := range n.Comments {
- comments = append(comments, c.clone().(*Comment))
+ comments = append(comments, c.Clone(newPos).(*Comment))
}
return &Struct{
- Pos: n.Pos,
- Name: n.Name.clone(),
+ Pos: selectPos(newPos, n.Pos),
+ Name: n.Name.Clone(newPos).(*Ident),
Fields: fields,
Attrs: attrs,
Comments: comments,
@@ -113,47 +116,47 @@ func (n *Struct) clone() Node {
}
}
-func (n *IntFlags) clone() Node {
+func (n *IntFlags) Clone(newPos Pos) Node {
var values []*Int
for _, v := range n.Values {
- values = append(values, v.clone())
+ values = append(values, v.Clone(newPos).(*Int))
}
return &IntFlags{
- Pos: n.Pos,
- Name: n.Name.clone(),
+ Pos: selectPos(newPos, n.Pos),
+ Name: n.Name.Clone(newPos).(*Ident),
Values: values,
}
}
-func (n *StrFlags) clone() Node {
+func (n *StrFlags) Clone(newPos Pos) Node {
var values []*String
for _, v := range n.Values {
- values = append(values, v.clone())
+ values = append(values, v.Clone(newPos).(*String))
}
return &StrFlags{
- Pos: n.Pos,
- Name: n.Name.clone(),
+ Pos: selectPos(newPos, n.Pos),
+ Name: n.Name.Clone(newPos).(*Ident),
Values: values,
}
}
-func (n *Ident) clone() *Ident {
+func (n *Ident) Clone(newPos Pos) Node {
return &Ident{
- Pos: n.Pos,
+ Pos: selectPos(newPos, n.Pos),
Name: n.Name,
}
}
-func (n *String) clone() *String {
+func (n *String) Clone(newPos Pos) Node {
return &String{
- Pos: n.Pos,
+ Pos: selectPos(newPos, n.Pos),
Value: n.Value,
}
}
-func (n *Int) clone() *Int {
+func (n *Int) Clone(newPos Pos) Node {
return &Int{
- Pos: n.Pos,
+ Pos: selectPos(newPos, n.Pos),
Value: n.Value,
ValueHex: n.ValueHex,
Ident: n.Ident,
@@ -161,19 +164,19 @@ func (n *Int) clone() *Int {
}
}
-func (n *Type) clone() *Type {
+func (n *Type) Clone(newPos Pos) Node {
var args []*Type
for _, a := range n.Args {
- args = append(args, a.clone())
+ args = append(args, a.Clone(newPos).(*Type))
}
return &Type{
- Pos: n.Pos,
+ Pos: selectPos(newPos, n.Pos),
Value: n.Value,
ValueHex: n.ValueHex,
Ident: n.Ident,
String: n.String,
HasColon: n.HasColon,
- Pos2: n.Pos2,
+ Pos2: selectPos(newPos, n.Pos2),
Value2: n.Value2,
Value2Hex: n.Value2Hex,
Ident2: n.Ident2,
@@ -181,15 +184,15 @@ func (n *Type) clone() *Type {
}
}
-func (n *Field) clone() *Field {
+func (n *Field) Clone(newPos Pos) Node {
var comments []*Comment
for _, c := range n.Comments {
- comments = append(comments, c.clone().(*Comment))
+ comments = append(comments, c.Clone(newPos).(*Comment))
}
return &Field{
- Pos: n.Pos,
- Name: n.Name.clone(),
- Type: n.Type.clone(),
+ Pos: selectPos(newPos, n.Pos),
+ Name: n.Name.Clone(newPos).(*Ident),
+ Type: n.Type.Clone(newPos).(*Type),
NewBlock: n.NewBlock,
Comments: comments,
}
diff --git a/pkg/ast/format.go b/pkg/ast/format.go
index 0f95d7ebf..a77662df8 100644
--- a/pkg/ast/format.go
+++ b/pkg/ast/format.go
@@ -62,6 +62,10 @@ func (res *Resource) serialize(w io.Writer) {
fmt.Fprintf(w, "\n")
}
+func (typedef *TypeDef) serialize(w io.Writer) {
+ fmt.Fprintf(w, "type %v %v\n", typedef.Name.Name, fmtType(typedef.Type))
+}
+
func (c *Call) serialize(w io.Writer) {
fmt.Fprintf(w, "%v(", c.Name.Name)
for i, a := range c.Args {
diff --git a/pkg/ast/parser.go b/pkg/ast/parser.go
index 9e1ab679e..db211ab2a 100644
--- a/pkg/ast/parser.go
+++ b/pkg/ast/parser.go
@@ -128,6 +128,9 @@ func (p *parser) parseTop() Node {
return p.parseResource()
case tokIdent:
name := p.parseIdent()
+ if name.Name == "type" {
+ return p.parseTypeDef()
+ }
switch p.tok {
case tokLParen:
return p.parseCall(name)
@@ -245,6 +248,17 @@ func (p *parser) parseResource() *Resource {
}
}
+func (p *parser) parseTypeDef() *TypeDef {
+ pos0 := p.pos
+ name := p.parseIdent()
+ typ := p.parseType()
+ return &TypeDef{
+ Pos: pos0,
+ Name: name,
+ Type: typ,
+ }
+}
+
func (p *parser) parseCall(name *Ident) *Call {
c := &Call{
Pos: name.Pos,
diff --git a/pkg/ast/testdata/all.txt b/pkg/ast/testdata/all.txt
index 3f647e4ca..fedcc51a2 100644
--- a/pkg/ast/testdata/all.txt
+++ b/pkg/ast/testdata/all.txt
@@ -45,3 +45,8 @@ s2 {
# comment
}
+
+type bool8 int8
+type net_port proc[1, 2, int16be]
+type bool16 ### unexpected '\n', expecting int, identifier, string
+type type4:4 int32 ### unexpected ':', expecting int, identifier, string
diff --git a/pkg/ast/walk.go b/pkg/ast/walk.go
index 79e0b4bec..7bc34410e 100644
--- a/pkg/ast/walk.go
+++ b/pkg/ast/walk.go
@@ -32,6 +32,9 @@ func WalkNode(n0 Node, cb func(n Node)) {
for _, v := range n.Values {
WalkNode(v, cb)
}
+ case *TypeDef:
+ WalkNode(n.Name, cb)
+ WalkNode(n.Type, cb)
case *Call:
WalkNode(n.Name, cb)
for _, f := range n.Args {
diff --git a/pkg/compiler/check.go b/pkg/compiler/check.go
index bfe1c9d75..ff87ead79 100644
--- a/pkg/compiler/check.go
+++ b/pkg/compiler/check.go
@@ -34,7 +34,7 @@ func (comp *compiler) checkNames() {
calls := make(map[string]*ast.Call)
for _, decl := range comp.desc.Nodes {
switch decl.(type) {
- case *ast.Resource, *ast.Struct:
+ case *ast.Resource, *ast.Struct, *ast.TypeDef:
pos, typ, name := decl.Info()
if reservedName[name] {
comp.error(pos, "%v uses reserved name %v", typ, name)
@@ -49,6 +49,11 @@ func (comp *compiler) checkNames() {
name, prev.Pos)
continue
}
+ if prev := comp.typedefs[name]; prev != nil {
+ comp.error(pos, "type %v redeclared, previously declared as type alias at %v",
+ name, prev.Pos)
+ continue
+ }
if prev := comp.structs[name]; prev != nil {
_, typ, _ := prev.Info()
comp.error(pos, "type %v redeclared, previously declared as %v at %v",
@@ -57,6 +62,8 @@ func (comp *compiler) checkNames() {
}
if res, ok := decl.(*ast.Resource); ok {
comp.resources[name] = res
+ } else if n, ok := decl.(*ast.TypeDef); ok {
+ comp.typedefs[name] = n
} else if str, ok := decl.(*ast.Struct); ok {
comp.structs[name] = str
}
@@ -148,19 +155,32 @@ func (comp *compiler) checkFields() {
func (comp *compiler) checkTypes() {
for _, decl := range comp.desc.Nodes {
switch n := decl.(type) {
+ case *ast.TypeDef:
+ if comp.typedefs[n.Name.Name] == nil {
+ continue
+ }
+ err0 := comp.errors
+ comp.checkType(n.Type, false, false, false, false, true, true)
+ if err0 != comp.errors {
+ delete(comp.typedefs, n.Name.Name)
+ }
+ }
+ }
+ for _, decl := range comp.desc.Nodes {
+ switch n := decl.(type) {
case *ast.Resource:
- comp.checkType(n.Base, false, false, false, true)
+ comp.checkType(n.Base, false, false, false, true, false, false)
case *ast.Struct:
for _, f := range n.Fields {
- comp.checkType(f.Type, false, false, !n.IsUnion, false)
+ comp.checkType(f.Type, false, false, !n.IsUnion, false, false, false)
}
comp.checkStruct(n)
case *ast.Call:
for _, a := range n.Args {
- comp.checkType(a.Type, true, false, false, false)
+ comp.checkType(a.Type, true, false, false, false, false, false)
}
if n.Ret != nil {
- comp.checkType(n.Ret, true, true, false, false)
+ comp.checkType(n.Ret, true, true, false, false, false, false)
}
}
}
@@ -459,7 +479,7 @@ func (comp *compiler) checkStruct(n *ast.Struct) {
}
}
-func (comp *compiler) checkType(t *ast.Type, isArg, isRet, isStruct, isResourceBase bool) {
+func (comp *compiler) checkType(t *ast.Type, isArg, isRet, isStruct, isResourceBase, isTypedef, isTypedefCtx bool) {
if unexpected, _, ok := checkTypeKind(t, kindIdent); !ok {
comp.error(t.Pos, "unexpected %v, expect type", unexpected)
return
@@ -469,6 +489,32 @@ func (comp *compiler) checkType(t *ast.Type, isArg, isRet, isStruct, isResourceB
comp.error(t.Pos, "unknown type %v", t.Ident)
return
}
+ if desc == typeTypedef {
+ if isTypedefCtx {
+ comp.error(t.Pos, "type aliases can't refer to other type aliases")
+ return
+ }
+ if t.HasColon {
+ comp.error(t.Pos, "type alias %v with ':'", t.Ident)
+ return
+ }
+ if len(t.Args) != 0 {
+ comp.error(t.Pos, "type alias %v with arguments", t.Ident)
+ return
+ }
+ *t = *comp.typedefs[t.Ident].Type.Clone(t.Pos).(*ast.Type)
+ desc = comp.getTypeDesc(t)
+ if isArg && desc.NeedBase {
+ baseTypePos := len(t.Args) - 1
+ if t.Args[baseTypePos].Ident == "opt" {
+ baseTypePos--
+ }
+ copy(t.Args[baseTypePos:], t.Args[baseTypePos+1:])
+ t.Args = t.Args[:len(t.Args)-1]
+ }
+ comp.checkType(t, isArg, isRet, isStruct, isResourceBase, isTypedef, isTypedefCtx)
+ return
+ }
if t.HasColon {
if !desc.AllowColon {
comp.error(t.Pos2, "unexpected ':'")
@@ -487,6 +533,10 @@ func (comp *compiler) checkType(t *ast.Type, isArg, isRet, isStruct, isResourceB
comp.error(t.Pos, "%v can't be syscall argument", t.Ident)
return
}
+ if isTypedef && !desc.CanBeTypedef {
+ comp.error(t.Pos, "%v can't be type alias target", t.Ident)
+ return
+ }
if isResourceBase && !desc.ResourceBase {
comp.error(t.Pos, "%v can't be resource base (int types can)", t.Ident)
return
@@ -519,7 +569,7 @@ func (comp *compiler) checkType(t *ast.Type, isArg, isRet, isStruct, isResourceB
err0 := comp.errors
for i, arg := range args {
if desc.Args[i].Type == typeArgType {
- comp.checkType(arg, false, isRet, false, false)
+ comp.checkType(arg, false, isRet, false, false, false, isTypedefCtx)
} else {
comp.checkTypeArg(t, arg, desc.Args[i])
}
diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go
index d250eda68..0be7f2817 100644
--- a/pkg/compiler/compiler.go
+++ b/pkg/compiler/compiler.go
@@ -53,6 +53,7 @@ func Compile(desc *ast.Description, consts map[string]uint64, target *targets.Ta
ptrSize: target.PtrSize,
unsupported: make(map[string]bool),
resources: make(map[string]*ast.Resource),
+ typedefs: make(map[string]*ast.TypeDef),
structs: make(map[string]*ast.Struct),
intFlags: make(map[string]*ast.IntFlags),
strFlags: make(map[string]*ast.StrFlags),
@@ -89,6 +90,7 @@ type compiler struct {
unsupported map[string]bool
resources map[string]*ast.Resource
+ typedefs map[string]*ast.TypeDef
structs map[string]*ast.Struct
intFlags map[string]*ast.IntFlags
strFlags map[string]*ast.StrFlags
@@ -163,6 +165,9 @@ func (comp *compiler) getTypeDesc(t *ast.Type) *typeDesc {
if comp.structs[t.Ident] != nil {
return typeStruct
}
+ if comp.typedefs[t.Ident] != nil {
+ return typeTypedef
+ }
return nil
}
diff --git a/pkg/compiler/compiler_test.go b/pkg/compiler/compiler_test.go
index ac2d88167..f26c272e6 100644
--- a/pkg/compiler/compiler_test.go
+++ b/pkg/compiler/compiler_test.go
@@ -5,7 +5,6 @@ package compiler
import (
"path/filepath"
- "runtime"
"testing"
"github.com/google/syzkaller/pkg/ast"
@@ -13,21 +12,33 @@ import (
)
func TestCompileAll(t *testing.T) {
- eh := func(pos ast.Pos, msg string) {
- t.Logf("%v: %v", pos, msg)
- }
- desc := ast.ParseGlob(filepath.Join("..", "..", "sys", "linux", "*.txt"), eh)
- if desc == nil {
- t.Fatalf("parsing failed")
- }
- glob := filepath.Join("..", "..", "sys", "linux", "*_"+runtime.GOARCH+".const")
- consts := DeserializeConstsGlob(glob, eh)
- if consts == nil {
- t.Fatalf("reading consts failed")
- }
- prog := Compile(desc, consts, targets.List["linux"]["amd64"], eh)
- if prog == nil {
- t.Fatalf("compilation failed")
+ for os, arches := range targets.List {
+ os, arches := os, arches
+ t.Run(os, func(t *testing.T) {
+ t.Parallel()
+ eh := func(pos ast.Pos, msg string) {
+ t.Logf("%v: %v", pos, msg)
+ }
+ path := filepath.Join("..", "..", "sys", os)
+ desc := ast.ParseGlob(filepath.Join(path, "*.txt"), eh)
+ if desc == nil {
+ t.Fatalf("parsing failed")
+ }
+ for arch, target := range arches {
+ arch, target := arch, target
+ t.Run(arch, func(t *testing.T) {
+ t.Parallel()
+ consts := DeserializeConstsGlob(filepath.Join(path, "*_"+arch+".const"), eh)
+ if consts == nil {
+ t.Fatalf("reading consts failed")
+ }
+ prog := Compile(desc, consts, target, eh)
+ if prog == nil {
+ t.Fatalf("compilation failed")
+ }
+ })
+ }
+ })
}
}
@@ -38,7 +49,7 @@ func TestErrors(t *testing.T) {
"C1": 1,
"C2": 2,
}
- target := targets.List["linux"]["amd64"]
+ target := targets.List["test"]["64"]
for _, name := range []string{"errors.txt", "errors2.txt"} {
name := name
t.Run(name, func(t *testing.T) {
@@ -69,7 +80,7 @@ func TestFuzz(t *testing.T) {
for _, data := range inputs {
desc := ast.Parse([]byte(data), "", eh)
if desc != nil {
- Compile(desc, consts, targets.List["linux"]["amd64"], eh)
+ Compile(desc, consts, targets.List["test"]["64"], eh)
}
}
}
@@ -96,7 +107,7 @@ s2 {
if desc == nil {
t.Fatal("failed to parse")
}
- p := Compile(desc, map[string]uint64{"__NR_foo": 1}, targets.List["linux"]["amd64"], nil)
+ p := Compile(desc, map[string]uint64{"__NR_foo": 1}, targets.List["test"]["64"], nil)
if p == nil {
t.Fatal("failed to compile")
}
diff --git a/pkg/compiler/consts.go b/pkg/compiler/consts.go
index 61198c96a..338dea4b1 100644
--- a/pkg/compiler/consts.go
+++ b/pkg/compiler/consts.go
@@ -145,7 +145,7 @@ func (comp *compiler) assignSyscallNumbers(consts map[string]uint64) {
comp.warning(c.Pos, "unsupported syscall: %v due to missing const %v",
c.CallName, str)
}
- case *ast.IntFlags, *ast.Resource, *ast.Struct, *ast.StrFlags:
+ case *ast.IntFlags, *ast.Resource, *ast.Struct, *ast.StrFlags, *ast.TypeDef:
top = append(top, decl)
case *ast.NewLine, *ast.Comment, *ast.Include, *ast.Incdir, *ast.Define:
// These are not needed anymore.
@@ -176,7 +176,7 @@ func (comp *compiler) patchConsts(consts map[string]uint64) {
top = append(top, n)
case *ast.StrFlags:
top = append(top, decl)
- case *ast.Resource, *ast.Struct, *ast.Call:
+ case *ast.Resource, *ast.Struct, *ast.Call, *ast.TypeDef:
// Walk whole tree and replace consts in Int's and Type's.
missing := ""
ast.WalkNode(decl, func(n0 ast.Node) {
diff --git a/pkg/compiler/testdata/errors.txt b/pkg/compiler/testdata/errors.txt
index ade8c624f..237519aa3 100644
--- a/pkg/compiler/testdata/errors.txt
+++ b/pkg/compiler/testdata/errors.txt
@@ -178,3 +178,66 @@ define d1 `some C expression`
define d2 some C expression
define d2 SOMETHING ### duplicate define d2
define d3 1
+
+# Type aliases.
+
+type bool8 int8[0:1]
+type bool16 int16[0:1]
+type net_port proc[100, 1, int16be]
+resource typeres0[bool8]
+typestruct {
+ f1 bool8
+ f2 bool16
+}
+typeunion [
+ f1 bool8
+ f2 bool16
+]
+
+type type0 int8
+type type0 int8 ### type type0 redeclared, previously declared as type alias at errors.txt:197:6
+resource type0[int32] ### type type0 redeclared, previously declared as type alias at errors.txt:197:6
+type0 = 0, 1
+type type1 type1 ### type aliases can't refer to other type aliases
+type type2 int8:4 ### unexpected ':', only struct fields can be bitfields
+type type3 type2 ### unknown type type2
+type type4 const[0] ### wrong number of arguments for type const, expect value, base type
+type type5 typeunion ### typeunion can't be type alias target
+type type6 len[foo, int32] ### len can't be type alias target
+type type7 len[foo] ### len can't be type alias target
+resource typeres1[int32]
+type type8 typeres1 ### typeres1 can't be type alias target
+type int8 int8 ### type name int8 conflicts with builtin type
+type opt int8 ### type uses reserved name opt
+type type9 const[0, int8]
+type type10 type0 ### type aliases can't refer to other type aliases
+type type11 typestruct11 ### typestruct11 can't be type alias target
+type type12 proc[123, 2, int16, opt]
+type type13 ptr[in, typestruct13]
+type type14 flags[type0, int32]
+type type15 const[0, type0] ### unexpected value type0 for base type argument of const type, expect [int8 int16 int32 int64 int16be int32be int64be intptr]
+type type16 ptr[in, type0] ### type aliases can't refer to other type aliases
+
+typestruct11 {
+ f type11 ### unknown type type11
+}
+
+typestruct12 {
+ f type11 ### unknown type type11
+}
+
+typestruct13 {
+ f1 type9
+ f2 type12
+}
+
+foo$100(a bool8, b bool16)
+foo$101(a type5) ### unknown type type5
+foo$102(a type2) ### unknown type type2
+foo$103(a type0:4) ### type alias type0 with ':'
+foo$104(a type0[opt]) ### type alias type0 with arguments
+foo$105() type0
+foo$106() type6 ### unknown type type6
+foo$107(a type9, b type12)
+foo$108(a flags[type0])
+foo$109(a ptr[in, type0])
diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go
index 30d3dac64..fbd44c0fa 100644
--- a/pkg/compiler/types.go
+++ b/pkg/compiler/types.go
@@ -15,6 +15,7 @@ import (
type typeDesc struct {
Names []string
CanBeArg bool // can be argument of syscall?
+ CanBeTypedef bool // can be type alias target?
CantBeOpt bool // can't be marked as opt?
CantBeRet bool // can't be syscall return (directly or indirectly)?
NeedBase bool // needs base type when used as field?
@@ -54,6 +55,7 @@ const (
var typeInt = &typeDesc{
Names: []string{"int8", "int16", "int32", "int64", "int16be", "int32be", "int64be", "intptr"},
CanBeArg: true,
+ CanBeTypedef: true,
AllowColon: true,
ResourceBase: true,
OptArgs: 1,
@@ -78,9 +80,10 @@ var typeInt = &typeDesc{
}
var typePtr = &typeDesc{
- Names: []string{"ptr", "ptr64"},
- CanBeArg: true,
- Args: []namedArg{{"direction", typeArgDir}, {"type", typeArgType}},
+ Names: []string{"ptr", "ptr64"},
+ CanBeArg: true,
+ CanBeTypedef: true,
+ Args: []namedArg{{"direction", typeArgDir}, {"type", typeArgType}},
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
base.ArgDir = prog.DirIn // pointers are always in
base.TypeSize = comp.ptrSize
@@ -175,11 +178,12 @@ var typeLen = &typeDesc{
}
var typeConst = &typeDesc{
- Names: []string{"const"},
- CanBeArg: true,
- CantBeOpt: true,
- NeedBase: true,
- Args: []namedArg{{"value", typeArgInt}},
+ Names: []string{"const"},
+ CanBeArg: true,
+ CanBeTypedef: true,
+ CantBeOpt: true,
+ NeedBase: true,
+ Args: []namedArg{{"value", typeArgInt}},
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
return &prog.ConstType{
IntTypeCommon: base,
@@ -193,11 +197,12 @@ var typeArgLenTarget = &typeArg{
}
var typeFlags = &typeDesc{
- Names: []string{"flags"},
- CanBeArg: true,
- CantBeOpt: true,
- NeedBase: true,
- Args: []namedArg{{"flags", typeArgFlags}},
+ Names: []string{"flags"},
+ CanBeArg: true,
+ CanBeTypedef: true,
+ CantBeOpt: true,
+ NeedBase: true,
+ Args: []namedArg{{"flags", typeArgFlags}},
Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
name := args[0].Ident
base.TypeName = name
@@ -334,10 +339,11 @@ func genCsumKind(t *ast.Type) prog.CsumKind {
}
var typeProc = &typeDesc{
- Names: []string{"proc"},
- CanBeArg: true,
- NeedBase: true,
- Args: []namedArg{{"range start", typeArgInt}, {"per-proc values", typeArgInt}},
+ Names: []string{"proc"},
+ CanBeArg: true,
+ CanBeTypedef: true,
+ NeedBase: true,
+ Args: []namedArg{{"range start", typeArgInt}, {"per-proc values", typeArgInt}},
Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) {
start := args[0].Value
perProc := args[1].Value
@@ -522,7 +528,7 @@ var typeArgType = &typeArg{
}
var typeResource = &typeDesc{
- // No Names, but compiler knows how to match it.
+ // No Names, but getTypeDesc knows how to match it.
CanBeArg: true,
ResourceBase: true,
// Gen is assigned below to avoid initialization loop.
@@ -544,7 +550,7 @@ func init() {
}
var typeStruct = &typeDesc{
- // No Names, but compiler knows how to match it.
+ // No Names, but getTypeDesc knows how to match it.
CantBeOpt: true,
// Varlen/Gen are assigned below due to initialization cycle.
}
@@ -579,6 +585,16 @@ func init() {
}
}
+var typeTypedef = &typeDesc{
+ // No Names, but getTypeDesc knows how to match it.
+ Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) {
+ panic("must not be called")
+ },
+ Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
+ panic("must not be called")
+ },
+}
+
var typeArgDir = &typeArg{
Kind: kindIdent,
Names: []string{"in", "out", "inout"},
@@ -660,16 +676,14 @@ func init() {
typeConst,
typeFlags,
typeFilename,
- typeFileoff,
+ typeFileoff, // make a type alias
typeVMA,
- typeSignalno,
+ typeSignalno, // make a type alias
typeCsum,
typeProc,
typeText,
- typeBuffer,
+ typeBuffer, // make a type alias
typeString,
- typeResource,
- typeStruct,
}
for _, desc := range builtins {
for _, name := range desc.Names {