diff --git a/cl/_testcgo/any/in.go b/cl/_testcgo/any/in.go new file mode 100644 index 00000000..0c75725e --- /dev/null +++ b/cl/_testcgo/any/in.go @@ -0,0 +1,16 @@ +package main + +import _ "unsafe" + +func incVal(a any) int { + return a.(int) + 1 +} + +//go:linkname printf C.printf +func printf(format *int8, __llgo_va_list ...any) + +var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0} + +func main() { + printf(&format[0], incVal(100)) +} diff --git a/cl/_testcgo/any/out.ll b/cl/_testcgo/any/out.ll new file mode 100644 index 00000000..132c18d4 --- /dev/null +++ b/cl/_testcgo/any/out.ll @@ -0,0 +1,55 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr } + +@main.format = global ptr null +@"main.init$guard" = global ptr null + +define i64 @main.incVal(%"github.com/goplus/llgo/internal/runtime.iface" %0) { +_llgo_0: + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2) + %2 = call i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) + %3 = add i64 %2, 1 + ret i64 %3 +} + +define void @main.init() { +_llgo_0: + %0 = load i1, ptr @"main.init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"main.init$guard", align 1 + store i8 72, ptr @main.format, align 1 + store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1 + store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1 + store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1 + store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1 + store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1 + store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1 + store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1 + store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1 + store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @main() { +_llgo_0: + call void @main.init() + %0 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(i64 100) + %1 = call i64 @main.incVal(%"github.com/goplus/llgo/internal/runtime.iface" %0) + call void (ptr, ...) @printf(ptr @main.format, i64 %1) + ret void +} + +declare void @printf(ptr, ...) + +declare i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface", ptr) + +declare ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64) + +declare %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr, i64) diff --git a/cl/compile.go b/cl/compile.go index 5a3a602d..9f2e9ceb 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -324,11 +324,15 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l } ret = b.Alloc(p.prog.Type(t), v.Heap) case *ssa.MakeInterface: + const ( + delayExpr = true // varargs: don't need to convert an expr to any + ) t := v.Type() - if isAny(t) { // varargs: don't need to convert an expr to any - return - } - panic("todo") + x := p.compileValue(b, v.X) + ret = b.MakeInterface(t, x, delayExpr) + case *ssa.TypeAssert: + x := p.compileValue(b, v.X) + ret = b.TypeAssert(x, p.prog.Type(v.AssertedType), v.CommaOk) default: panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv)) } @@ -428,7 +432,7 @@ func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int) n := len(vals) - hasVArg ret := make([]llssa.Expr, n) for i := 0; i < n; i++ { - ret[i] = p.compileValue(b, vals[i]) + ret[i] = p.compileValue(b, vals[i]).Do() } if hasVArg > 0 { ret = p.compileVArg(ret, b, vals[n]) diff --git a/internal/build/build.go b/internal/build/build.go index 20bc0bf2..0c97023f 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -19,6 +19,7 @@ package build import ( "fmt" "go/token" + "go/types" "log" "os" "os/exec" @@ -112,6 +113,11 @@ func Do(args []string, conf *Config) { } prog := llssa.NewProgram(nil) + prog.SetRuntime(func() *types.Package { + rt, err := packages.Load(cfg, "github.com/goplus/llgo/internal/runtime") + check(err) + return rt[0].Types + }) mode := conf.Mode if mode == ModeBuild && len(initial) == 1 { mode = ModeInstall diff --git a/internal/llgen/llgenf.go b/internal/llgen/llgenf.go index 9eca9f8d..203ed23e 100644 --- a/internal/llgen/llgenf.go +++ b/internal/llgen/llgenf.go @@ -17,6 +17,7 @@ package llgen import ( + "go/types" "os" "github.com/goplus/llgo/cl" @@ -48,6 +49,12 @@ func GenFromFile(inFile string) string { ssaPkg.Build() prog := llssa.NewProgram(nil) + prog.SetRuntime(func() *types.Package { + rt, err := packages.Load(cfg, "github.com/goplus/llgo/internal/runtime") + check(err) + return rt[0].Types + }) + ret, err := cl.NewPackage(prog, ssaPkg, pkg.Syntax) check(err) diff --git a/internal/runtime/iface.go b/internal/runtime/iface.go new file mode 100644 index 00000000..47bf8fc2 --- /dev/null +++ b/internal/runtime/iface.go @@ -0,0 +1,5 @@ +// Copyright 2014 The Go 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 runtime diff --git a/internal/runtime/runtime2.go b/internal/runtime/runtime2.go new file mode 100644 index 00000000..d0d8bea7 --- /dev/null +++ b/internal/runtime/runtime2.go @@ -0,0 +1,37 @@ +// Copyright 2009 The Go 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 runtime + +import ( + "unsafe" +) + +type iface struct { + tab *itab + data unsafe.Pointer +} + +/* +type eface struct { + _type *_type + data unsafe.Pointer +} + +func efaceOf(ep *any) *eface { + return (*eface)(unsafe.Pointer(ep)) +} +*/ + +// layout of Itab known to compilers +// allocated in non-garbage-collected memory +// Needs to be in sync with +// ../cmd/compile/internal/reflectdata/reflect.go:/^func.WriteTabs. +type itab struct { + inter *interfacetype + _type *_type + hash uint32 // copy of _type.hash. Used for type switches. + _ [4]byte + fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. +} diff --git a/internal/runtime/slice.go b/internal/runtime/slice.go new file mode 100644 index 00000000..21caa126 --- /dev/null +++ b/internal/runtime/slice.go @@ -0,0 +1,15 @@ +// Copyright 2009 The Go 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 runtime + +import ( + "unsafe" +) + +type slice struct { + array unsafe.Pointer + len int + cap int +} diff --git a/internal/runtime/type.go b/internal/runtime/type.go new file mode 100644 index 00000000..a02e3b6c --- /dev/null +++ b/internal/runtime/type.go @@ -0,0 +1,18 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Runtime type representation. + +package runtime + +import ( + "github.com/goplus/llgo/internal/abi" +) + +// type nameOff = abi.NameOff +// type typeOff = abi.TypeOff + +type _type = abi.Type + +type interfacetype = abi.InterfaceType diff --git a/internal/runtime/z_iface.go b/internal/runtime/z_iface.go new file mode 100644 index 00000000..e2a07fea --- /dev/null +++ b/internal/runtime/z_iface.go @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package runtime + +import ( + "unsafe" + + "github.com/goplus/llgo/internal/abi" +) + +type Interface = iface + +type InterfaceType = abi.InterfaceType + +var ( + TyAny = &InterfaceType{} +) + +func MakeAnyInt(typ *Type, data uintptr) Interface { + return Interface{ + tab: &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}}, + data: unsafe.Pointer(data), + } +} + +func MakeAny(typ *Type, data unsafe.Pointer) Interface { + return Interface{ + tab: &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}}, + data: data, + } +} + +func MakeInterface(inter *InterfaceType, typ *Type, data unsafe.Pointer) Interface { + return Interface{ + tab: &itab{inter: inter, _type: typ, hash: 0, fun: [1]uintptr{0}}, + data: data, + } +} + +func I2Int(v Interface, t *Type) uintptr { + if v.tab._type == t { + return uintptr(v.data) + } + panic("I2Int: type mismatch") +} + +func CheckI2Int(v Interface, t *Type) (uintptr, bool) { + if v.tab._type == t { + return uintptr(v.data), true + } + return 0, false +} diff --git a/internal/runtime/z_slice.go b/internal/runtime/z_slice.go new file mode 100644 index 00000000..b6d5cbf5 --- /dev/null +++ b/internal/runtime/z_slice.go @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package runtime + +type Slice = slice + +func MakeEmptySlice() Slice { + return Slice{nil, 0, 0} +} diff --git a/internal/runtime/z_type.go b/internal/runtime/z_type.go new file mode 100644 index 00000000..f0ad7723 --- /dev/null +++ b/internal/runtime/z_type.go @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package runtime + +import ( + "go/types" + "unsafe" + + "github.com/goplus/llgo/internal/abi" +) + +type Type = abi.Type + +func Basic(kind types.BasicKind) *Type { + return basicTypes[kind] +} + +var ( + basicTypes = [...]*Type{ + abi.Bool: basicType(abi.Bool), + abi.Int: basicType(abi.Int), + abi.Int8: basicType(abi.Int8), + abi.Int16: basicType(abi.Int16), + abi.Int32: basicType(abi.Int32), + abi.Int64: basicType(abi.Int64), + abi.Uint: basicType(abi.Uint), + abi.Uint8: basicType(abi.Uint8), + abi.Uint16: basicType(abi.Uint16), + abi.Uint32: basicType(abi.Uint32), + abi.Uint64: basicType(abi.Uint64), + abi.Uintptr: basicType(abi.Uintptr), + abi.Float32: basicType(abi.Float32), + abi.Float64: basicType(abi.Float64), + abi.Complex64: basicType(abi.Complex64), + abi.Complex128: basicType(abi.Complex128), + } +) + +var ( + sizeBasicTypes = [...]uintptr{ + abi.Bool: unsafe.Sizeof(false), + abi.Int: unsafe.Sizeof(0), + abi.Int8: 1, + abi.Int16: 2, + abi.Int32: 4, + abi.Int64: 8, + abi.Uint: unsafe.Sizeof(uint(0)), + abi.Uint8: 1, + abi.Uint16: 2, + abi.Uint32: 4, + abi.Uint64: 8, + abi.Uintptr: unsafe.Sizeof(uintptr(0)), + abi.Float32: 4, + abi.Float64: 8, + abi.Complex64: 8, + abi.Complex128: 16, + } +) + +func basicType(kind abi.Kind) *Type { + return &abi.Type{ + Size_: sizeBasicTypes[kind], + Kind_: uint8(kind), + } +} diff --git a/internal/unsafeheader/unsafeheader.go b/internal/unsafeheader/unsafeheader.go deleted file mode 100644 index 6d092c62..00000000 --- a/internal/unsafeheader/unsafeheader.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020 The Go 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 unsafeheader contains header declarations for the Go runtime's slice -// and string implementations. -// -// This package allows packages that cannot import "reflect" to use types that -// are tested to be equivalent to reflect.SliceHeader and reflect.StringHeader. -package unsafeheader - -import ( - "unsafe" -) - -// Slice is the runtime representation of a slice. -// It cannot be used safely or portably and its representation may -// change in a later release. -// -// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the -// data it references will not be garbage collected. -type Slice struct { - Data unsafe.Pointer - Len int - Cap int -} - -// String is the runtime representation of a string. -// It cannot be used safely or portably and its representation may -// change in a later release. -// -// Unlike reflect.StringHeader, its Data field is sufficient to guarantee the -// data it references will not be garbage collected. -type String struct { - Data unsafe.Pointer - Len int -} diff --git a/ssa/decl.go b/ssa/decl.go index 18760708..942a1623 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -106,6 +106,7 @@ func (g Global) Init(v Expr) { // respectively, and is nil in the generic method. type aFunction struct { Expr + pkg Package prog Program blks []BasicBlock @@ -116,9 +117,9 @@ type aFunction struct { // Function represents a function or method. type Function = *aFunction -func newFunction(fn llvm.Value, t Type, prog Program) Function { +func newFunction(fn llvm.Value, t Type, pkg Package, prog Program) Function { params, hasVArg := newParams(t, prog) - return &aFunction{Expr{fn, t}, prog, nil, params, hasVArg} + return &aFunction{Expr{fn, t}, pkg, prog, nil, params, hasVArg} } func newParams(fn Type, prog Program) (params []Type, hasVArg bool) { diff --git a/ssa/expr.go b/ssa/expr.go index dca906b9..811d6d06 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -41,6 +41,29 @@ func (v Expr) TypeOf() types.Type { } */ +// Do evaluates the delay expression and returns the result. +func (v Expr) Do() Expr { + if vt := v.Type; vt.kind == vkDelayExpr { + return vt.t.(delayExprTy)() + } + return v +} + +// DelayExpr returns a delay expression. +func DelayExpr(f func() Expr) Expr { + return Expr{Type: &aType{t: delayExprTy(f), kind: vkDelayExpr}} +} + +type delayExprTy func() Expr + +func (p delayExprTy) Underlying() types.Type { + panic("don't call") +} + +func (p delayExprTy) String() string { + return "delayExpr" +} + // ----------------------------------------------------------------------------- func llvmValues(vals []Expr) []llvm.Value { @@ -407,8 +430,114 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) { panic("todo") } +// MakeInterface constructs an instance of an interface type from a +// value of a concrete type. +// +// Use Program.MethodSets.MethodSet(X.Type()) to find the method-set +// of X, and Program.MethodValue(m) to find the implementation of a method. +// +// To construct the zero value of an interface type T, use: +// +// NewConst(constant.MakeNil(), T, pos) +// +// Pos() returns the ast.CallExpr.Lparen, if the instruction arose +// from an explicit conversion in the source. +// +// Example printed form: +// +// t1 = make interface{} <- int (42:int) +// t2 = make Stringer <- t0 +func (b Builder) MakeInterface(inter types.Type, x Expr, mayDelay bool) (ret Expr) { + if debugInstr { + log.Printf("MakeInterface %v, %v\n", inter, x.impl) + } + t := inter.Underlying().(*types.Interface) + isAny := t.Empty() + fnDo := func() Expr { + pkg := b.fn.pkg + switch x.kind { + case vkSigned, vkUnsigned, vkFloat: + fn := pkg.rtFunc("MakeAnyInt") + return b.InlineCall(fn, x) + } + panic("todo") + } + if mayDelay && isAny { + return DelayExpr(fnDo) + } + return fnDo() +} + +// The TypeAssert instruction tests whether interface value X has type +// AssertedType. +// +// If !CommaOk, on success it returns v, the result of the conversion +// (defined below); on failure it panics. +// +// If CommaOk: on success it returns a pair (v, true) where v is the +// result of the conversion; on failure it returns (z, false) where z +// is AssertedType's zero value. The components of the pair must be +// accessed using the Extract instruction. +// +// If Underlying: tests whether interface value X has the underlying +// type AssertedType. +// +// If AssertedType is a concrete type, TypeAssert checks whether the +// dynamic type in interface X is equal to it, and if so, the result +// of the conversion is a copy of the value in the interface. +// +// If AssertedType is an interface, TypeAssert checks whether the +// dynamic type of the interface is assignable to it, and if so, the +// result of the conversion is a copy of the interface value X. +// If AssertedType is a superinterface of X.Type(), the operation will +// fail iff the operand is nil. (Contrast with ChangeInterface, which +// performs no nil-check.) +// +// Type() reflects the actual type of the result, possibly a +// 2-types.Tuple; AssertedType is the asserted type. +// +// Depending on the TypeAssert's purpose, Pos may return: +// - the ast.CallExpr.Lparen of an explicit T(e) conversion; +// - the ast.TypeAssertExpr.Lparen of an explicit e.(T) operation; +// - the ast.CaseClause.Case of a case of a type-switch statement; +// - the Ident(m).NamePos of an interface method value i.m +// (for which TypeAssert may be used to effect the nil check). +// +// Example printed form: +// +// t1 = typeassert t0.(int) +// t3 = typeassert,ok t2.(T) +func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) { + if debugInstr { + log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.t, commaOk) + } + switch assertedTyp.kind { + case vkSigned, vkUnsigned, vkFloat: + pkg := b.fn.pkg + fnName := "I2Int" + if commaOk { + fnName = "CheckI2Int" + } + fn := pkg.rtFunc(fnName) + var kind types.BasicKind + switch t := assertedTyp.t.(type) { + case *types.Basic: + kind = t.Kind() + default: + panic("todo") + } + typ := b.InlineCall(pkg.rtFunc("Basic"), b.prog.Val(int(kind))) + return b.InlineCall(fn, x, typ) + } + panic("todo") +} + // ----------------------------------------------------------------------------- +func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) { + return b.Call(fn, args...) +} + // The Call instruction represents a function or method call. // // The Call instruction yields the function result if there is exactly diff --git a/ssa/package.go b/ssa/package.go index b6507c1f..23af1d23 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -94,6 +94,9 @@ type aProgram struct { ctx llvm.Context typs typeutil.Map + rt *types.Scope + rtget func() *types.Package + target *Target td llvm.TargetData // tm llvm.TargetMachine @@ -107,6 +110,10 @@ type aProgram struct { voidType llvm.Type voidPtrTy llvm.Type + rtIfaceTy llvm.Type + rtSliceTy llvm.Type + + anyTy Type voidTy Type boolTy Type intTy Type @@ -128,6 +135,40 @@ func NewProgram(target *Target) Program { return &aProgram{ctx: ctx, target: target, td: td} } +// SetRuntime sets the runtime. +func (p Program) SetRuntime(runtime func() *types.Package) { + p.rtget = runtime +} + +func (p Program) runtime() *types.Scope { + if p.rt == nil { + p.rt = p.rtget().Scope() + } + return p.rt +} + +func (p Program) rtNamed(name string) *types.Named { + return p.runtime().Lookup(name).Type().(*types.Named) +} + +func (p Program) rtType(name string) Type { + return p.Type(p.rtNamed(name)) +} + +func (p Program) rtIface() llvm.Type { + if p.rtIfaceTy.IsNil() { + p.rtIfaceTy = p.rtType("Interface").ll + } + return p.rtIfaceTy +} + +func (p Program) rtSlice() llvm.Type { + if p.rtSliceTy.IsNil() { + p.rtSliceTy = p.rtType("Slice").ll + } + return p.rtSliceTy +} + // NewPackage creates a new package. func (p Program) NewPackage(name, pkgPath string) Package { mod := p.ctx.NewModule(pkgPath) @@ -154,6 +195,14 @@ func (p Program) Bool() Type { return p.boolTy } +// Any returns any type. +func (p Program) Any() Type { + if p.anyTy == nil { + p.anyTy = p.Type(tyAny) + } + return p.anyTy +} + // Int returns int type. func (p Program) Int() Type { if p.intTy == nil { @@ -203,11 +252,16 @@ func (p Package) NewVar(name string, typ types.Type) Global { return ret } +// VarOf returns a global variable by name. +func (p Package) VarOf(name string) Global { + return p.vars[name] +} + // NewFunc creates a new function. func (p Package) NewFunc(name string, sig *types.Signature) Function { t := p.prog.llvmSignature(sig) fn := llvm.AddFunction(p.mod, name, t.ll) - ret := newFunction(fn, t, p.prog) + ret := newFunction(fn, t, p, p.prog) p.fns[name] = ret return ret } @@ -217,9 +271,14 @@ func (p Package) FuncOf(name string) Function { return p.fns[name] } -// VarOf returns a global variable by name. -func (p Package) VarOf(name string) Global { - return p.vars[name] +func (p Package) rtFunc(fnName string) Expr { + fn := p.prog.runtime().Lookup(fnName).(*types.Func) + name := FullName(fn.Pkg(), fnName) + v, ok := p.fns[name] + if !ok { + v = p.NewFunc(name, fn.Type().(*types.Signature)) + } + return v.Expr } // ----------------------------------------------------------------------------- diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 448515bf..c6977054 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -23,6 +23,37 @@ import ( "testing" ) +/* +func TestMakeInterface(t *testing.T) { + var b Builder + b.MakeInterface(types.NewInterfaceType(nil, nil), Expr{}, true).Do(true) +} +*/ + +func TestDelayExpr(t *testing.T) { + a := delayExprTy(nil) + _ = a.String() + defer func() { + if r := recover(); r == nil { + t.Log("TestDelayExpr: no error?") + } + }() + a.Underlying() +} + +func TestAny(t *testing.T) { + prog := NewProgram(nil) + prog.SetRuntime(func() *types.Package { + ret := types.NewPackage("runtime", "runtime") + scope := ret.Scope() + name := types.NewTypeName(0, ret, "Interface", nil) + types.NewNamed(name, types.NewStruct(nil, nil), nil) + scope.Insert(name) + return ret + }) + prog.Any() +} + func assertPkg(t *testing.T, p Package, expected string) { t.Helper() if v := p.String(); v != expected { diff --git a/ssa/type.go b/ssa/type.go index 0b26d7bb..ffec59b9 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -23,6 +23,10 @@ import ( "github.com/goplus/llvm" ) +var ( + tyAny = types.NewInterfaceType(nil, nil) +) + // ----------------------------------------------------------------------------- type valueKind = int @@ -37,6 +41,7 @@ const ( vkBool vkFunc vkTuple + vkDelayExpr = -1 ) // ----------------------------------------------------------------------------- @@ -237,7 +242,10 @@ func (p Program) toLLVMType(typ types.Type) Type { case *types.Pointer: elem := p.Type(t.Elem()) return &aType{llvm.PointerType(elem.ll, 0), typ, vkInvalid} + case *types.Interface: + return &aType{p.rtIface(), typ, vkInvalid} case *types.Slice: + return &aType{p.rtSlice(), typ, vkInvalid} case *types.Map: case *types.Struct: return p.toLLVMStruct(t)