// Copyright 2022 The Gc Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gc // modernc.org/gc/v3 import ( "fmt" "go/constant" "go/token" "os" "path/filepath" "strings" "sync" ) type ctx struct { ast *AST cfg *Config errs errList iota int64 pkg *Package int32 Type // Set by newCtx untypedFloat Type // Set by newCtx untypedInt Type // Set by newCtx untypedString Type // Set by newCtx } func newCtx(cfg *Config) (r *ctx) { r = &ctx{ cfg: cfg, iota: -1, // -> Invalid } r.int32 = r.newPredeclaredType(znode, Int32) r.untypedFloat = r.newPredeclaredType(znode, UntypedFloat) r.untypedInt = r.newPredeclaredType(znode, UntypedInt) r.untypedString = r.newPredeclaredType(znode, UntypedString) return r } func (c *ctx) err(n Node, msg string, args ...interface{}) { var pos token.Position if n != nil { pos = n.Position() } s := fmt.Sprintf(msg, args...) if trcTODOs && strings.HasPrefix(s, "TODO") { fmt.Fprintf(os.Stderr, "%v: %s (%v)\n", pos, s, origin(2)) os.Stderr.Sync() } switch { case extendedErrors: c.errs.err(pos, "%s (%v: %v: %v)", s, origin(4), origin(3), origin(2)) default: c.errs.err(pos, s) } } func (c *ctx) isBuiltin() bool { return c.pkg.Scope.kind == UniverseScope } func (c *ctx) isUnsafe() bool { return c.pkg.isUnsafe } func (c *ctx) lookup(sc *Scope, id Token) (pkg *Package, in *Scope, r named) { sc0 := sc pkg = c.pkg for { switch in, nm := sc.lookup(id); x := nm.n.(type) { case *TypeDefNode: if sc.kind == UniverseScope { if sc0.kind != UniverseScope && token.IsExported(id.Src()) { // trc("%v: %q %v %v", id.Position(), id.Src(), sc0.kind, sc.kind) return nil, nil, r } } return x.pkg, in, nm default: panic(todo("%v: %q %T", id.Position(), id.Src(), x)) } } } func (n *Package) check(c *ctx) (err error) { if n == nil { return nil } c.pkg = n // trc("PKG %q", n.ImportPath) // defer func() { trc("PKG %q -> err: %v", n.ImportPath, err) }() for _, v := range n.GoFiles { path := filepath.Join(n.FSPath, v.Name()) n.AST[path].check(c) } return c.errs.Err() } func (n *AST) check(c *ctx) { if n == nil { return } c.ast = n n.SourceFile.check(c) } func (n *SourceFileNode) check(c *ctx) { if n == nil { return } n.PackageClause.check(c) for l := n.ImportDeclList; l != nil; l = l.List { l.ImportDecl.check(c) } for l := n.TopLevelDeclList; l != nil; l = l.List { switch x := l.TopLevelDecl.(type) { case *TypeDeclNode: x.check(c) case *ConstDeclNode: x.check(c) case *VarDeclNode: x.check(c) case *FunctionDeclNode: x.check(c) case *MethodDeclNode: x.check(c) default: panic(todo("%v: %T %s", x.Position(), x, x.Source(false))) } } } func (n *MethodDeclNode) check(c *ctx) { if n == nil { return } n.Receiver.check(c) n.Signature.check(c) } func (n *FunctionDeclNode) check(c *ctx) { if n == nil { return } if c.isBuiltin() { switch nm := n.FunctionName.IDENT.Src(); nm { case "append", "cap", "close", "complex", "copy", "delete", "imag", "len", "make", "new", "panic", "print", "println", "real", "recover", // Go 1.21 "max", "min", "clear": n.Signature.t = c.newPredeclaredType(n, Function) default: panic(todo("%v: %q %s", n.Position(), nm, n.Source(false))) } return } n.Signature.check(c) if n.TypeParameters != nil { panic(todo("%v: %T %s", n.Position(), n, n.Source(false))) } } func (n *SignatureNode) check(c *ctx) Type { if n == nil { return Invalid } if !n.enter(c, n) { return n.Type() } in := n.Parameters.check(c) out := n.Result.check(c) return n.setType(newTupleType(n.Parameters, []Type{in, out})) } func (n *ResultNode) check(c *ctx) Type { if n == nil { return Invalid } switch { case n.Parameters != nil: return n.Parameters.check(c) case n.TypeNode != nil: return n.TypeNode.check(c) default: panic(todo("%v: %T %s", n.Position(), n, n.Source(false))) } } func (n *ParametersNode) check(c *ctx) Type { if n == nil { return Invalid } r := newTupleType(n, nil) for l := n.ParameterDeclList; l != nil; l = l.List { r.Types = append(r.Types, l.ParameterDecl.check(c)...) } return r } func (n *ParameterDeclNode) check(c *ctx) (r []Type) { if n == nil { return nil } t := n.TypeNode.check(c) for l := n.IdentifierList; l != nil; l = l.List { r = append(r, t) } return r } func (n *VarDeclNode) check(c *ctx) { if n == nil { return } switch x := n.VarSpec.(type) { case *VarSpecNode: x.check(c) default: panic(todo("%v: %T %s", n.Position(), x, n.Source(false))) } } func (n *VarSpecNode) check(c *ctx) { if n == nil { return } if c.isBuiltin() { switch nm := n.IDENT.Src(); nm { case "nil": n.TypeNode = c.newPredeclaredType(n, UntypedNil) default: panic(todo("%v: %q", n.IDENT.Position(), nm)) } return } if n.TypeNode != nil { c.err(n, "TODO %v", n.TypeNode.Source(false)) } var e []Expression for l := n.ExpressionList; l != nil; l = l.List { e = append(e, l.Expression.checkExpr(c)) } switch len(e) { default: panic(todo("", len(e))) c.err(n, "TODO %v", len(e)) } } func (n *ConstDeclNode) check(c *ctx) { if n == nil { return } switch x := n.ConstSpec.(type) { case *ConstSpecListNode: var prev Node for l := x; l != nil; l = l.List { switch y := l.ConstSpec.(type) { case *ConstSpecNode: y.check(c, prev) if y.Expression != nil || y.TypeNode != nil { prev = y } default: panic(todo("%v: %T %s", n.Position(), y, n.Source(false))) } } case *ConstSpecNode: x.check(c, nil) default: panic(todo("%v: %T %s", n.Position(), x, n.Source(false))) } } func (n *ConstSpecNode) check(c *ctx, prev Node) { if n == nil { return } if !n.enter(c, n) { if n.guard == guardChecking { panic(todo("")) // report recursive } return } defer func() { n.guard = guardChecked }() if c.isBuiltin() { switch n.IDENT.Src() { case "true": switch x := n.Expression.(type) { case *BinaryExpressionNode: x.setValue(trueVal) x.setType(c.newPredeclaredType(x, UntypedBool)) default: panic(todo("%v: %T %s", n.Position(), x, n.Source(false))) } case "false": switch x := n.Expression.(type) { case *BinaryExpressionNode: x.setValue(falseVal) x.setType(c.newPredeclaredType(x, UntypedBool)) default: panic(todo("%v: %T %s", n.Position(), x, n.Source(false))) } case "iota": switch x := n.Expression.(type) { case *BasicLitNode: // ok default: panic(todo("%v: %T %s", n.Position(), x, n.Source(false))) } default: panic(todo("", n.Position(), n.Source(false))) } return } save := c.iota c.iota = n.iota defer func() { c.iota = save }() switch { case n.Expression != nil: n.Expression = n.Expression.checkExpr(c) if n.TypeNode == nil { n.TypeNode = n.Expression.Type() return } t := n.TypeNode.check(c) trc("", t) panic(todo("%v: %T %s", n.Position(), n, n.Source(false))) default: // var e Expression // var pe *Expression // switch { // case n.Expression != nil: // e = n.Expression // pe = &n.Expression // default: // switch x := prev.(type) { // case *ConstSpecNode: // e = x.Expression.clone() // pe = &e // default: // panic(todo("%v: %T %s", n.Position(), x, n.Source(false))) // } // } // ev, et := e.checkExpr(c, pe) // e = *pe // if ev.Kind() == constant.Unknown { // c.err(e, "%s is not a constant", e.Source(false)) // n.t = Invalid // n.setValue(unknown) // return Invalid // } // switch { // case n.t == nil: // n.t = et // default: // c.err(n.Expression, "cannot assign %v (type %v) to type %v", ev, et, n.Type()) // return Invalid // } else { // n.setValue(convertValue(c, e, ev, n.Type())) // } // } // return n.Type() panic(todo("%v: %T %s", n.Position(), n, n.Source(false))) } } func (n *TypeDeclNode) check(c *ctx) { if n == nil { return } for l := n.TypeSpecList; l != nil; l = l.List { switch x := l.TypeSpec.(type) { case *TypeDefNode: switch { case c.isBuiltin(): x.pkg = c.pkg switch nm := x.IDENT.Src(); nm { case "bool": x.TypeNode = c.newPredeclaredType(x, Bool) case "int": x.TypeNode = c.newPredeclaredType(x, Int) c.cfg.int = x.TypeNode case "int8": x.TypeNode = c.newPredeclaredType(x, Int8) case "int16": x.TypeNode = c.newPredeclaredType(x, Int16) case "int32": x.TypeNode = c.newPredeclaredType(x, Int32) case "int64": x.TypeNode = c.newPredeclaredType(x, Int64) case "uint": x.TypeNode = c.newPredeclaredType(x, Uint) c.cfg.uint = x.TypeNode case "uint8": x.TypeNode = c.newPredeclaredType(x, Uint8) case "uint16": x.TypeNode = c.newPredeclaredType(x, Uint16) case "uint32": x.TypeNode = c.newPredeclaredType(x, Uint32) case "uint64": x.TypeNode = c.newPredeclaredType(x, Uint64) case "uintptr": x.TypeNode = c.newPredeclaredType(x, Uintptr) case "string": x.TypeNode = c.newPredeclaredType(x, String) case "float32": x.TypeNode = c.newPredeclaredType(x, Float32) case "float64": x.TypeNode = c.newPredeclaredType(x, Float64) case "complex64": x.TypeNode = c.newPredeclaredType(x, Complex64) case "complex128": x.TypeNode = c.newPredeclaredType(x, Complex128) case "comparable": x.TypeNode = c.newPredeclaredType(x, Interface) case "error": x.check(c) default: if token.IsExported(nm) { delete(c.pkg.Scope.nodes, nm) return } panic(todo("%v: %T %s", x.Position(), x, x.Source(false))) } case c.isUnsafe(): switch nm := x.IDENT.Src(); nm { case "ArbitraryType", "IntegerType", "Pointer": x.TypeNode.check(c) default: panic(todo("%v: %T %s", x.Position(), x, x.Source(false))) } default: switch { case x.TypeParameters != nil: panic(todo("%v: %T %s", x.Position(), x, x.Source(false))) default: x.check(c) } } case *AliasDeclNode: x.check(c) default: panic(todo("%v: %T %s", x.Position(), x, x.Source(false))) } } } func (n *AliasDeclNode) check(c *ctx) { if n == nil { return } n.TypeNode.check(c) } func (n *ImportDeclNode) check(c *ctx) { if n == nil { return } type result struct { spec *ImportSpecNode pkg *Package err error } var a []*result var wg sync.WaitGroup for l := n.ImportSpecList; l != nil; l = l.List { r := &result{} a = append(a, r) wg.Add(1) go func(isln *ImportSpecListNode, r *result) { defer wg.Done() r.spec = isln.ImportSpec r.pkg, r.err = r.spec.check(c) r.spec.pkg = r.pkg }(l, r) } wg.Wait() fileScope := c.ast.FileScope pkgScope := c.pkg.Scope for _, v := range a { switch x := v.err.(type) { case nil: // ok default: panic(todo("%v: %T: %s", v.spec.Position(), x, x)) } if c.pkg.ImportPath == "builtin" && v.spec.ImportPath.Src() == `"cmp"` { continue } switch ex := fileScope.declare(v.pkg.Name, v.spec, 0, nil, true); { case ex.declTok.IsValid(): c.err(n, "%s redeclared, previous declaration at %v:", v.pkg.Name.Src(), ex.declTok.Position()) continue } switch ex := pkgScope.declare(v.pkg.Name, v.spec, 0, nil, true); { case ex.declTok.IsValid(): c.err(n, "%s redeclared, previous declaration at %v:", v.pkg.Name.Src(), ex.declTok.Position()) continue } } } func (n *ImportSpecNode) check(c *ctx) (*Package, error) { if n == nil { return nil, nil } switch { case n.PERIOD.IsValid(): panic(todo("", n.Position(), n.Source(false))) case n.PackageName.IsValid(): //TODO version check := c.pkg.typeCheck switch check { case TypeCheckAll: // nop default: panic(todo("", check)) } return c.cfg.newPackage(c.pkg.FSPath, constant.StringVal(n.ImportPath.Value()), "", nil, false, check, c.pkg.guard) default: //TODO version check := c.pkg.typeCheck switch check { case TypeCheckAll: // nop default: if c.pkg.ImportPath == "builtin" && n.ImportPath.Src() == `"cmp"` { return nil, nil } } return c.cfg.newPackage(c.pkg.FSPath, constant.StringVal(n.ImportPath.Value()), "", nil, false, check, c.pkg.guard) } } func (n *PackageClauseNode) check(c *ctx) { if n == nil { return } nm := n.PackageName.Src() if ex := c.pkg.Name; ex.IsValid() && ex.Src() != nm { c.err(n.PackageName, "found different packages %q and %q", ex.Src(), nm) return } c.pkg.Name = n.PackageName }