compiler: implement operations on nil hashmaps

* comparing a map against nil
  * getting the length of a nil map
This commit is contained in:
Ayke van Laethem
2018-10-27 00:54:13 +02:00
parent c84fc6a670
commit 436901dc49
4 changed files with 31 additions and 7 deletions
+15 -7
View File
@@ -1870,12 +1870,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string)
// string or slice
llvmLen = c.builder.CreateExtractValue(value, 1, "len")
case *types.Map:
indices := []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 2, false), // hashmap.count
}
ptr := c.builder.CreateGEP(value, indices, "lenptr")
llvmLen = c.builder.CreateLoad(ptr, "len")
llvmLen = c.createRuntimeCall("hashmapLen", []llvm.Value{value}, "len")
default:
return llvm.Value{}, errors.New("todo: len: unknown type")
}
@@ -3053,7 +3048,10 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value) (
default:
return llvm.Value{}, errors.New("binop on interface: " + op.String())
}
case *types.Pointer:
case *types.Map, *types.Pointer:
// Maps are in general not comparable, but can be compared against nil
// (which is a nil pointer). This means they can be trivially compared
// by treating them as a pointer.
switch op {
case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
@@ -3212,6 +3210,16 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) (llvm.Value, error
llvmLen, // cap
}, false)
return slice, nil
case *types.Map:
if !expr.IsNil() {
// I believe this is not allowed by the Go spec.
panic("non-nil map constant")
}
llvmType, err := c.getLLVMType(typ)
if err != nil {
return llvm.Value{}, err
}
return c.getZeroValue(llvmType)
default:
return llvm.Value{}, errors.New("todo: unknown constant: " + expr.String())
}
+9
View File
@@ -71,6 +71,15 @@ func hashmapMake(keySize, valueSize uint8) *hashmap {
}
}
// Return the number of entries in this hashmap, called from the len builtin.
// A nil hashmap is defined as having length 0.
func hashmapLen(m *hashmap) int {
if m == nil {
return 0
}
return int(m.count)
}
// Set a specified key to a given value. Grow the map if necessary.
//go:nobounds
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
+5
View File
@@ -26,6 +26,11 @@ func main() {
readMap(testmap2, "seven")
lookup(testmap2, "eight")
lookup(testmap2, "nokey")
// operations on nil maps
var nilmap map[string]int
println(m == nil, m != nil, len(m))
println(nilmap == nil, nilmap != nil, len(nilmap))
}
func readMap(m map[string]int, key string) {
+2
View File
@@ -48,3 +48,5 @@ map read: seven = 7
twelve = 12
lookup with comma-ok: eight 8 true
lookup with comma-ok: nokey 0 false
false true 2
true false 0