1
0
mirror of https://github.com/golang/go synced 2024-09-24 05:20:13 -06:00

[dev.typeparams] cmd/compile: directly set some simple expression types

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>
This commit is contained in:
Matthew Dempsky 2021-01-20 12:54:23 -08:00
parent 455c29af83
commit 2427f6e6c0
5 changed files with 90 additions and 39 deletions

View File

@ -100,6 +100,9 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
return return
} }
// Prevent size calculations until we set the underlying type.
types.DeferCheckSize()
name, obj := g.def(decl.Name) name, obj := g.def(decl.Name)
ntyp, otyp := name.Type(), obj.Type() ntyp, otyp := name.Type(), obj.Type()
if ir.CurFunc != nil { 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 // [mdempsky: Subtleties like these are why I always vehemently
// object to new type pragmas.] // object to new type pragmas.]
ntyp.SetUnderlying(g.typeExpr(decl.Type)) ntyp.SetUnderlying(g.typeExpr(decl.Type))
types.ResumeCheckSize()
if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 { if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 {
methods := make([]*types.Field, otyp.NumMethods()) methods := make([]*types.Field, otyp.NumMethods())

View File

@ -43,15 +43,36 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node {
base.FatalfAt(g.pos(expr), "unrecognized type-checker result") base.FatalfAt(g.pos(expr), "unrecognized type-checker result")
} }
// Constant expression. // The gc backend expects all expressions to have a concrete type, and
if tv.Value != nil { // types2 mostly satisfies this expectation already. But there are a few
return Const(g.pos(expr), g.typ(tv.Type), tv.Value) // 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)
}
} }
// TODO(mdempsky): Remove dependency on typecheck.Expr. // Constant expression.
n := typecheck.Expr(g.expr0(tv.Type, expr)) if tv.Value != nil {
if !g.match(n.Type(), tv.Type, tv.HasOk()) { return Const(g.pos(expr), g.typ(typ), tv.Value)
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 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 { if _, isNil := g.info.Uses[expr].(*types2.Nil); isNil {
return Nil(pos, g.typ(typ)) 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: case *syntax.CompositeLit:
return g.compLit(typ, expr) return g.compLit(typ, expr)
@ -83,13 +105,14 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
// Qualified identifier. // Qualified identifier.
if name, ok := expr.X.(*syntax.Name); ok { if name, ok := expr.X.(*syntax.Name); ok {
if _, ok := g.info.Uses[name].(*types2.PkgName); 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] // TODO(mdempsky/danscales): Use g.info.Selections[expr]
// to resolve field/method selection. See CL 280633. // 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: 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])) 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 { func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node {
if ptr, ok := typ.Underlying().(*types2.Pointer); ok { if ptr, ok := typ.Underlying().(*types2.Pointer); ok {
if _, isNamed := typ.(*types2.Named); isNamed { n := ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit))
// TODO(mdempsky): Questionable, but this is n.SetOp(ir.OPTRLIT)
// currently allowed by cmd/compile, go/types, return typed(g.typ(typ), n)
// 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))
} }
_, isStruct := typ.Underlying().(*types2.Struct) _, 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 { func (g *irgen) funcLit(typ types2.Type, expr *syntax.FuncLit) ir.Node {

View File

@ -21,32 +21,41 @@ import (
// results, rather than leaving the caller responsible for using // results, rather than leaving the caller responsible for using
// typecheck.Expr or typecheck.Stmt. // typecheck.Expr or typecheck.Stmt.
// Values // typed returns n after setting its type to typ.
func typed(typ *types.Type, n ir.Node) ir.Node {
func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node {
n := ir.NewBasicLit(pos, val)
n.SetType(typ) n.SetType(typ)
n.SetTypecheck(1)
return n return n
} }
// Values
func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node {
return typed(typ, ir.NewBasicLit(pos, val))
}
func Nil(pos src.XPos, typ *types.Type) ir.Node { func Nil(pos src.XPos, typ *types.Type) ir.Node {
n := ir.NewNilExpr(pos) return typed(typ, ir.NewNilExpr(pos))
n.SetType(typ)
return n
} }
// Expressions // Expressions
func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node { 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 { func Binary(pos src.XPos, op ir.Op, x, y ir.Node) ir.Node {
switch op { switch op {
case ir.OANDAND, ir.OOROR: 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: 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 { 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 := ir.NewBinaryExpr(pos, op, x, y)
n.SetType(typ) if !types.Identical(x.Type(), y.Type()) {
return n // 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 { 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 { 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 { if max != nil {
op = ir.OSLICE3 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 { func Unary(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
typ := x.Type()
switch op { switch op {
case ir.OADDR: 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: 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: default:
return ir.NewUnaryExpr(pos, op, x) return typed(typ, ir.NewUnaryExpr(pos, op, x))
} }
} }

View File

@ -103,6 +103,10 @@ func (g *irgen) generate(noders []*noder) {
types.LocalPkg.Name = g.self.Name() types.LocalPkg.Name = g.self.Name()
typecheck.TypecheckAllowed = true 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 // At this point, types2 has already handled name resolution and
// type checking. We just need to map from its object and type // type checking. We just need to map from its object and type
// representations to those currently used by the rest of the // representations to those currently used by the rest of the
@ -152,6 +156,7 @@ Outer:
} }
} }
} }
types.ResumeCheckSize()
// 3. Process all remaining declarations. // 3. Process all remaining declarations.
for _, declList := range declLists { for _, declList := range declLists {

View File

@ -35,6 +35,13 @@ func (g *irgen) typ(typ types2.Type) *types.Type {
if !ok { if !ok {
res = g.typ0(typ) res = g.typ0(typ)
g.typs[typ] = res 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 return res
} }