compiler: implement operations on nil hashmaps

* comparing a map against nil
  * getting the length of a nil map
Этот коммит содержится в:
Ayke van Laethem 2018-10-27 00:54:13 +02:00
родитель c84fc6a670
коммит 436901dc49
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
4 изменённых файлов: 31 добавлений и 7 удалений

Просмотреть файл

@ -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())
}

Просмотреть файл

@ -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
testdata/map.go предоставленный
Просмотреть файл

@ -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
testdata/map.txt предоставленный
Просмотреть файл

@ -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