Browse Source

compiler: implement casting named structs and pointers to them

pull/261/head
Ayke van Laethem 6 years ago
committed by Ron Evans
parent
commit
38c3d0852e
  1. 40
      compiler/compiler.go
  2. 18
      testdata/structs.go
  3. 2
      testdata/structs.txt

40
compiler/compiler.go

@ -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())
if err != nil {
return llvm.Value{}, err
}
return c.builder.CreateBitCast(x, llvmType, "changetype"), nil
llvmType, err := c.getLLVMType(expr.Type())
if err != nil {
return llvm.Value{}, err
}
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())
}
return x, nil
case *ssa.Const:
return c.parseConst(frame.fn.LinkName(), expr)
case *ssa.Convert:

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

@ -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
Loading…
Cancel
Save