diff --git a/generator/assignment.go b/generator/assignment.go index b48f6ea..6b59462 100644 --- a/generator/assignment.go +++ b/generator/assignment.go @@ -7,61 +7,68 @@ import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/analyzer" func (this *generator) generateAssignment (assignment *entity.Assignment) (llvm.Value, error) { - source, err := this.generateAssignmentSource ( - assignment.Value, - assignment.Location.Type()) - if err != nil { return nil, err } destination, err := this.generateExpressionLoc(assignment.Location) if err != nil { return nil, err } - this.blockManager.NewStore(source, destination) - return nil, nil + + return this.generateAssignmentToDestination ( + assignment.Value, + assignment.Location.Type(), + destination) } -// generateAssignmentSource performs type coercions if necessary, mainly -// interface assignment. this should be called instead of generateExpression -// when the type to assign to is known. -func (this *generator) generateAssignmentSource ( - from entity.Expression, - to entity.Type, +// generateAssignmentToDestination performs type coercions if necessary, mainly +// interface assignment. This should be called when the user is assigning a +// value to something via an assignment statement, a function/method call, or a +// field in a composite literal. +// irDestLoc specifies the location to assign the source to. If it is nil, this +// function will allocate a destination (if necessary) and return its value as a +// register. +func (this *generator) generateAssignmentToDestination ( + source entity.Expression, + destType entity.Type, + irDestLoc llvm.Value, ) ( llvm.Value, error, ) { - toBase := analyzer.ReduceToBase(to) + destinationSpecified := irDestLoc != nil + destTypeBase := analyzer.ReduceToBase(destType) - switch toBase := toBase.(type) { + switch destTypeBase := destTypeBase.(type) { // conversion from any type to interface case *entity.TypeInterface: // methods defined on interface or pointer types cannot pass // through an interface! - // create destination interface - irToType, err := this.generateType(to) + // create destination interface, if necessary + irDestType, err := this.generateType(destType) if err != nil { return nil, err } - destination := this.blockManager.newAllocaFront(irToType) - toDataField := this.getInterfaceDataFieldLoc(destination, irToType) + if !destinationSpecified { + irDestLoc = this.blockManager.newAllocaFront(irDestType) + } + toDataField := this.getInterfaceDataFieldLoc(irDestLoc, irDestType) - fromType := from.Type() - switch fromBase := analyzer.ReduceToBase(fromType).(type) { + sourceType := source.Type() + switch sourceTypeBase := analyzer.ReduceToBase(sourceType).(type) { // conversion from interface to interface case *entity.TypeInterface: // re-assign data - source, err := this.generateExpressionLoc(from) + source, err := this.generateExpressionLoc(source) if err != nil { return nil, err } - irFromType, err := this.generateType(fromType) + irFromType, err := this.generateType(sourceType) if err != nil { return nil, err } fromDataFieldAddress := this.getInterfaceDataFieldLoc(source, irFromType) fromData := this.blockManager.NewLoad(new(llvm.TypePointer), fromDataFieldAddress) this.blockManager.NewStore(fromData, toDataField) // re-assign behaviors - for _, name := range toBase.BehaviorOrder { + for _, name := range destTypeBase.BehaviorOrder { fromBehaviorField := this.getInterfaceBehaviorFieldLoc ( - fromBase, source, + sourceTypeBase, source, irFromType, name) toBehaviorField := this.getInterfaceBehaviorFieldLoc ( - toBase, destination, - irToType, name) + destTypeBase, irDestLoc, + irDestType, name) fromBehavior := this.blockManager.NewLoad(new(llvm.TypePointer), fromBehaviorField) this.blockManager.NewStore(fromBehavior, toBehaviorField) } @@ -69,22 +76,22 @@ func (this *generator) generateAssignmentSource ( // conversion from pointer to interface case *entity.TypePointer: // assign data - source, err := this.generateExpressionVal(from) + source, err := this.generateExpressionVal(source) if err != nil { return nil, err } fromData := this.blockManager.NewLoad(new(llvm.TypePointer), source) this.blockManager.NewStore(fromData, toDataField) - fromReferenced, ok := fromBase.Referenced.(*entity.TypeNamed) + fromReferenced, ok := sourceTypeBase.Referenced.(*entity.TypeNamed) if !ok { return nil, errors.New(fmt.Sprint( - "can't assign", fromBase, "to interface")) + "can't assign", sourceTypeBase, "to interface")) } // assign behaviors - for _, name := range toBase.BehaviorOrder { + for _, name := range destTypeBase.BehaviorOrder { toBehaviorFieldAddress := this.getInterfaceBehaviorFieldLoc ( - toBase, destination, - irToType, name) + destTypeBase, irDestLoc, + irDestType, name) fromBehavior, err := this.method(fromReferenced.Name, name) if err != nil { return nil, err } this.blockManager.NewStore(fromBehavior, toBehaviorFieldAddress) @@ -93,39 +100,66 @@ func (this *generator) generateAssignmentSource ( // conversion from other type to interface default: // assign data - source, err := this.generateExpressionLoc(from) + source, err := this.generateExpressionLoc(source) if err != nil { return nil, err } this.blockManager.NewStore(source, toDataField) - fromType, ok := fromType.(*entity.TypeNamed) + sourceType, ok := sourceType.(*entity.TypeNamed) if !ok { return nil, errors.New(fmt.Sprint( - "can't assign", fromBase, "to interface")) + "can't assign", sourceType, "to interface")) } // assign behaviors - for _, name := range toBase.BehaviorOrder { + for _, name := range destTypeBase.BehaviorOrder { toBehaviorFieldAddress := this.getInterfaceBehaviorFieldLoc ( - toBase, destination, - irToType, name) - fromBehavior, err := this.method(fromType.Name, name) + destTypeBase, irDestLoc, + irDestType, name) + fromBehavior, err := this.method(sourceType.Name, name) if err != nil { return nil, err } this.blockManager.NewStore(fromBehavior, toBehaviorFieldAddress) } } - return this.blockManager.NewLoad(irToType, destination), nil + if destinationSpecified { + return nil, nil + } else { + return this.blockManager.NewLoad(irDestType, irDestLoc), nil + } - // conversion from array to slice + // conversion from any type to slice case *entity.TypeSlice: - switch fromBase := analyzer.ReduceToBase(from.Type()).(type) { + switch sourceTypeBase := analyzer.ReduceToBase(source.Type()).(type) { + // conversion from array to slice case *entity.TypeArray: - source, err := this.generateExpressionVal(from) + // TODO: + array, err := this.generateExpressionLoc(source) if err != nil { return nil, err } - return this.generateSliceDefinedLength(to, source, int64(fromBase.Length)) + irDestType, err := this.generateType(destType) + if err != nil { return nil, err } + + if !destinationSpecified { + irDestLoc = this.blockManager.newAllocaFront(irDestType) + } + destDataField := this.getSliceDataFieldLoc(irDestLoc, irDestType) + destLengthField := this.getSliceLengthFieldLoc(irDestLoc, irDestType) + this.blockManager.NewStore(array, destDataField) + this.blockManager.NewStore ( + llvm.NewConstInt(llvm.I32, int64(sourceTypeBase.Length)), + destLengthField) + + if !destinationSpecified { + return this.blockManager.NewLoad(irDestType, irDestLoc), nil + } } } - source, err := this.generateExpressionVal(from) + irSource, err := this.generateExpressionVal(source) if err != nil { return nil, err } - return source, nil + + if destinationSpecified { + this.blockManager.NewStore(irDestLoc, irSource) + return nil, nil + } else { + return irSource, nil + } } diff --git a/generator/data.go b/generator/data.go index aa6aa47..8b1efdb 100644 --- a/generator/data.go +++ b/generator/data.go @@ -28,13 +28,23 @@ func (this *generator) generateSliceDefinedLength (ty entity.Type, data llvm.Val } func (this *generator) getSliceDataAddress (slice llvm.Value, irType llvm.Type) llvm.Value { - dataAddressFieldAddress := this.blockManager.NewGetElementPtr ( + return this.blockManager.NewLoad ( + new(llvm.TypePointer), + this.getSliceDataFieldLoc(slice, irType)) +} + +func (this *generator) getSliceDataFieldLoc (slice llvm.Value, irType llvm.Type) llvm.Value { + return this.blockManager.NewGetElementPtr ( irType, slice, llvm.NewConstInt(llvm.I32, 0), llvm.NewConstInt(llvm.I32, 0)) - return this.blockManager.NewLoad ( - new(llvm.TypePointer), - dataAddressFieldAddress) +} + +func (this *generator) getSliceLengthFieldLoc (slice llvm.Value, irType llvm.Type) llvm.Value { + return this.blockManager.NewGetElementPtr ( + irType, slice, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, 1)) } func (this *generator) getInterfaceDataFieldLoc (iface llvm.Value, irType llvm.Type) llvm.Value { diff --git a/generator/expression-val.go b/generator/expression-val.go index 27bc07d..f9aeee4 100644 --- a/generator/expression-val.go +++ b/generator/expression-val.go @@ -12,9 +12,10 @@ func (this *generator) generateCallVal (call *entity.Call) (llvm.Value, error) { args := make([]llvm.Value, len(call.Arguments)) for index, argument := range call.Arguments { - irArgument, err := this.generateAssignmentSource ( + irArgument, err := this.generateAssignmentToDestination ( argument, - call.Function.Signature.Arguments[index].Type()) + call.Function.Signature.Arguments[index].Type(), + nil) if err != nil { return nil, err } args[index] = irArgument @@ -30,9 +31,10 @@ func (this *generator) generateMethodCallVal (call *entity.MethodCall) (llvm.Val // build list of args args := make([]llvm.Value, len(call.Arguments) + 1) for index, argument := range call.Arguments { - irArgument, err := this.generateAssignmentSource ( + irArgument, err := this.generateAssignmentToDestination ( argument, - signature.Arguments[index].Type()) + signature.Arguments[index].Type(), + nil) if err != nil { return nil, err } args[index + 1] = irArgument } diff --git a/generator/interface_test.go b/generator/interface_test.go index fe5a617..f47bff2 100644 --- a/generator/interface_test.go +++ b/generator/interface_test.go @@ -194,63 +194,7 @@ File.[write buffer:*:Byte]:Index = [write [.this] [~*Byte buffer] [#buffer]] func TestInterfaceInStruct (test *testing.T) { testString (test, -`%Writer = type { ptr, ptr } -%A = type { %Writer } -%File = type i32 -%Index = type i64 -define void @main() { -0: - %1 = alloca %A - %2 = getelementptr %A, ptr %1, i32 0, i32 0 - store i32 0, ptr %2 - %3 = load %A, ptr %1 - %4 = alloca %A - store %A %3, ptr %4 - %5 = alloca %Writer - %6 = getelementptr %Writer, ptr %5, i32 0, i32 0 - %7 = getelementptr %A, ptr %4, i32 0, i32 0 - %8 = getelementptr %Writer, ptr %7, i32 0, i32 0 - %9 = load ptr, ptr %8 - store ptr %9, ptr %6 - %10 = getelementptr %Writer, ptr %7, i32 0, i32 1 - %11 = getelementptr %Writer, ptr %5, i32 0, i32 1 - %12 = load ptr, ptr %10 - store ptr %12, ptr %11 - %13 = load %Writer, ptr %5 - %14 = alloca %Writer - store %Writer %13, ptr %14 - %15 = alloca [1 x i8] - %16 = getelementptr [1 x i8], ptr %15, i32 0, i32 0 - store i8 33, ptr %16 - %17 = alloca { ptr, %Index } - %18 = getelementptr { ptr, %Index }, ptr %17, i32 0, i32 0 - store ptr %15, ptr %18 - %19 = getelementptr { ptr, %Index }, ptr %17, i32 0, i32 1 - store %Index 1, ptr %19 - %20 = load { ptr, %Index }, ptr %17 - %21 = getelementptr %Writer, ptr %14, i32 0, i32 1 - %22 = getelementptr %Writer, ptr %14, i32 0, i32 0 - %23 = load ptr, ptr %22 - %24 = load ptr, ptr %21 - %25 = call %Index %24(ptr %23, { ptr, %Index } %20) - ret void -} -declare %Index @write(%File %fd, ptr %buffer, %Index %count) -define %Index @File.write(ptr %this, { ptr, %Index } %buffer) { -0: - %1 = alloca ptr - store ptr %this, ptr %1 - %2 = alloca { ptr, %Index } - store { ptr, %Index } %buffer, ptr %2 - %3 = load ptr, ptr %1 - %4 = load %File, ptr %3 - %5 = getelementptr { ptr, %Index }, ptr %2, i32 0, i32 0 - %6 = load ptr, ptr %5 - %7 = getelementptr { ptr, %Index }, ptr %2, i32 0, i32 1 - %8 = load %Index, ptr %7 - %9 = call %Index @write(%File %4, ptr %6, %Index %8) - ret %Index %9 -} +` `, ` Writer: ([write buffer:*:Byte]: Index) @@ -267,3 +211,17 @@ File.[write buffer:*:Byte]:Index = [write [.this] [~*Byte buffer] [#buffer]] } `) } + +func TestInterfaceAssignment (test *testing.T) { +testString (test, +` +`, +` +Face: ([x]:Int) +Impl: Int +Impl.[x]:Int = 5 +[main] 'main' = { + i:Face = f:Impl +} +`) +} diff --git a/generator/literal.go b/generator/literal.go index 27501be..6559a3b 100644 --- a/generator/literal.go +++ b/generator/literal.go @@ -19,6 +19,7 @@ func (this *generator) generateLiteralFloat (literal *entity.LiteralFloat) (llvm } func (this *generator) generateLiteralArrayLoc (literal *entity.LiteralArray) (llvm.Value, error) { + // TODO support irDestLoc here, make use of it in generateAssignmentToDestination base := analyzer.ReduceToBase(literal.Type()) makeData := func (elementType entity.Type, irArrayType llvm.Type) (llvm.Value, error) { @@ -32,15 +33,15 @@ func (this *generator) generateLiteralArrayLoc (literal *entity.LiteralArray) (l } array := this.blockManager.newAllocaFront(irArrayType) for index, element := range literal.Elements { - irElement, err := this.generateAssignmentSource ( - element, - elementType) - if err != nil { return nil, err } elementPointer := this.blockManager.NewGetElementPtr ( irArrayType, array, llvm.NewConstInt(llvm.I32, 0), llvm.NewConstInt(llvm.I32, int64(index))) - this.blockManager.NewStore(irElement, elementPointer) + _, err := this.generateAssignmentToDestination ( + element, + elementType, + elementPointer) + if err != nil { return nil, err } } return array, nil } @@ -67,6 +68,7 @@ func (this *generator) generateLiteralArrayLoc (literal *entity.LiteralArray) (l } func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString) (llvm.Value, error) { + // TODO support irDestLoc here, make use of it in generateAssignmentToDestination base := analyzer.ReduceToBase(literal.Type()) makeData := func (anyElementType entity.Type, cstring bool) (llvm.Value, int64, error) { @@ -202,6 +204,8 @@ func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString) } func (this *generator) generateLiteralStructLoc (literal *entity.LiteralStruct) (llvm.Value, error) { + // TODO support irDestLoc here, make use of it in generateAssignmentToDestination + base, ok := analyzer.ReduceToBase(literal.Type()).(*entity.TypeStruct) if !ok { return nil, errors.New(fmt.Sprintln("struct can't be used as ", literal.Type())) } @@ -211,21 +215,24 @@ func (this *generator) generateLiteralStructLoc (literal *entity.LiteralStruct) structure := this.blockManager.newAllocaFront(irType) for index, member := range base.Members { - var irMember llvm.Value - if pair, ok := literal.MemberMap[member.Name]; ok { - irMember, err = this.generateExpressionVal(pair.Value) - if err != nil { return nil, err } - } else { - irMemberType, err := this.generateType(member.Type()) - if err != nil { return nil, err } - irMember = llvm.NewConstZeroInitializer(irMemberType) - } - elementPointer := this.blockManager.NewGetElementPtr ( irType, structure, llvm.NewConstInt(llvm.I32, 0), llvm.NewConstInt(llvm.I32, int64(index))) - this.blockManager.NewStore(irMember, elementPointer) + + if pair, ok := literal.MemberMap[member.Name]; ok { + _, err = this.generateAssignmentToDestination ( + pair.Value, + member.Type(), + elementPointer) + if err != nil { return nil, err } + } else { + irMemberType, err := this.generateType(member.Type()) + if err != nil { return nil, err } + this.blockManager.NewStore ( + llvm.NewConstZeroInitializer(irMemberType), + elementPointer) + } } return structure, nil