]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: directly set some simple expression types
authorMatthew Dempsky <mdempsky@google.com>
Wed, 20 Jan 2021 20:54:23 +0000 (12:54 -0800)
committerMatthew Dempsky <mdempsky@google.com>
Thu, 21 Jan 2021 01:25:11 +0000 (01:25 +0000)
This CL updates irgen to directly set the type for a bunch of basic
expressions that are easy to handle already. Trickier rewrites are
still handled with typecheck.Expr, but responsibility of calling that
is pushed down to the conversion of individual operations.

Change-Id: I774ac6ab4c72ad854860ab5c741867dd42a066b3
Reviewed-on: https://go-review.googlesource.com/c/go/+/285058
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>

src/cmd/compile/internal/noder/decl.go
src/cmd/compile/internal/noder/expr.go
src/cmd/compile/internal/noder/helpers.go
src/cmd/compile/internal/noder/irgen.go
src/cmd/compile/internal/noder/types.go

index ce5bad88f398188e3498f40b1f9a2c55043af1e7..4d20f410bc832f6c713c6569d3c189bd96b88c23 100644 (file)
@@ -100,6 +100,9 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
                return
        }
 
+       // Prevent size calculations until we set the underlying type.
+       types.DeferCheckSize()
+
        name, obj := g.def(decl.Name)
        ntyp, otyp := name.Type(), obj.Type()
        if ir.CurFunc != nil {
@@ -135,6 +138,7 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
        // [mdempsky: Subtleties like these are why I always vehemently
        // object to new type pragmas.]
        ntyp.SetUnderlying(g.typeExpr(decl.Type))
+       types.ResumeCheckSize()
 
        if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 {
                methods := make([]*types.Field, otyp.NumMethods())
index d5177ead06fefce1c332422425f4b3745b37794a..be592003e18b55870797209f0f4462d7141b8a8c 100644 (file)
@@ -43,15 +43,36 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node {
                base.FatalfAt(g.pos(expr), "unrecognized type-checker result")
        }
 
+       // The gc backend expects all expressions to have a concrete type, and
+       // types2 mostly satisfies this expectation already. But there are a few
+       // cases where the Go spec doesn't require converting to concrete type,
+       // and so types2 leaves them untyped. So we need to fix those up here.
+       typ := tv.Type
+       if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 {
+               switch basic.Kind() {
+               case types2.UntypedNil:
+                       // ok; can appear in type switch case clauses
+                       // TODO(mdempsky): Handle as part of type switches instead?
+               case types2.UntypedBool:
+                       typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition
+               case types2.UntypedString:
+                       typ = types2.Typ[types2.String] // argument to "append" or "copy" calls
+               default:
+                       base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", basic)
+               }
+       }
+
        // Constant expression.
        if tv.Value != nil {
-               return Const(g.pos(expr), g.typ(tv.Type), tv.Value)
+               return Const(g.pos(expr), g.typ(typ), tv.Value)
        }
 
-       // TODO(mdempsky): Remove dependency on typecheck.Expr.
-       n := typecheck.Expr(g.expr0(tv.Type, expr))
-       if !g.match(n.Type(), tv.Type, tv.HasOk()) {
-               base.FatalfAt(g.pos(expr), "expected %L to have type %v", n, tv.Type)
+       n := g.expr0(typ, expr)
+       if n.Typecheck() != 1 {
+               base.FatalfAt(g.pos(expr), "missed typecheck: %+v", n)
+       }
+       if !g.match(n.Type(), typ, tv.HasOk()) {
+               base.FatalfAt(g.pos(expr), "expected %L to have type %v", n, typ)
        }
        return n
 }
@@ -64,7 +85,8 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
                if _, isNil := g.info.Uses[expr].(*types2.Nil); isNil {
                        return Nil(pos, g.typ(typ))
                }
-               return g.use(expr)
+               // TODO(mdempsky): Remove dependency on typecheck.Expr.
+               return typecheck.Expr(g.use(expr))
 
        case *syntax.CompositeLit:
                return g.compLit(typ, expr)
@@ -83,13 +105,14 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
                // Qualified identifier.
                if name, ok := expr.X.(*syntax.Name); ok {
                        if _, ok := g.info.Uses[name].(*types2.PkgName); ok {
-                               return g.use(expr.Sel)
+                               // TODO(mdempsky): Remove dependency on typecheck.Expr.
+                               return typecheck.Expr(g.use(expr.Sel))
                        }
                }
 
                // TODO(mdempsky/danscales): Use g.info.Selections[expr]
                // to resolve field/method selection. See CL 280633.
-               return ir.NewSelectorExpr(pos, ir.OXDOT, g.expr(expr.X), g.name(expr.Sel))
+               return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, g.expr(expr.X), g.name(expr.Sel)))
        case *syntax.SliceExpr:
                return Slice(pos, g.expr(expr.X), g.expr(expr.Index[0]), g.expr(expr.Index[1]), g.expr(expr.Index[2]))
 
@@ -131,16 +154,9 @@ func (g *irgen) exprs(exprs []syntax.Expr) []ir.Node {
 
 func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node {
        if ptr, ok := typ.Underlying().(*types2.Pointer); ok {
-               if _, isNamed := typ.(*types2.Named); isNamed {
-                       // TODO(mdempsky): Questionable, but this is
-                       // currently allowed by cmd/compile, go/types,
-                       // and gccgo:
-                       //
-                       //      type T *struct{}
-                       //      var _ = []T{{}}
-                       base.FatalfAt(g.pos(lit), "defined-pointer composite literal")
-               }
-               return ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit))
+               n := ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit))
+               n.SetOp(ir.OPTRLIT)
+               return typed(g.typ(typ), n)
        }
 
        _, isStruct := typ.Underlying().(*types2.Struct)
@@ -159,7 +175,8 @@ func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node {
                }
        }
 
-       return ir.NewCompLitExpr(g.pos(lit), ir.OCOMPLIT, ir.TypeNode(g.typ(typ)), exprs)
+       // TODO(mdempsky): Remove dependency on typecheck.Expr.
+       return typecheck.Expr(ir.NewCompLitExpr(g.pos(lit), ir.OCOMPLIT, ir.TypeNode(g.typ(typ)), exprs))
 }
 
 func (g *irgen) funcLit(typ types2.Type, expr *syntax.FuncLit) ir.Node {
index 2139f16a6c9f4013c7d4da6bc19e3575f76d7fa6..3c20f74d8bd42e7d7c6e287e55a3c41e9213b2e7 100644 (file)
@@ -21,32 +21,41 @@ import (
 // results, rather than leaving the caller responsible for using
 // typecheck.Expr or typecheck.Stmt.
 
+// typed returns n after setting its type to typ.
+func typed(typ *types.Type, n ir.Node) ir.Node {
+       n.SetType(typ)
+       n.SetTypecheck(1)
+       return n
+}
+
 // Values
 
 func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node {
-       n := ir.NewBasicLit(pos, val)
-       n.SetType(typ)
-       return n
+       return typed(typ, ir.NewBasicLit(pos, val))
 }
 
 func Nil(pos src.XPos, typ *types.Type) ir.Node {
-       n := ir.NewNilExpr(pos)
-       n.SetType(typ)
-       return n
+       return typed(typ, ir.NewNilExpr(pos))
 }
 
 // Expressions
 
 func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node {
-       return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, ir.TypeNode(typ)))
+       return typed(typ, ir.NewTypeAssertExpr(pos, x, nil))
 }
 
 func Binary(pos src.XPos, op ir.Op, x, y ir.Node) ir.Node {
        switch op {
        case ir.OANDAND, ir.OOROR:
-               return ir.NewLogicalExpr(pos, op, x, y)
+               return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y))
+       case ir.OADD:
+               if x.Type().IsString() {
+                       // TODO(mdempsky): Construct OADDSTR directly.
+                       return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
+               }
+               fallthrough
        default:
-               return ir.NewBinaryExpr(pos, op, x, y)
+               return typed(x.Type(), ir.NewBinaryExpr(pos, op, x, y))
        }
 }
 
@@ -92,13 +101,17 @@ func Call(pos src.XPos, fun ir.Node, args []ir.Node, dots bool) ir.Node {
 }
 
 func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node {
-       n := typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
-       n.SetType(typ)
-       return n
+       n := ir.NewBinaryExpr(pos, op, x, y)
+       if !types.Identical(x.Type(), y.Type()) {
+               // TODO(mdempsky): Handle subtleties of constructing mixed-typed comparisons.
+               n = typecheck.Expr(n).(*ir.BinaryExpr)
+       }
+       return typed(typ, n)
 }
 
 func Index(pos src.XPos, x, index ir.Node) ir.Node {
-       return ir.NewIndexExpr(pos, x, index)
+       // TODO(mdempsky): Avoid typecheck.Expr.
+       return typecheck.Expr(ir.NewIndexExpr(pos, x, index))
 }
 
 func Slice(pos src.XPos, x, low, high, max ir.Node) ir.Node {
@@ -106,17 +119,22 @@ func Slice(pos src.XPos, x, low, high, max ir.Node) ir.Node {
        if max != nil {
                op = ir.OSLICE3
        }
-       return ir.NewSliceExpr(pos, op, x, low, high, max)
+       // TODO(mdempsky): Avoid typecheck.Expr.
+       return typecheck.Expr(ir.NewSliceExpr(pos, op, x, low, high, max))
 }
 
 func Unary(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
+       typ := x.Type()
        switch op {
        case ir.OADDR:
-               return typecheck.NodAddrAt(pos, x)
+               // TODO(mdempsky): Avoid typecheck.Expr. Probably just need to set OPTRLIT as needed.
+               return typed(types.NewPtr(typ), typecheck.Expr(typecheck.NodAddrAt(pos, x)))
        case ir.ODEREF:
-               return ir.NewStarExpr(pos, x)
+               return typed(typ.Elem(), ir.NewStarExpr(pos, x))
+       case ir.ORECV:
+               return typed(typ.Elem(), ir.NewUnaryExpr(pos, op, x))
        default:
-               return ir.NewUnaryExpr(pos, op, x)
+               return typed(typ, ir.NewUnaryExpr(pos, op, x))
        }
 }
 
index e1273484821d2d6e25fa440bbbc88c000facf084..5c779ab810ab8353889bdbf52133f9a38339836f 100644 (file)
@@ -103,6 +103,10 @@ func (g *irgen) generate(noders []*noder) {
        types.LocalPkg.Name = g.self.Name()
        typecheck.TypecheckAllowed = true
 
+       // Prevent size calculations until we set the underlying type
+       // for all package-block defined types.
+       types.DeferCheckSize()
+
        // At this point, types2 has already handled name resolution and
        // type checking. We just need to map from its object and type
        // representations to those currently used by the rest of the
@@ -152,6 +156,7 @@ Outer:
                        }
                }
        }
+       types.ResumeCheckSize()
 
        // 3. Process all remaining declarations.
        for _, declList := range declLists {
index aec1846619d72c3f5cb1480e6df4b6d81aef4010..e0ed5a3f994e026f292644736402986201b3ed39 100644 (file)
@@ -35,6 +35,13 @@ func (g *irgen) typ(typ types2.Type) *types.Type {
        if !ok {
                res = g.typ0(typ)
                g.typs[typ] = res
+
+               // Ensure we calculate the size for all concrete types seen by
+               // the frontend. This is another heavy hammer for something that
+               // should really be the backend's responsibility instead.
+               if !res.IsUntyped() {
+                       types.CheckSize(res)
+               }
        }
        return res
 }