func BoolValue(expr ast.Expr, info *types.Info) (bool, bool) { v := info.Types[expr].Value if v != nil && v.Kind() == exact.Bool { return exact.BoolVal(v), true } switch e := expr.(type) { case *ast.BinaryExpr: switch e.Op { case token.LAND: if b, ok := BoolValue(e.X, info); ok { if !b { return false, true } return BoolValue(e.Y, info) } case token.LOR: if b, ok := BoolValue(e.X, info); ok { if b { return true, true } return BoolValue(e.Y, info) } } case *ast.UnaryExpr: if e.Op == token.NOT { if b, ok := BoolValue(e.X, info); ok { return !b, true } } case *ast.ParenExpr: return BoolValue(e.X, info) } return false, false }
func (p *exporter) value(x exact.Value) { if trace { p.tracef("value { ") defer p.tracef("} ") } switch kind := x.Kind(); kind { case exact.Bool: tag := falseTag if exact.BoolVal(x) { tag = trueTag } p.int(tag) case exact.Int: if i, ok := exact.Int64Val(x); ok { p.int(int64Tag) p.int64(i) return } p.int(floatTag) p.float(x) case exact.Float: p.int(fractionTag) p.fraction(x) case exact.Complex: p.int(complexTag) p.fraction(exact.Real(x)) p.fraction(exact.Imag(x)) case exact.String: p.int(stringTag) p.string(exact.StringVal(x)) default: panic(fmt.Sprintf("unexpected value kind %d", kind)) } }
// constValue returns the value of the constant with the // dynamic type tag appropriate for c.Type(). func constValue(c *ssa.Const) value { if c.IsNil() { return zero(c.Type()) // typed nil } if t, ok := c.Type().Underlying().(*types.Basic); ok { // TODO(adonovan): eliminate untyped constants from SSA form. switch t.Kind() { case types.Bool, types.UntypedBool: return exact.BoolVal(c.Value) case types.Int, types.UntypedInt: // Assume sizeof(int) is same on host and target. return int(c.Int64()) case types.Int8: return int8(c.Int64()) case types.Int16: return int16(c.Int64()) case types.Int32, types.UntypedRune: return int32(c.Int64()) case types.Int64: return c.Int64() case types.Uint: // Assume sizeof(uint) is same on host and target. return uint(c.Uint64()) case types.Uint8: return uint8(c.Uint64()) case types.Uint16: return uint16(c.Uint64()) case types.Uint32: return uint32(c.Uint64()) case types.Uint64: return c.Uint64() case types.Uintptr: // Assume sizeof(uintptr) is same on host and target. return uintptr(c.Uint64()) case types.Float32: return float32(c.Float64()) case types.Float64, types.UntypedFloat: return c.Float64() case types.Complex64: return complex64(c.Complex128()) case types.Complex128, types.UntypedComplex: return c.Complex128() case types.String, types.UntypedString: if c.Value.Kind() == exact.String { return exact.StringVal(c.Value) } return string(rune(c.Int64())) } } panic(fmt.Sprintf("constValue: %s", c)) }
func (r *Rule) evaluate(d float64) (bool, error) { pkg := types.NewPackage("evaluateme", "evaluateme") v := exact.MakeFloat64(d) pkg.Scope().Insert(types.NewConst(0, pkg, "x", types.Typ[types.Float64], v)) tv, err := types.Eval(token.NewFileSet(), pkg, token.NoPos, r.Condition) if err != nil { return false, fmt.Errorf("Failed to evaluate condition %q: %s", r.Condition, err) } if tv.Type.String() != "untyped bool" { return false, fmt.Errorf("Rule \"%v\" does not return boolean type.", r.Condition) } return exact.BoolVal(tv.Value), nil }
func (c *funcContext) translateExpr(expr ast.Expr) *expression { exprType := c.p.TypeOf(expr) if value := c.p.Types[expr].Value; value != nil { basic := exprType.Underlying().(*types.Basic) switch { case isBoolean(basic): return c.formatExpr("%s", strconv.FormatBool(exact.BoolVal(value))) case isInteger(basic): if is64Bit(basic) { if basic.Kind() == types.Int64 { d, ok := exact.Int64Val(value) if !ok { panic("could not get exact uint") } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatInt(d>>32, 10), strconv.FormatUint(uint64(d)&(1<<32-1), 10)) } d, ok := exact.Uint64Val(value) if !ok { panic("could not get exact uint") } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatUint(d>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } d, ok := exact.Int64Val(value) if !ok { panic("could not get exact int") } return c.formatExpr("%s", strconv.FormatInt(d, 10)) case isFloat(basic): f, _ := exact.Float64Val(value) return c.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64)) case isComplex(basic): r, _ := exact.Float64Val(exact.Real(value)) i, _ := exact.Float64Val(exact.Imag(value)) if basic.Kind() == types.UntypedComplex { exprType = types.Typ[types.Complex128] } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64)) case isString(basic): return c.formatExpr("%s", encodeString(exact.StringVal(value))) default: panic("Unhandled constant type: " + basic.String()) } } var obj types.Object switch e := expr.(type) { case *ast.SelectorExpr: obj = c.p.Uses[e.Sel] case *ast.Ident: obj = c.p.Defs[e] if obj == nil { obj = c.p.Uses[e] } } if obj != nil && typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Global": return c.formatExpr("$global") case "Module": return c.formatExpr("$module") case "Undefined": return c.formatExpr("undefined") } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { var elements []string i := 0 zero := c.translateExpr(c.zeroValue(elementType)).String() for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, ok := exact.Int64Val(c.p.Types[kve.Key].Value) if !ok { panic("could not get exact int") } i = int(key) element = kve.Value } for len(elements) <= i { elements = append(elements, zero) } elements[i] = c.translateImplicitConversionWithCloning(element, elementType).String() i++ } return elements } switch t := exprType.Underlying().(type) { case *types.Array: elements := collectIndexedElements(t.Elem()) if len(elements) == 0 { return c.formatExpr("%s.zero()", c.typeName(t)) } zero := c.translateExpr(c.zeroValue(t.Elem())).String() for len(elements) < int(t.Len()) { elements = append(elements, zero) } return c.formatExpr(`$toNativeArray(%s, [%s])`, typeKind(t.Elem()), strings.Join(elements, ", ")) case *types.Slice: return c.formatExpr("new %s([%s])", c.typeName(exprType), strings.Join(collectIndexedElements(t.Elem()), ", ")) case *types.Map: entries := make([]string, len(e.Elts)) for i, element := range e.Elts { kve := element.(*ast.KeyValueExpr) entries[i] = fmt.Sprintf("{ k: %s, v: %s }", c.translateImplicitConversionWithCloning(kve.Key, t.Key()), c.translateImplicitConversionWithCloning(kve.Value, t.Elem())) } return c.formatExpr("$makeMap(%s.keyFor, [%s])", c.typeName(t.Key()), strings.Join(entries, ", ")) case *types.Struct: elements := make([]string, t.NumFields()) isKeyValue := true if len(e.Elts) != 0 { _, isKeyValue = e.Elts[0].(*ast.KeyValueExpr) } if !isKeyValue { for i, element := range e.Elts { elements[i] = c.translateImplicitConversionWithCloning(element, t.Field(i).Type()).String() } } if isKeyValue { for i := range elements { elements[i] = c.translateExpr(c.zeroValue(t.Field(i).Type())).String() } for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) for j := range elements { if kve.Key.(*ast.Ident).Name == t.Field(j).Name() { elements[j] = c.translateImplicitConversionWithCloning(kve.Value, t.Field(j).Type()).String() break } } } } return c.formatExpr("new %s.ptr(%s)", c.typeName(exprType), strings.Join(elements, ", ")) default: panic(fmt.Sprintf("Unhandled CompositeLit type: %T\n", t)) } case *ast.FuncLit: _, fun := translateFunction(e.Type, nil, e.Body, c, exprType.(*types.Signature), c.p.FuncLitInfos[e], "") if len(c.p.escapingVars) != 0 { names := make([]string, 0, len(c.p.escapingVars)) for obj := range c.p.escapingVars { names = append(names, c.p.objectNames[obj]) } sort.Strings(names) list := strings.Join(names, ", ") return c.formatExpr("(function(%s) { return %s; })(%s)", list, fun, list) } return c.formatExpr("(%s)", fun) case *ast.UnaryExpr: t := c.p.TypeOf(e.X) switch e.Op { case token.AND: if typesutil.IsJsObject(exprType) { return c.formatExpr("%e.object", e.X) } switch t.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } switch x := astutil.RemoveParens(e.X).(type) { case *ast.CompositeLit: return c.formatExpr("$newDataPointer(%e, %s)", x, c.typeName(c.p.TypeOf(e))) case *ast.Ident: obj := c.p.Uses[x].(*types.Var) if c.p.escapingVars[obj] { return c.formatExpr("(%1s.$ptr || (%1s.$ptr = new %2s(function() { return this.$target[0]; }, function($v) { this.$target[0] = $v; }, %1s)))", c.p.objectNames[obj], c.typeName(exprType)) } return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) case *ast.SelectorExpr: sel, ok := c.p.Selections[x] if !ok { // qualified identifier obj := c.p.Uses[x.Sel].(*types.Var) return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) } newSel := &ast.SelectorExpr{X: c.newIdent("this.$target", c.p.TypeOf(x.X)), Sel: x.Sel} c.setType(newSel, exprType) c.p.Selections[newSel] = sel return c.formatExpr("(%1e.$ptr_%2s || (%1e.$ptr_%2s = new %3s(function() { return %4e; }, function($v) { %5s }, %1e)))", x.X, x.Sel.Name, c.typeName(exprType), newSel, c.translateAssign(newSel, c.newIdent("$v", exprType), false)) case *ast.IndexExpr: if _, ok := c.p.TypeOf(x.X).Underlying().(*types.Slice); ok { return c.formatExpr("$indexPtr(%1e.$array, %1e.$offset + %2e, %3s)", x.X, x.Index, c.typeName(exprType)) } return c.formatExpr("$indexPtr(%e, %e, %s)", x.X, x.Index, c.typeName(exprType)) case *ast.StarExpr: return c.translateExpr(x.X) default: panic(fmt.Sprintf("Unhandled: %T\n", x)) } case token.ARROW: call := &ast.CallExpr{ Fun: c.newIdent("$recv", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", t)), types.NewTuple(types.NewVar(0, nil, "", exprType), types.NewVar(0, nil, "", types.Typ[types.Bool])), false)), Args: []ast.Expr{e.X}, } c.Blocking[call] = true if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("%e", call) } return c.formatExpr("%e[0]", call) } basic := t.Underlying().(*types.Basic) switch e.Op { case token.ADD: return c.translateExpr(e.X) case token.SUB: switch { case is64Bit(basic): return c.formatExpr("new %1s(-%2h, -%2l)", c.typeName(t), e.X) case isComplex(basic): return c.formatExpr("new %1s(-%2r, -%2i)", c.typeName(t), e.X) case isUnsigned(basic): return c.fixNumber(c.formatExpr("-%e", e.X), basic) default: return c.formatExpr("-%e", e.X) } case token.XOR: if is64Bit(basic) { return c.formatExpr("new %1s(~%2h, ~%2l >>> 0)", c.typeName(t), e.X) } return c.fixNumber(c.formatExpr("~%e", e.X), basic) case token.NOT: return c.formatExpr("!%e", e.X) default: panic(e.Op) } case *ast.BinaryExpr: if e.Op == token.NEQ { return c.formatExpr("!(%s)", c.translateExpr(&ast.BinaryExpr{ X: e.X, Op: token.EQL, Y: e.Y, })) } t := c.p.TypeOf(e.X) t2 := c.p.TypeOf(e.Y) _, isInterface := t2.Underlying().(*types.Interface) if isInterface || types.Identical(t, types.Typ[types.UntypedNil]) { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && isNumeric(basic) { if is64Bit(basic) { switch e.Op { case token.MUL: return c.formatExpr("$mul64(%e, %e)", e.X, e.Y) case token.QUO: return c.formatExpr("$div64(%e, %e, false)", e.X, e.Y) case token.REM: return c.formatExpr("$div64(%e, %e, true)", e.X, e.Y) case token.SHL: return c.formatExpr("$shiftLeft64(%e, %f)", e.X, e.Y) case token.SHR: return c.formatExpr("$shiftRight%s(%e, %f)", toJavaScriptType(basic), e.X, e.Y) case token.EQL: return c.formatExpr("(%1h === %2h && %1l === %2l)", e.X, e.Y) case token.LSS: return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l < %2l))", e.X, e.Y) case token.LEQ: return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l <= %2l))", e.X, e.Y) case token.GTR: return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l > %2l))", e.X, e.Y) case token.GEQ: return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l >= %2l))", e.X, e.Y) case token.ADD, token.SUB: return c.formatExpr("new %3s(%1h %4t %2h, %1l %4t %2l)", e.X, e.Y, c.typeName(t), e.Op) case token.AND, token.OR, token.XOR: return c.formatExpr("new %3s(%1h %4t %2h, (%1l %4t %2l) >>> 0)", e.X, e.Y, c.typeName(t), e.Op) case token.AND_NOT: return c.formatExpr("new %3s(%1h & ~%2h, (%1l & ~%2l) >>> 0)", e.X, e.Y, c.typeName(t)) default: panic(e.Op) } } if isComplex(basic) { switch e.Op { case token.EQL: return c.formatExpr("(%1r === %2r && %1i === %2i)", e.X, e.Y) case token.ADD, token.SUB: return c.formatExpr("new %3s(%1r %4t %2r, %1i %4t %2i)", e.X, e.Y, c.typeName(t), e.Op) case token.MUL: return c.formatExpr("new %3s(%1r * %2r - %1i * %2i, %1r * %2i + %1i * %2r)", e.X, e.Y, c.typeName(t)) case token.QUO: return c.formatExpr("$divComplex(%e, %e)", e.X, e.Y) default: panic(e.Op) } } switch e.Op { case token.EQL: return c.formatParenExpr("%e === %e", e.X, e.Y) case token.LSS, token.LEQ, token.GTR, token.GEQ: return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.ADD, token.SUB: return c.fixNumber(c.formatExpr("%e %t %e", e.X, e.Op, e.Y), basic) case token.MUL: switch basic.Kind() { case types.Int32: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >> 0) + (%1e << 16 >>> 16) * %2e) >> 0", e.X, e.Y) case types.Uint32, types.Uintptr: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >>> 0) + (%1e << 16 >>> 16) * %2e) >>> 0", e.X, e.Y) } return c.fixNumber(c.formatExpr("%e * %e", e.X, e.Y), basic) case token.QUO: if isInteger(basic) { // cut off decimals shift := ">>" if isUnsigned(basic) { shift = ">>>" } return c.formatExpr(`(%1s = %2e / %3e, (%1s === %1s && %1s !== 1/0 && %1s !== -1/0) ? %1s %4s 0 : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_q"), e.X, e.Y, shift) } if basic.Kind() == types.Float32 { return c.fixNumber(c.formatExpr("%e / %e", e.X, e.Y), basic) } return c.formatExpr("%e / %e", e.X, e.Y) case token.REM: return c.formatExpr(`(%1s = %2e %% %3e, %1s === %1s ? %1s : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_r"), e.X, e.Y) case token.SHL, token.SHR: op := e.Op.String() if e.Op == token.SHR && isUnsigned(basic) { op = ">>>" } if c.p.Types[e.Y].Value != nil { return c.fixNumber(c.formatExpr("%e %s %e", e.X, op, e.Y), basic) } if e.Op == token.SHR && !isUnsigned(basic) { return c.fixNumber(c.formatParenExpr("%e >> $min(%e, 31)", e.X, e.Y), basic) } y := c.newVariable("y") return c.fixNumber(c.formatExpr("(%s = %s, %s < 32 ? (%e %s %s) : 0)", y, c.translateImplicitConversion(e.Y, types.Typ[types.Uint]), y, e.X, op, y), basic) case token.AND, token.OR: if isUnsigned(basic) { return c.formatParenExpr("(%e %t %e) >>> 0", e.X, e.Op, e.Y) } return c.formatParenExpr("%e %t %e", e.X, e.Op, e.Y) case token.AND_NOT: return c.fixNumber(c.formatParenExpr("%e & ~%e", e.X, e.Y), basic) case token.XOR: return c.fixNumber(c.formatParenExpr("%e ^ %e", e.X, e.Y), basic) default: panic(e.Op) } } switch e.Op { case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ: return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.LAND: if c.Blocking[e.Y] { skipCase := c.caseCounter c.caseCounter++ resultVar := c.newVariable("_v") c.Printf("if (!(%s)) { %s = false; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) return c.formatExpr("%s", resultVar) } return c.formatExpr("%e && %e", e.X, e.Y) case token.LOR: if c.Blocking[e.Y] { skipCase := c.caseCounter c.caseCounter++ resultVar := c.newVariable("_v") c.Printf("if (%s) { %s = true; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) return c.formatExpr("%s", resultVar) } return c.formatExpr("%e || %e", e.X, e.Y) case token.EQL: switch u := t.Underlying().(type) { case *types.Array, *types.Struct: return c.formatExpr("$equal(%e, %e, %s)", e.X, e.Y, c.typeName(t)) case *types.Interface: return c.formatExpr("$interfaceIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) case *types.Pointer: if _, ok := u.Elem().Underlying().(*types.Array); ok { return c.formatExpr("$equal(%s, %s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t), c.typeName(u.Elem())) } case *types.Basic: if isBoolean(u) { if b, ok := analysis.BoolValue(e.X, c.p.Info.Info); ok && b { return c.translateExpr(e.Y) } if b, ok := analysis.BoolValue(e.Y, c.p.Info.Info); ok && b { return c.translateExpr(e.X) } } } return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) default: panic(e.Op) } case *ast.ParenExpr: return c.formatParenExpr("%e", e.X) case *ast.IndexExpr: switch t := c.p.TypeOf(e.X).Underlying().(type) { case *types.Array, *types.Pointer: pattern := rangeCheck("%1e[%2f]", c.p.Types[e.Index].Value != nil, true) if _, ok := t.(*types.Pointer); ok { // check pointer for nix (attribute getter causes a panic) pattern = `(%1e.nilCheck, ` + pattern + `)` } return c.formatExpr(pattern, e.X, e.Index) case *types.Slice: return c.formatExpr(rangeCheck("%1e.$array[%1e.$offset + %2f]", c.p.Types[e.Index].Value != nil, false), e.X, e.Index) case *types.Map: if typesutil.IsJsObject(c.p.TypeOf(e.Index)) { c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: e.Index.Pos(), Msg: "cannot use js.Object as map key"}) } key := fmt.Sprintf("%s.keyFor(%s)", c.typeName(t.Key()), c.translateImplicitConversion(e.Index, t.Key())) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? [%1s.v, true] : [%4e, false])`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) } return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? %1s.v : %4e)`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) case *types.Basic: return c.formatExpr("%e.charCodeAt(%f)", e.X, e.Index) default: panic(fmt.Sprintf("Unhandled IndexExpr: %T\n", t)) } case *ast.SliceExpr: if b, isBasic := c.p.TypeOf(e.X).Underlying().(*types.Basic); isBasic && isString(b) { switch { case e.Low == nil && e.High == nil: return c.translateExpr(e.X) case e.Low == nil: return c.formatExpr("%e.substring(0, %f)", e.X, e.High) case e.High == nil: return c.formatExpr("%e.substring(%f)", e.X, e.Low) default: return c.formatExpr("%e.substring(%f, %f)", e.X, e.Low, e.High) } } slice := c.translateConversionToSlice(e.X, exprType) switch { case e.Low == nil && e.High == nil: return c.formatExpr("%s", slice) case e.Low == nil: if e.Max != nil { return c.formatExpr("$subslice(%s, 0, %f, %f)", slice, e.High, e.Max) } return c.formatExpr("$subslice(%s, 0, %f)", slice, e.High) case e.High == nil: return c.formatExpr("$subslice(%s, %f)", slice, e.Low) default: if e.Max != nil { return c.formatExpr("$subslice(%s, %f, %f, %f)", slice, e.Low, e.High, e.Max) } return c.formatExpr("$subslice(%s, %f, %f)", slice, e.Low, e.High) } case *ast.SelectorExpr: sel, ok := c.p.Selections[e] if !ok { // qualified identifier return c.formatExpr("%s", c.objectName(obj)) } switch sel.Kind() { case types.FieldVal: fields, jsTag := c.translateSelection(sel, e.Pos()) if jsTag != "" { if _, ok := sel.Type().(*types.Signature); ok { return c.formatExpr("$internalize(%1e.%2s.%3s, %4s, %1e.%2s)", e.X, strings.Join(fields, "."), jsTag, c.typeName(sel.Type())) } return c.internalize(c.formatExpr("%e.%s.%s", e.X, strings.Join(fields, "."), jsTag), sel.Type()) } return c.formatExpr("%e.%s", e.X, strings.Join(fields, ".")) case types.MethodVal: recv := c.makeReceiver(e.X, sel) return c.formatExpr(`$methodVal(%s, "%s")`, recv, sel.Obj().(*types.Func).Name()) case types.MethodExpr: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } return c.formatExpr(`$methodExpr(%s, "%s")`, c.typeName(sel.Recv()), sel.Obj().(*types.Func).Name()) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } case *ast.CallExpr: plainFun := astutil.RemoveParens(e.Fun) if astutil.IsTypeExpr(plainFun, c.p.Info.Info) { return c.formatExpr("%s", c.translateConversion(e.Args[0], c.p.TypeOf(plainFun))) } sig := c.p.TypeOf(plainFun).Underlying().(*types.Signature) switch f := plainFun.(type) { case *ast.Ident: obj := c.p.Uses[f] if o, ok := obj.(*types.Builtin); ok { return c.translateBuiltin(o.Name(), sig, e.Args, e.Ellipsis.IsValid()) } if typesutil.IsJsPackage(obj.Pkg()) && obj.Name() == "InternalObject" { return c.translateExpr(e.Args[0]) } return c.translateCall(e, sig, c.translateExpr(f)) case *ast.SelectorExpr: sel, ok := c.p.Selections[f] if !ok { // qualified identifier obj := c.p.Uses[f.Sel] if typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Debugger": return c.formatExpr("debugger") case "InternalObject": return c.translateExpr(e.Args[0]) case "MakeFunc": return c.formatExpr("(function() { return $externalize(%e(this, new ($sliceType($jsObjectPtr))($global.Array.prototype.slice.call(arguments, []))), $emptyInterface); })", e.Args[0]) } } return c.translateCall(e, sig, c.translateExpr(f)) } externalizeExpr := func(e ast.Expr) string { t := c.p.TypeOf(e) if types.Identical(t, types.Typ[types.UntypedNil]) { return "null" } return c.externalize(c.translateExpr(e).String(), t) } externalizeArgs := func(args []ast.Expr) string { s := make([]string, len(args)) for i, arg := range args { s[i] = externalizeExpr(arg) } return strings.Join(s, ", ") } switch sel.Kind() { case types.MethodVal: recv := c.makeReceiver(f.X, sel) if typesutil.IsJsPackage(sel.Obj().Pkg()) { globalRef := func(id string) string { if recv.String() == "$global" && id[0] == '$' { return id } return recv.String() + "." + id } switch sel.Obj().Name() { case "Get": if id, ok := c.identifierConstant(e.Args[0]); ok { return c.formatExpr("%s", globalRef(id)) } return c.formatExpr("%s[$externalize(%e, $String)]", recv, e.Args[0]) case "Set": if id, ok := c.identifierConstant(e.Args[0]); ok { return c.formatExpr("%s = %s", globalRef(id), externalizeExpr(e.Args[1])) } return c.formatExpr("%s[$externalize(%e, $String)] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Delete": return c.formatExpr("delete %s[$externalize(%e, $String)]", recv, e.Args[0]) case "Length": return c.formatExpr("$parseInt(%s.length)", recv) case "Index": return c.formatExpr("%s[%e]", recv, e.Args[0]) case "SetIndex": return c.formatExpr("%s[%e] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Call": if id, ok := c.identifierConstant(e.Args[0]); ok { if e.Ellipsis.IsValid() { objVar := c.newVariable("obj") return c.formatExpr("(%s = %s, %s.%s.apply(%s, %s))", objVar, recv, objVar, id, objVar, externalizeExpr(e.Args[1])) } return c.formatExpr("%s(%s)", globalRef(id), externalizeArgs(e.Args[1:])) } if e.Ellipsis.IsValid() { objVar := c.newVariable("obj") return c.formatExpr("(%s = %s, %s[$externalize(%e, $String)].apply(%s, %s))", objVar, recv, objVar, e.Args[0], objVar, externalizeExpr(e.Args[1])) } return c.formatExpr("%s[$externalize(%e, $String)](%s)", recv, e.Args[0], externalizeArgs(e.Args[1:])) case "Invoke": if e.Ellipsis.IsValid() { return c.formatExpr("%s.apply(undefined, %s)", recv, externalizeExpr(e.Args[0])) } return c.formatExpr("%s(%s)", recv, externalizeArgs(e.Args)) case "New": if e.Ellipsis.IsValid() { return c.formatExpr("new ($global.Function.prototype.bind.apply(%s, [undefined].concat(%s)))", recv, externalizeExpr(e.Args[0])) } return c.formatExpr("new (%s)(%s)", recv, externalizeArgs(e.Args)) case "Bool": return c.internalize(recv, types.Typ[types.Bool]) case "String": return c.internalize(recv, types.Typ[types.String]) case "Int": return c.internalize(recv, types.Typ[types.Int]) case "Int64": return c.internalize(recv, types.Typ[types.Int64]) case "Uint64": return c.internalize(recv, types.Typ[types.Uint64]) case "Float": return c.internalize(recv, types.Typ[types.Float64]) case "Interface": return c.internalize(recv, types.NewInterface(nil, nil)) case "Unsafe": return recv default: panic("Invalid js package object: " + sel.Obj().Name()) } } methodName := sel.Obj().Name() if reservedKeywords[methodName] { methodName += "$" } return c.translateCall(e, sig, c.formatExpr("%s.%s", recv, methodName)) case types.FieldVal: fields, jsTag := c.translateSelection(sel, f.Pos()) if jsTag != "" { call := c.formatExpr("%e.%s.%s(%s)", f.X, strings.Join(fields, "."), jsTag, externalizeArgs(e.Args)) switch sig.Results().Len() { case 0: return call case 1: return c.internalize(call, sig.Results().At(0).Type()) default: c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: f.Pos(), Msg: "field with js tag can not have func type with multiple results"}) } } return c.translateCall(e, sig, c.formatExpr("%e.%s", f.X, strings.Join(fields, "."))) case types.MethodExpr: return c.translateCall(e, sig, c.translateExpr(f)) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } default: return c.translateCall(e, sig, c.translateExpr(plainFun)) } case *ast.StarExpr: if typesutil.IsJsObject(c.p.TypeOf(e.X)) { return c.formatExpr("new $jsObjectPtr(%e)", e.X) } if c1, isCall := e.X.(*ast.CallExpr); isCall && len(c1.Args) == 1 { if c2, isCall := c1.Args[0].(*ast.CallExpr); isCall && len(c2.Args) == 1 && types.Identical(c.p.TypeOf(c2.Fun), types.Typ[types.UnsafePointer]) { if unary, isUnary := c2.Args[0].(*ast.UnaryExpr); isUnary && unary.Op == token.AND { return c.translateExpr(unary.X) // unsafe conversion } } } switch exprType.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } return c.formatExpr("%e.$get()", e.X) case *ast.TypeAssertExpr: if e.Type == nil { return c.translateExpr(e.X) } t := c.p.TypeOf(e.Type) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("$assertType(%e, %s, true)", e.X, c.typeName(t)) } return c.formatExpr("$assertType(%e, %s)", e.X, c.typeName(t)) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } switch o := obj.(type) { case *types.Var, *types.Const: return c.formatExpr("%s", c.objectName(o)) case *types.Func: return c.formatExpr("%s", c.objectName(o)) case *types.TypeName: return c.formatExpr("%s", c.typeName(o.Type())) case *types.Nil: if typesutil.IsJsObject(exprType) { return c.formatExpr("null") } switch t := exprType.Underlying().(type) { case *types.Basic: if t.Kind() != types.UnsafePointer { panic("unexpected basic type") } return c.formatExpr("0") case *types.Slice, *types.Pointer, *types.Chan: return c.formatExpr("%s.nil", c.typeName(exprType)) case *types.Map: return c.formatExpr("false") case *types.Interface: return c.formatExpr("$ifaceNil") case *types.Signature: return c.formatExpr("$throwNilPointerError") default: panic(fmt.Sprintf("unexpected type: %T", t)) } default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case *this: if isWrapped(c.p.TypeOf(e)) { return c.formatExpr("this.$val") } return c.formatExpr("this") case nil: return c.formatExpr("") default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) } }
// builtin type-checks a call to the built-in specified by id and // returns true if the call is valid, with *x holding the result; // but x.expr is not set. If the call is invalid, the result is // false, and *x is undefined. // func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ bool) { // append is the only built-in that permits the use of ... for the last argument bin := predeclaredFuncs[id] if call.Ellipsis.IsValid() && id != _Append { check.invalidOp(call.Ellipsis, "invalid use of ... with built-in %s", bin.name) check.use(call.Args...) return } // For len(x) and cap(x) we need to know if x contains any function calls or // receive operations. Save/restore current setting and set hasCallOrRecv to // false for the evaluation of x so that we can check it afterwards. // Note: We must do this _before_ calling unpack because unpack evaluates the // first argument before we even call arg(x, 0)! if id == _Len || id == _Cap { defer func(b bool) { check.hasCallOrRecv = b }(check.hasCallOrRecv) check.hasCallOrRecv = false } // determine actual arguments var arg getter nargs := len(call.Args) switch id { default: // make argument getter arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false) if arg == nil { return } // evaluate first argument, if present if nargs > 0 { arg(x, 0) if x.mode == invalid { return } } case _Make, _New, _Offsetof, _Trace: // arguments require special handling } // check argument count { msg := "" if nargs < bin.nargs { msg = "not enough" } else if !bin.variadic && nargs > bin.nargs { msg = "too many" } if msg != "" { check.invalidOp(call.Rparen, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs) return } } switch id { case _Append: // append(s S, x ...T) S, where T is the element type of S // spec: "The variadic function append appends zero or more values x to s of type // S, which must be a slice type, and returns the resulting slice, also of type S. // The values x are passed to a parameter of type ...T where T is the element type // of S and the respective parameter passing rules apply." S := x.typ var T Type if s, _ := S.Underlying().(*Slice); s != nil { T = s.elem } else { check.invalidArg(x.pos(), "%s is not a slice", x) return } // remember arguments that have been evaluated already alist := []operand{*x} // spec: "As a special case, append also accepts a first argument assignable // to type []byte with a second argument of string type followed by ... . // This form appends the bytes of the string. if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check.conf, NewSlice(universeByte)) { arg(x, 1) if x.mode == invalid { return } if isString(x.typ) { if check.Types != nil { sig := makeSig(S, S, x.typ) sig.variadic = true check.recordBuiltinType(call.Fun, sig) } x.mode = value x.typ = S break } alist = append(alist, *x) // fallthrough } // check general case by creating custom signature sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature sig.variadic = true check.arguments(x, call, sig, func(x *operand, i int) { // only evaluate arguments that have not been evaluated before if i < len(alist) { *x = alist[i] return } arg(x, i) }, nargs) // ok to continue even if check.arguments reported errors x.mode = value x.typ = S if check.Types != nil { check.recordBuiltinType(call.Fun, sig) } case _Cap, _Len: // cap(x) // len(x) mode := invalid var typ Type var val exact.Value switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) { case *Basic: if isString(t) && id == _Len { if x.mode == constant { mode = constant val = exact.MakeInt64(int64(len(exact.StringVal(x.val)))) } else { mode = value } } case *Array: mode = value // spec: "The expressions len(s) and cap(s) are constants // if the type of s is an array or pointer to an array and // the expression s does not contain channel receives or // function calls; in this case s is not evaluated." if !check.hasCallOrRecv { mode = constant val = exact.MakeInt64(t.len) } case *Slice, *Chan: mode = value case *Map: if id == _Len { mode = value } } if mode == invalid { check.invalidArg(x.pos(), "%s for %s", x, bin.name) return } x.mode = mode x.typ = Typ[Int] x.val = val if check.Types != nil && mode != constant { check.recordBuiltinType(call.Fun, makeSig(x.typ, typ)) } case _Close: // close(c) c, _ := x.typ.Underlying().(*Chan) if c == nil { check.invalidArg(x.pos(), "%s is not a channel", x) return } if c.dir == RecvOnly { check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, c)) } case _Complex: // complex(x, y realT) complexT if !check.complexArg(x) { return } var y operand arg(&y, 1) if y.mode == invalid { return } if !check.complexArg(&y) { return } check.convertUntyped(x, y.typ) if x.mode == invalid { return } check.convertUntyped(&y, x.typ) if y.mode == invalid { return } if !Identical(x.typ, y.typ) { check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) return } if x.mode == constant && y.mode == constant { x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val)) } else { x.mode = value } realT := x.typ complexT := Typ[Invalid] switch realT.Underlying().(*Basic).kind { case Float32: complexT = Typ[Complex64] case Float64: complexT = Typ[Complex128] case UntypedInt, UntypedRune, UntypedFloat: if x.mode == constant { realT = defaultType(realT).(*Basic) complexT = Typ[UntypedComplex] } else { // untyped but not constant; probably because one // operand is a non-constant shift of untyped lhs realT = Typ[Float64] complexT = Typ[Complex128] } default: check.invalidArg(x.pos(), "float32 or float64 arguments expected") return } x.typ = complexT if check.Types != nil && x.mode != constant { check.recordBuiltinType(call.Fun, makeSig(complexT, realT, realT)) } if x.mode != constant { // The arguments have now their final types, which at run- // time will be materialized. Update the expression trees. // If the current types are untyped, the materialized type // is the respective default type. // (If the result is constant, the arguments are never // materialized and there is nothing to do.) check.updateExprType(x.expr, realT, true) check.updateExprType(y.expr, realT, true) } case _Copy: // copy(x, y []T) int var dst Type if t, _ := x.typ.Underlying().(*Slice); t != nil { dst = t.elem } var y operand arg(&y, 1) if y.mode == invalid { return } var src Type switch t := y.typ.Underlying().(type) { case *Basic: if isString(y.typ) { src = universeByte } case *Slice: src = t.elem } if dst == nil || src == nil { check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y) return } if !Identical(dst, src) { check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) return } if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ)) } x.mode = value x.typ = Typ[Int] case _Delete: // delete(m, k) m, _ := x.typ.Underlying().(*Map) if m == nil { check.invalidArg(x.pos(), "%s is not a map", x) return } arg(x, 1) // k if x.mode == invalid { return } if !x.assignableTo(check.conf, m.key) { check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key)) } case _Imag, _Real: // imag(complexT) realT // real(complexT) realT if !isComplex(x.typ) { check.invalidArg(x.pos(), "%s must be a complex number", x) return } if x.mode == constant { if id == _Real { x.val = exact.Real(x.val) } else { x.val = exact.Imag(x.val) } } else { x.mode = value } var k BasicKind switch x.typ.Underlying().(*Basic).kind { case Complex64: k = Float32 case Complex128: k = Float64 case UntypedComplex: k = UntypedFloat default: unreachable() } if check.Types != nil && x.mode != constant { check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ)) } x.typ = Typ[k] case _Make: // make(T, n) // make(T, n, m) // (no argument evaluated yet) arg0 := call.Args[0] T := check.typ(arg0) if T == Typ[Invalid] { return } var min int // minimum number of arguments switch T.Underlying().(type) { case *Slice: min = 2 case *Map, *Chan: min = 1 default: check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0) return } if nargs < min || min+1 < nargs { check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, nargs) return } var sizes []int64 // constant integer arguments, if any for _, arg := range call.Args[1:] { if s, ok := check.index(arg, -1); ok && s >= 0 { sizes = append(sizes, s) } } if len(sizes) == 2 && sizes[0] > sizes[1] { check.invalidArg(call.Args[1].Pos(), "length and capacity swapped") // safe to continue } x.mode = value x.typ = T if check.Types != nil { params := [...]Type{T, Typ[Int], Typ[Int]} check.recordBuiltinType(call.Fun, makeSig(x.typ, params[:1+len(sizes)]...)) } case _New: // new(T) // (no argument evaluated yet) T := check.typ(call.Args[0]) if T == Typ[Invalid] { return } x.mode = value x.typ = &Pointer{base: T} if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(x.typ, T)) } case _Panic: // panic(x) T := new(Interface) if !check.assignment(x, T) { assert(x.mode == invalid) return } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, T)) } case _Print, _Println: // print(x, y, ...) // println(x, y, ...) var params []Type if nargs > 0 { params = make([]Type, nargs) for i := 0; i < nargs; i++ { if i > 0 { arg(x, i) // first argument already evaluated } if !check.assignment(x, nil) { assert(x.mode == invalid) return } params[i] = x.typ } } x.mode = novalue if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(nil, params...)) } case _Recover: // recover() interface{} x.mode = value x.typ = new(Interface) if check.Types != nil { check.recordBuiltinType(call.Fun, makeSig(x.typ)) } case _Alignof: // unsafe.Alignof(x T) uintptr if !check.assignment(x, nil) { assert(x.mode == invalid) return } x.mode = constant x.val = exact.MakeInt64(check.conf.alignof(x.typ)) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Offsetof: // unsafe.Offsetof(x T) uintptr, where x must be a selector // (no argument evaluated yet) arg0 := call.Args[0] selx, _ := unparen(arg0).(*ast.SelectorExpr) if selx == nil { check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0) check.use(arg0) return } check.expr(x, selx.X) if x.mode == invalid { return } base := derefStructPtr(x.typ) sel := selx.Sel.Name obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel) switch obj.(type) { case nil: check.invalidArg(x.pos(), "%s has no single field %s", base, sel) return case *Func: // TODO(gri) Using derefStructPtr may result in methods being found // that don't actually exist. An error either way, but the error // message is confusing. See: http://play.golang.org/p/al75v23kUy , // but go/types reports: "invalid argument: x.m is a method value". check.invalidArg(arg0.Pos(), "%s is a method value", arg0) return } if indirect { check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base) return } // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)? check.recordSelection(selx, FieldVal, base, obj, index, false) offs := check.conf.offsetof(base, index) x.mode = constant x.val = exact.MakeInt64(offs) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Sizeof: // unsafe.Sizeof(x T) uintptr if !check.assignment(x, nil) { assert(x.mode == invalid) return } x.mode = constant x.val = exact.MakeInt64(check.conf.sizeof(x.typ)) x.typ = Typ[Uintptr] // result is constant - no need to record signature case _Assert: // assert(pred) causes a typechecker error if pred is false. // The result of assert is the value of pred if there is no error. // Note: assert is only available in self-test mode. if x.mode != constant || !isBoolean(x.typ) { check.invalidArg(x.pos(), "%s is not a boolean constant", x) return } if x.val.Kind() != exact.Bool { check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) return } if !exact.BoolVal(x.val) { check.errorf(call.Pos(), "%s failed", call) // compile-time assertion failure - safe to continue } // result is constant - no need to record signature case _Trace: // trace(x, y, z, ...) dumps the positions, expressions, and // values of its arguments. The result of trace is the value // of the first argument. // Note: trace is only available in self-test mode. // (no argument evaluated yet) if nargs == 0 { check.dump("%s: trace() without arguments", call.Pos()) x.mode = novalue break } var t operand x1 := x for _, arg := range call.Args { check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T)) check.dump("%s: %s", x1.pos(), x1) x1 = &t // use incoming x only for first argument } // trace is only available in test mode - no need to record signature default: unreachable() } return true }
// newValueFromConst converts a constant value to an LLVM value. func (fr *frame) newValueFromConst(v exact.Value, typ types.Type) *govalue { switch { case v == nil: llvmtyp := fr.types.ToLLVM(typ) return newValue(llvm.ConstNull(llvmtyp), typ) case isString(typ): if isUntyped(typ) { typ = types.Typ[types.String] } llvmtyp := fr.types.ToLLVM(typ) strval := exact.StringVal(v) strlen := len(strval) i8ptr := llvm.PointerType(llvm.Int8Type(), 0) var ptr llvm.Value if strlen > 0 { init := llvm.ConstString(strval, false) ptr = llvm.AddGlobal(fr.module.Module, init.Type(), "") ptr.SetInitializer(init) ptr.SetLinkage(llvm.InternalLinkage) ptr = llvm.ConstBitCast(ptr, i8ptr) } else { ptr = llvm.ConstNull(i8ptr) } len_ := llvm.ConstInt(fr.types.inttype, uint64(strlen), false) llvmvalue := llvm.Undef(llvmtyp) llvmvalue = llvm.ConstInsertValue(llvmvalue, ptr, []uint32{0}) llvmvalue = llvm.ConstInsertValue(llvmvalue, len_, []uint32{1}) return newValue(llvmvalue, typ) case isInteger(typ): if isUntyped(typ) { typ = types.Typ[types.Int] } llvmtyp := fr.types.ToLLVM(typ) var llvmvalue llvm.Value if isUnsigned(typ) { v, _ := exact.Uint64Val(v) llvmvalue = llvm.ConstInt(llvmtyp, v, false) } else { v, _ := exact.Int64Val(v) llvmvalue = llvm.ConstInt(llvmtyp, uint64(v), true) } return newValue(llvmvalue, typ) case isBoolean(typ): if isUntyped(typ) { typ = types.Typ[types.Bool] } return newValue(boolLLVMValue(exact.BoolVal(v)), typ) case isFloat(typ): if isUntyped(typ) { typ = types.Typ[types.Float64] } llvmtyp := fr.types.ToLLVM(typ) floatval, _ := exact.Float64Val(v) llvmvalue := llvm.ConstFloat(llvmtyp, floatval) return newValue(llvmvalue, typ) case typ == types.Typ[types.UnsafePointer]: llvmtyp := fr.types.ToLLVM(typ) v, _ := exact.Uint64Val(v) llvmvalue := llvm.ConstInt(fr.types.inttype, v, false) llvmvalue = llvm.ConstIntToPtr(llvmvalue, llvmtyp) return newValue(llvmvalue, typ) case isComplex(typ): if isUntyped(typ) { typ = types.Typ[types.Complex128] } llvmtyp := fr.types.ToLLVM(typ) floattyp := llvmtyp.StructElementTypes()[0] llvmvalue := llvm.ConstNull(llvmtyp) realv := exact.Real(v) imagv := exact.Imag(v) realfloatval, _ := exact.Float64Val(realv) imagfloatval, _ := exact.Float64Val(imagv) llvmre := llvm.ConstFloat(floattyp, realfloatval) llvmim := llvm.ConstFloat(floattyp, imagfloatval) llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmre, []uint32{0}) llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmim, []uint32{1}) return newValue(llvmvalue, typ) } // Special case for string -> [](byte|rune) if u, ok := typ.Underlying().(*types.Slice); ok && isInteger(u.Elem()) { if v.Kind() == exact.String { strval := fr.newValueFromConst(v, types.Typ[types.String]) return fr.convert(strval, typ) } } panic(fmt.Sprintf("unhandled: t=%s(%T), v=%v(%T)", typ, typ, v, v)) }