compiler: implement casting named structs and pointers to them
Этот коммит содержится в:
родитель
85f2ef40f8
коммит
38c3d0852e
3 изменённых файлов: 50 добавлений и 10 удалений
|
@ -1517,21 +1517,41 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
// https://research.swtch.com/interfaces
|
||||
return c.parseExpr(frame, expr.X)
|
||||
case *ssa.ChangeType:
|
||||
// This instruction changes the type, but the underlying value remains
|
||||
// the same. This is often a no-op, but sometimes we have to change the
|
||||
// LLVM type as well.
|
||||
x, err := c.parseExpr(frame, expr.X)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
// The only case when we need to bitcast is when casting between named
|
||||
// struct types, as those are actually different in LLVM. Let's just
|
||||
// bitcast all struct types for ease of use.
|
||||
if _, ok := expr.Type().Underlying().(*types.Struct); ok {
|
||||
llvmType, err := c.getLLVMType(expr.X.Type())
|
||||
llvmType, err := c.getLLVMType(expr.Type())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return c.builder.CreateBitCast(x, llvmType, "changetype"), nil
|
||||
}
|
||||
if x.Type() == llvmType {
|
||||
// Different Go type but same LLVM type (for example, named int).
|
||||
// This is the common case.
|
||||
return x, nil
|
||||
}
|
||||
// Figure out what kind of type we need to cast.
|
||||
switch llvmType.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
// Unfortunately, we can't just bitcast structs. We have to
|
||||
// actually create a new struct of the correct type and insert the
|
||||
// values from the previous struct in there.
|
||||
value := llvm.Undef(llvmType)
|
||||
for i := 0; i < llvmType.StructElementTypesCount(); i++ {
|
||||
field := c.builder.CreateExtractValue(x, i, "changetype.field")
|
||||
value = c.builder.CreateInsertValue(value, field, i, "changetype.struct")
|
||||
}
|
||||
return value, nil
|
||||
case llvm.PointerTypeKind:
|
||||
// This can happen with pointers to structs. This case is easy:
|
||||
// simply bitcast the pointer to the destination type.
|
||||
return c.builder.CreateBitCast(x, llvmType, "changetype.pointer"), nil
|
||||
default:
|
||||
return llvm.Value{}, errors.New("todo: unknown ChangeType type: " + expr.X.Type().String())
|
||||
}
|
||||
case *ssa.Const:
|
||||
return c.parseConst(frame.fn.LinkName(), expr)
|
||||
case *ssa.Convert:
|
||||
|
|
18
testdata/structexpand.go → testdata/structs.go
предоставленный
18
testdata/structexpand.go → testdata/structs.go
предоставленный
|
@ -28,6 +28,14 @@ type s4 struct {
|
|||
d byte
|
||||
}
|
||||
|
||||
// same struct, different type
|
||||
type s4b struct {
|
||||
a byte
|
||||
b byte
|
||||
c byte
|
||||
d byte
|
||||
}
|
||||
|
||||
type s5 struct {
|
||||
a struct {
|
||||
aa byte
|
||||
|
@ -70,6 +78,16 @@ func test3(s s3) {
|
|||
|
||||
func test4(s s4) {
|
||||
println("test4", s.a, s.b, s.c, s.d)
|
||||
test4b(s4b(s))
|
||||
test4bp((*s4b)(&s))
|
||||
}
|
||||
|
||||
func test4b(s s4b) {
|
||||
println("test4b", s.a, s.b, s.c, s.d)
|
||||
}
|
||||
|
||||
func test4bp(s *s4b) {
|
||||
println("test4bp", s.a, s.b, s.c, s.d)
|
||||
}
|
||||
|
||||
func test5(s s5) {
|
2
testdata/structexpand.txt → testdata/structs.txt
предоставленный
2
testdata/structexpand.txt → testdata/structs.txt
предоставленный
|
@ -3,6 +3,8 @@ test1 1
|
|||
test2 1 2
|
||||
test3 1 2 3
|
||||
test4 1 2 3 4
|
||||
test4b 1 2 3 4
|
||||
test4bp 1 2 3 4
|
||||
test5 1 2 3
|
||||
test6 foo 3 5
|
||||
test7 (0:nil) 8
|
Загрузка…
Создание таблицы
Сослаться в новой задаче