diff --git a/compiler/interface.go b/compiler/interface.go index 7460c658..418b746a 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -31,6 +31,15 @@ func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token. return itf } +// extractValueFromInterface extract the value from an interface value +// (runtime._interface) under the assumption that it is of the type given in +// llvmType. The behavior is undefied if the interface is nil or llvmType +// doesn't match the underlying type of the interface. +func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type) llvm.Value { + valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr") + return b.emitPointerUnpack(valuePtr, []llvm.Type{llvmType})[0] +} + // getTypeCode returns a reference to a type code. // It returns a pointer to an external global which should be replaced with the // real type in the interface lowering pass. @@ -416,8 +425,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value { } else { // Type assert on concrete type. Extract the underlying type from // the interface (but only after checking it matches). - valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr") - valueOk = b.emitPointerUnpack(valuePtr, []llvm.Type{assertedType})[0] + valueOk = b.extractValueFromInterface(itf, assertedType) } b.CreateBr(nextBlock) diff --git a/compiler/map.go b/compiler/map.go index 030a27df..62162c0c 100644 --- a/compiler/map.go +++ b/compiler/map.go @@ -171,19 +171,59 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok // map. It returns a tuple of {bool, key, value} with the result of the // iteration. func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llvm.Value) llvm.Value { - llvmKeyType := b.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Key()) - llvmValueType := b.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Elem()) + // Determine the type of the values to return from the *ssa.Next + // instruction. It is returned as {bool, keyType, valueType}. + keyType := rangeVal.Type().Underlying().(*types.Map).Key() + valueType := rangeVal.Type().Underlying().(*types.Map).Elem() + llvmKeyType := b.getLLVMType(keyType) + llvmValueType := b.getLLVMType(valueType) - mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmKeyType, "range.key") + // There is a special case in which keys are stored as an interface value + // instead of the value they normally are. This happens for non-trivially + // comparable types such as float32 or some structs. + isKeyStoredAsInterface := false + if t, ok := keyType.Underlying().(*types.Basic); ok && t.Info()&types.IsString != 0 { + // key is a string + } else if hashmapIsBinaryKey(keyType) { + // key can be compared with runtime.memequal + } else { + // The key is stored as an interface value, and may or may not be an + // interface type (for example, float32 keys are stored as an interface + // value). + if _, ok := keyType.Underlying().(*types.Interface); !ok { + isKeyStoredAsInterface = true + } + } + + // Determine the type of the key as stored in the map. + llvmStoredKeyType := llvmKeyType + if isKeyStoredAsInterface { + llvmStoredKeyType = b.getLLVMRuntimeType("_interface") + } + + // Extract the key and value from the map. + mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key") mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value") ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next") + mapKey := b.CreateLoad(mapKeyAlloca, "") + mapValue := b.CreateLoad(mapValueAlloca, "") - tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.Int1Type(), llvmKeyType, llvmValueType}, false)) - tuple = b.CreateInsertValue(tuple, ok, 0, "") - tuple = b.CreateInsertValue(tuple, b.CreateLoad(mapKeyAlloca, ""), 1, "") - tuple = b.CreateInsertValue(tuple, b.CreateLoad(mapValueAlloca, ""), 2, "") + if isKeyStoredAsInterface { + // The key is stored as an interface but it isn't of interface type. + // Extract the underlying value. + mapKey = b.extractValueFromInterface(mapKey, llvmKeyType) + } + + // End the lifetimes of the allocas, because we're done with them. b.emitLifetimeEnd(mapKeyPtr, mapKeySize) b.emitLifetimeEnd(mapValuePtr, mapValueSize) + + // Construct the *ssa.Next return value: {ok, mapKey, mapValue} + tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.Int1Type(), llvmKeyType, llvmValueType}, false)) + tuple = b.CreateInsertValue(tuple, ok, 0, "") + tuple = b.CreateInsertValue(tuple, mapKey, 1, "") + tuple = b.CreateInsertValue(tuple, mapValue, 2, "") + return tuple } diff --git a/testdata/map.go b/testdata/map.go index 1d7cbf28..4ca456ea 100644 --- a/testdata/map.go +++ b/testdata/map.go @@ -80,15 +80,24 @@ func main() { println("itfMap[true]:", itfMap[true]) delete(itfMap, 8) println("itfMap[8]:", itfMap[8]) + for key, value := range itfMap { + if key == "eight" { + println("itfMap: found key \"eight\":", value) + } + } // test map with float keys floatMap := map[float32]int{ - 42: 84, + 42: 84, + 3.14: 6, } println("floatMap[42]:", floatMap[42]) println("floatMap[43]:", floatMap[43]) delete(floatMap, 42) println("floatMap[42]:", floatMap[42]) + for k, v := range floatMap { + println("floatMap key, value:", k, v) + } // test maps with struct keys structMap := map[namedFloat]int{ diff --git a/testdata/map.txt b/testdata/map.txt index d32dae55..834dd7ad 100644 --- a/testdata/map.txt +++ b/testdata/map.txt @@ -63,9 +63,11 @@ itfMap["eight"]: 800 itfMap[[2]int{5, 2}]: 52 itfMap[true]: 1 itfMap[8]: 0 +itfMap: found key "eight": 800 floatMap[42]: 84 floatMap[43]: 0 floatMap[42]: 0 +floatMap key, value: +3.140000e+000 6 structMap[{"tau", 6.28}]: 5 structMap[{"Tau", 6.28}]: 0 structMap[{"tau", 3.14}]: 0