package generator import "fmt" import "errors" import "git.tebibyte.media/fspl/fspl/llvm" import "git.tebibyte.media/fspl/fspl/entity" import "git.tebibyte.media/fspl/fspl/analyzer" func (this *generator) generateAssignment (assignment *entity.Assignment) (llvm.Value, error) { destination, err := this.generateExpressionLoc(assignment.Location) if err != nil { return nil, err } return this.generateAssignmentToDestination ( assignment.Value, assignment.Location.Type(), destination) } // TODO possibly break generateAssignmentToDestination into several routines, // each handling one destination 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 ( // TODO: add -Val suffix source entity.Expression, destType entity.Type, irDestLoc llvm.Value, ) ( llvm.Value, error, ) { destinationSpecified := irDestLoc != nil destTypeBase := analyzer.ReduceToBase(destType) 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, if necessary irDestType, err := this.generateType(destType) if err != nil { return nil, err } if !destinationSpecified { irDestLoc = this.blockManager.newAllocaFront(irDestType) } destDataFieldLoc := this.getInterfaceDataFieldLoc(irDestLoc, irDestType) sourceType := source.Type() switch sourceTypeBase := analyzer.ReduceToBase(sourceType).(type) { // conversion from interface to interface case *entity.TypeInterface: // re-assign data source, err := this.generateExpressionLoc(source) if err != nil { return nil, err } irFromType, err := this.generateType(sourceType) if err != nil { return nil, err } sourceDataFieldLoc := this.getInterfaceDataFieldLoc(source, irFromType) sourceDataLoc := this.blockManager.NewLoad(new(llvm.TypePointer), sourceDataFieldLoc) this.blockManager.NewStore(sourceDataLoc, destDataFieldLoc) // re-assign behaviors for _, name := range destTypeBase.BehaviorOrder { fromBehaviorField := this.getInterfaceBehaviorFieldLoc ( sourceTypeBase, source, irFromType, name) toBehaviorField := this.getInterfaceBehaviorFieldLoc ( destTypeBase, irDestLoc, irDestType, name) fromBehavior := this.blockManager.NewLoad(new(llvm.TypePointer), fromBehaviorField) this.blockManager.NewStore(fromBehavior, toBehaviorField) } // conversion from pointer to interface case *entity.TypePointer: // assign data source, err := this.generateExpressionVal(source) if err != nil { return nil, err } sourceDataLoc := this.blockManager.NewLoad(new(llvm.TypePointer), source) this.blockManager.NewStore(sourceDataLoc, destDataFieldLoc) sourceType, ok := sourceTypeBase.Referenced.(*entity.TypeNamed) if !ok { return nil, errors.New(fmt.Sprint( "can't assign", sourceTypeBase, "to interface")) } // assign behaviors for _, name := range destTypeBase.BehaviorOrder { toBehaviorFieldAddress := this.getInterfaceBehaviorFieldLoc ( destTypeBase, irDestLoc, irDestType, name) key := entity.Key { Unit: sourceType.Unit(), Name: sourceType.Name, Method: name, } fromBehavior, err := this.method(key) if err != nil { return nil, err } this.blockManager.NewStore(fromBehavior, toBehaviorFieldAddress) } // conversion from other type to interface default: // assign data sourceLoc, err := this.generateExpressionLoc(source) if err != nil { return nil, err } this.blockManager.NewStore(sourceLoc, destDataFieldLoc) sourceType, ok := sourceType.(*entity.TypeNamed) if !ok { return nil, errors.New(fmt.Sprint( "can't assign", sourceType, "to interface")) } // assign behaviors for _, name := range destTypeBase.BehaviorOrder { toBehaviorFieldAddress := this.getInterfaceBehaviorFieldLoc ( destTypeBase, irDestLoc, irDestType, name) key := entity.Key { Unit: sourceType.Type.Unit(), Name: sourceType.Name, Method: name, } fromBehavior, err := this.method(key) if err != nil { return nil, err } this.blockManager.NewStore(fromBehavior, toBehaviorFieldAddress) } } if destinationSpecified { return nil, nil } else { return this.blockManager.NewLoad(irDestType, irDestLoc), nil } // conversion from any type to union case *entity.TypeUnion: // only proceed if the types are different if entity.TypesEqual(source.Type(), destType) { break } irHashType := llvm.I64 // create destination union, if necessary irDestType, err := this.generateType(destType) if err != nil { return nil, err } if !destinationSpecified { irDestLoc = this.blockManager.newAllocaFront(irDestType) } destTypeFieldLoc := this.getUnionTypeFieldLoc(irDestLoc, irDestType) destDataFieldLoc := this.getUnionDataFieldLoc(irDestLoc, irDestType) // store type hash hash := source.Type().Hash() this.blockManager.NewStore ( llvm.NewConstInt(irHashType, int64(hash.Number())), destTypeFieldLoc) // store data source, err := this.generateExpressionVal(source) if err != nil { return nil, err } this.blockManager.NewStore(source, destDataFieldLoc) if destinationSpecified { return nil, nil } else { return this.blockManager.NewLoad(irDestType, irDestLoc), nil } // conversion from any type to pointer case *entity.TypePointer: // assignments switch source := source.(type) { // assignment from array literal to pointer case *entity.LiteralArray: if destinationSpecified { _, err := this.generateLiteralArrayLoc(source, irDestLoc) return nil, err } // assignment from string literal to pointer case *entity.LiteralString: if destinationSpecified { _, err := this.generateLiteralStringLoc(source, irDestLoc) return nil, err } } // conversion from any type to slice case *entity.TypeSlice: // conversions switch sourceTypeBase := analyzer.ReduceToBase(source.Type()).(type) { // conversion from array to slice case *entity.TypeArray: irIndexType, err := this.generateTypeIndex() if err != nil { return nil, err } array, err := this.generateExpressionLoc(source) if err != nil { return nil, err } 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(irIndexType, int64(sourceTypeBase.Length)), destLengthField) if destinationSpecified { return nil, nil } else { return this.blockManager.NewLoad(irDestType, irDestLoc), nil } } // assignments switch source := source.(type) { // assignment from array literal to slice case *entity.LiteralArray: if destinationSpecified { _, err := this.generateLiteralArrayLoc(source, irDestLoc) return nil, err } // assignment from string literal to slice case *entity.LiteralString: if destinationSpecified { _, err := this.generateLiteralStringLoc(source, irDestLoc) return nil, err } } // conversion from any type to array case *entity.TypeArray: // assignments switch source := source.(type) { // assignment from array literal to array case *entity.LiteralArray: if destinationSpecified { _, err := this.generateLiteralArrayLoc(source, irDestLoc) return nil, err } // assignment from string literal to array case *entity.LiteralString: if destinationSpecified { _, err := this.generateLiteralStringLoc(source, irDestLoc) return nil, err } } // conversion from any type to struct case *entity.TypeStruct: // assignments switch source := source.(type) { // assignment from struct literal to struct case *entity.LiteralStruct: if destinationSpecified { _, err := this.generateLiteralStructLoc(source, irDestLoc) return nil, err } } } irSource, err := this.generateExpressionVal(source) if err != nil { return nil, err } if destinationSpecified { this.blockManager.NewStore(irSource, irDestLoc) return nil, nil } else { return irSource, nil } }