Skip to content

Commit

Permalink
Merge pull request #391 from visualfc/typecase
Browse files Browse the repository at this point in the history
typeCase/typeDefaultThen
  • Loading branch information
xushiwei authored Feb 20, 2024
2 parents 75004b2 + 7c28522 commit 41fa11b
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 47 deletions.
11 changes: 8 additions & 3 deletions codebuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -2497,17 +2497,22 @@ func (p *CodeBuilder) TypeAssertThen() *CodeBuilder {
}

// TypeCase starts case body of a type switch statement.
func (p *CodeBuilder) TypeCase(n int, src ...ast.Node) *CodeBuilder { // n=0 means default case
func (p *CodeBuilder) TypeCase(src ...ast.Node) *CodeBuilder {
if debugInstr {
log.Println("TypeCase", n)
log.Println("TypeCase")
}
if flow, ok := p.current.codeBlock.(*typeSwitchStmt); ok {
flow.TypeCase(p, n, src...)
flow.TypeCase(p, src...)
return p
}
panic("use switch x.(type) .. case please")
}

// TypeDefaultThen starts default clause of a type switch statement.
func (p *CodeBuilder) TypeDefaultThen(src ...ast.Node) *CodeBuilder {
return p.TypeCase(src...).Then(src...)
}

// Select starts a select statement.
func (p *CodeBuilder) Select(src ...ast.Node) *CodeBuilder {
if debugInstr {
Expand Down
4 changes: 2 additions & 2 deletions error_msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func TestErrTypeSwitch(t *testing.T) {
v := pkg.NewParam(token.NoPos, "v", tyInterf)
pkg.NewFunc(nil, "foo", types.NewTuple(v), nil, false).BodyStart(pkg).
/**/ TypeSwitch("t").Val(v, source("v", 1, 5)).TypeAssertThen().
/**/ Typ(types.Typ[types.Int], source("int", 2, 9)).TypeCase(1).
/**/ TypeCase().Typ(types.Typ[types.Int], source("int", 2, 9)).Then().
/**/ End().
End()
})
Expand All @@ -211,7 +211,7 @@ func TestErrTypeSwitch(t *testing.T) {
v := pkg.NewParam(token.NoPos, "v", gox.TyEmptyInterface)
pkg.NewFunc(nil, "foo", types.NewTuple(v), nil, false).BodyStart(pkg).
/**/ TypeSwitch("t").Val(v).TypeAssertThen().
/**/ Val(1, source("1", 2, 9)).TypeCase(1).
/**/ TypeCase().Val(1, source("1", 2, 9)).Then().
/**/ End().
End()
})
Expand Down
10 changes: 5 additions & 5 deletions package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -915,13 +915,13 @@ func TestTypeSwitch(t *testing.T) {
newFuncDecl(pkg, "bar", types.NewTuple(p), nil)
newFuncDecl(pkg, "foo", types.NewTuple(v), nil).BodyStart(pkg).
/**/ TypeSwitch("t").Val(v).TypeAssertThen().
/****/ Typ(types.Typ[types.Int]).Typ(types.Typ[types.String]).TypeCase(2).
/****/ TypeCase().Typ(types.Typ[types.Int]).Typ(types.Typ[types.String]).Then().
/******/ Val(ctxRef(pkg, "bar")).VarRef(ctxRef(pkg, "t")).UnaryOp(token.AND).Call(1).EndStmt().
/****/ End().
/**/ Typ(types.Typ[types.Bool]).TypeCase(1).
/**/ TypeCase().Typ(types.Typ[types.Bool]).Then().
/******/ NewVarStart(types.Typ[types.Bool], "x").Val(ctxRef(pkg, "t")).EndInit(1).
/****/ End().
/****/ TypeCase(0).
/****/ TypeDefaultThen().
/******/ Val(ctxRef(pkg, "bar")).VarRef(ctxRef(pkg, "t")).UnaryOp(token.AND).Call(1).EndStmt().
/****/ End().
/**/ End().
Expand Down Expand Up @@ -951,7 +951,7 @@ func TestTypeSwitch2(t *testing.T) {
pkg.NewFunc(nil, "bar", types.NewTuple(p), nil, false).BodyStart(pkg).End()
pkg.NewFunc(nil, "foo", types.NewTuple(v), nil, false).BodyStart(pkg).
/**/ TypeSwitch("").Val(ctxRef(pkg, "bar")).Val(nil).Call(1).EndStmt().Val(v).TypeAssertThen().
/****/ Typ(types.Typ[types.Int]).TypeCase(1).
/****/ TypeCase().Typ(types.Typ[types.Int]).Then().
/******/ Val(ctxRef(pkg, "bar")).VarRef(ctxRef(pkg, "v")).UnaryOp(token.AND).Call(1).EndStmt().
/****/ End().
/**/ End().
Expand Down Expand Up @@ -1261,7 +1261,7 @@ func main() {
pkg.CB().RangeAssignThen(0)
})
safeRun(t, func() {
pkg.CB().TypeCase(1)
pkg.CB().TypeCase()
})
safeRun(t, func() {
pkg.CB().CommCase()
Expand Down
82 changes: 45 additions & 37 deletions stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,17 @@ func (p *commCase) End(cb *CodeBuilder, src ast.Node) {
// ----------------------------------------------------------------------------
//
// typeSwitch(name) init; expr typeAssertThen
// type1, type2, ... typeN typeCase(N)
// typeCase type1, type2, ... typeN then
//
// ...
// end
//
// type1, type2, ... typeM typeCase(M)
// typeCase type1, type2, ... typeM then
//
// ...
// end
//
// typeDefaultThen
//
// ...
// end
Expand Down Expand Up @@ -313,42 +318,9 @@ func (p *typeSwitchStmt) TypeAssertThen(cb *CodeBuilder) {
p.x, p.xSrc, p.xType = x.Val, x.Src, xType
}

func (p *typeSwitchStmt) TypeCase(cb *CodeBuilder, n int, src ...ast.Node) {
var list []ast.Expr
var typ types.Type
if n > 0 {
list = make([]ast.Expr, n)
args := cb.stk.GetArgs(n)
for i, arg := range args {
typ = arg.Type
if tt, ok := typ.(*TypeType); ok {
typ = tt.Type()
if missing := cb.missingMethod(typ, p.xType); missing != "" {
xsrc, _ := cb.loadExpr(p.xSrc)
pos := getSrcPos(arg.Src)
cb.panicCodeErrorf(
pos, "impossible type switch case: %s (type %v) cannot have dynamic type %v (missing %s method)",
xsrc, p.xType, typ, missing)
}
} else if typ != types.Typ[types.UntypedNil] {
src, pos := cb.loadExpr(arg.Src)
cb.panicCodeErrorf(pos, "%s (type %v) is not a type", src, typ)
}
list[i] = arg.Val
}
cb.stk.PopN(n)
}

stmt := &typeCaseStmt{list: list}
func (p *typeSwitchStmt) TypeCase(cb *CodeBuilder, src ...ast.Node) {
stmt := &typeCaseStmt{pss: p}
cb.startBlockStmt(stmt, src, "type case statement", &stmt.old)

if p.name != "" {
if n != 1 { // default, or case with multi expr
typ = p.xType
}
name := types.NewParam(token.NoPos, cb.pkg.Types, p.name, typ)
cb.current.scope.Insert(name)
}
}

func (p *typeSwitchStmt) End(cb *CodeBuilder, src ast.Node) {
Expand All @@ -371,10 +343,46 @@ func (p *typeSwitchStmt) End(cb *CodeBuilder, src ast.Node) {
}

type typeCaseStmt struct {
pss *typeSwitchStmt
list []ast.Expr
old codeBlockCtx
}

func (p *typeCaseStmt) Then(cb *CodeBuilder, src ...ast.Node) {
var typ types.Type
pss := p.pss
n := cb.stk.Len()
if n > 0 {
p.list = make([]ast.Expr, n)
args := cb.stk.GetArgs(n)
for i, arg := range args {
typ = arg.Type
if tt, ok := typ.(*TypeType); ok {
typ = tt.Type()
if missing := cb.missingMethod(typ, pss.xType); missing != "" {
xsrc, _ := cb.loadExpr(pss.xSrc)
pos := getSrcPos(arg.Src)
cb.panicCodeErrorf(
pos, "impossible type switch case: %s (type %v) cannot have dynamic type %v (missing %s method)",
xsrc, pss.xType, typ, missing)
}
} else if typ != types.Typ[types.UntypedNil] {
src, pos := cb.loadExpr(arg.Src)
cb.panicCodeErrorf(pos, "%s (type %v) is not a type", src, typ)
}
p.list[i] = arg.Val
}
cb.stk.PopN(n)
}
if pss.name != "" {
if n != 1 { // default, or case with multi expr
typ = pss.xType
}
name := types.NewParam(token.NoPos, cb.pkg.Types, pss.name, typ)
cb.current.scope.Insert(name)
}
}

func (p *typeCaseStmt) End(cb *CodeBuilder, src ast.Node) {
body, flows := cb.endBlockStmt(&p.old)
cb.current.flows |= flows
Expand Down

0 comments on commit 41fa11b

Please sign in to comment.