1/*
2 * gomacro - A Go interpreter with Lisp-like macros
3 *
4 * Copyright (C) 2017-2019 Massimiliano Ghilardi
5 *
6 *     This Source Code Form is subject to the terms of the Mozilla Public
7 *     License, v. 2.0. If a copy of the MPL was not distributed with this
8 *     file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 *
11 * compositelit.go
12 *
13 *  Created on May 28, 2017
14 *      Author Massimiliano Ghilardi
15 */
16
17package fast
18
19import (
20	"go/ast"
21	r "reflect"
22
23	. "github.com/cosmos72/gomacro/base"
24	"github.com/cosmos72/gomacro/base/untyped"
25	xr "github.com/cosmos72/gomacro/xreflect"
26)
27
28func (c *Comp) CompositeLit(node *ast.CompositeLit, t xr.Type) *Expr {
29	var ellipsis bool
30	// node.Type is nil when exploiting type inference
31	if node.Type != nil {
32		var et xr.Type
33		et, ellipsis = c.compileType2(node.Type, false)
34		if et != nil {
35			if t == nil || et.AssignableTo(t) {
36				t = et
37			} else {
38				c.Errorf("invalid type for composite literal: <%v> %v, expecting %v", et, node.Type, t)
39			}
40		}
41	}
42	if t == nil {
43		c.Errorf("no explicit type and no inferred type, cannot compile composite literal: %v", node)
44	}
45	switch t.Kind() {
46	case r.Array:
47		return c.compositeLitArray(t, ellipsis, node)
48	case r.Map:
49		return c.compositeLitMap(t, node)
50	case r.Slice:
51		return c.compositeLitSlice(t, node)
52	case r.Struct:
53		return c.compositeLitStruct(t, node)
54	case r.Ptr:
55		switch t.Elem().Kind() {
56		case r.Array, r.Map, r.Slice, r.Struct:
57			return c.addressOf(node, t)
58		}
59	}
60	c.Errorf("invalid type for composite literal: <%v> %v", t, node.Type)
61	return nil
62}
63
64func (c *Comp) compositeLitArray(t xr.Type, ellipsis bool, node *ast.CompositeLit) *Expr {
65	rtype := t.ReflectType()
66	n := len(node.Elts)
67	if n == 0 {
68		return exprX1(t, func(env *Env) r.Value {
69			// array len is already encoded in its type
70			return r.New(rtype).Elem()
71		})
72	}
73	size, keys, funvals := c.compositeLitElements(t, ellipsis, node)
74	if ellipsis {
75		// rebuild type with correct length
76		t = c.Universe.ArrayOf(size, t.Elem())
77		rtype = t.ReflectType()
78	}
79
80	rtval := rtype.Elem()
81	zeroval := r.Zero(rtval)
82
83	return exprX1(t, func(env *Env) r.Value {
84		obj := r.New(rtype).Elem()
85		var val r.Value
86		for i, funval := range funvals {
87			val = funval(env)
88			if val == Nil || val == None {
89				val = zeroval
90			} else if val.Type() != rtval {
91				val = convert(val, rtval)
92			}
93			obj.Index(keys[i]).Set(val)
94		}
95		return obj
96	})
97}
98
99func (c *Comp) compositeLitSlice(t xr.Type, node *ast.CompositeLit) *Expr {
100	rtype := t.ReflectType()
101	n := len(node.Elts)
102	if n == 0 {
103		return exprX1(t, func(env *Env) r.Value {
104			return r.MakeSlice(rtype, 0, 0)
105		})
106	}
107	size, keys, funvals := c.compositeLitElements(t, false, node)
108
109	rtval := rtype.Elem()
110	zeroval := r.Zero(rtval)
111	return exprX1(t, func(env *Env) r.Value {
112		obj := r.MakeSlice(rtype, size, size)
113		var val r.Value
114		for i, funval := range funvals {
115			val = funval(env)
116			if val == Nil || val == None {
117				val = zeroval
118			} else if val.Type() != rtval {
119				val = convert(val, rtval)
120			}
121			obj.Index(keys[i]).Set(val)
122		}
123		return obj
124	})
125}
126
127func (c *Comp) compositeLitElements(t xr.Type, ellipsis bool, node *ast.CompositeLit) (size int, keys []int, funvals []func(*Env) r.Value) {
128	n := len(node.Elts)
129	tval := t.Elem()
130	seen := make(map[int]bool) // indexes already seen
131	keys = make([]int, n)
132	funvals = make([]func(*Env) r.Value, n)
133	size = 0
134	key, lastkey := 0, -1
135
136	for i, el := range node.Elts {
137		elv := el
138		switch elkv := el.(type) {
139		case *ast.KeyValueExpr:
140			ekey := c.Expr1(elkv.Key, nil)
141			if !ekey.Const() {
142				c.Errorf("literal %s index must be non-negative integer constant: %v", t.Kind(), elkv.Key)
143			} else if ekey.Untyped() {
144				key = ekey.ConstTo(c.TypeOfInt()).(int)
145			} else {
146				key = untyped.ConvertLiteralCheckOverflow(ekey.Value, c.TypeOfInt()).(int)
147			}
148			lastkey = key
149			elv = elkv.Value
150		default:
151			lastkey++
152		}
153		if lastkey < 0 {
154			c.Errorf("literal %s index must be non-negative integer constant: %v", t.Kind(), lastkey)
155		} else if !ellipsis && t.Kind() == r.Array && lastkey >= t.Len() {
156			c.Errorf("%s index %d out of bounds [0:%d]", t.Kind(), lastkey, t.Len())
157		} else if seen[lastkey] {
158			c.Errorf("duplicate index in %s literal: %d", t.Kind(), lastkey)
159		}
160		seen[lastkey] = true
161		if size <= lastkey {
162			if lastkey == MaxInt {
163				c.Errorf("literal %s too large: found index == MaxInt", t.Kind())
164			}
165			size = lastkey + 1
166		}
167		keys[i] = lastkey
168
169		eval := c.Expr1(elv, tval)
170		if eval.Const() {
171			eval.ConstTo(tval)
172		} else if !eval.Type.AssignableTo(tval) {
173			c.Errorf("cannot use %v <%v> as type <%v> in %s value", elv, eval.Type, tval, t.Kind())
174		} else {
175			eval.To(c, tval)
176		}
177		funvals[i] = eval.AsX1()
178	}
179	return size, keys, funvals
180}
181
182func (c *Comp) compositeLitMap(t xr.Type, node *ast.CompositeLit) *Expr {
183	rtype := t.ReflectType()
184	n := len(node.Elts)
185	if n == 0 {
186		return exprX1(t, func(env *Env) r.Value {
187			return r.MakeMap(rtype)
188		})
189	}
190	tkey := t.Key()
191	tval := t.Elem()
192
193	seen := make(map[interface{}]bool) // constant keys already seen
194	funkeys := make([]func(*Env) r.Value, n)
195	funvals := make([]func(*Env) r.Value, n)
196
197	for i, el := range node.Elts {
198		switch elkv := el.(type) {
199		case *ast.KeyValueExpr:
200			ekey := c.Expr1(elkv.Key, tkey)
201			if ekey.Const() {
202				ekey.ConstTo(tkey)
203				if seen[ekey.Value] {
204					c.Errorf("duplicate key %v in map literal", elkv.Key)
205				}
206				seen[ekey.Value] = true
207			} else if !ekey.Type.AssignableTo(tkey) {
208				c.Errorf("cannot use %v <%v> as type <%v> in map key", elkv.Key, ekey.Type, tkey)
209			} else {
210				ekey.To(c, tkey)
211			}
212			eval := c.Expr1(elkv.Value, tval)
213			if eval.Const() {
214				eval.ConstTo(tval)
215			} else if !eval.Type.AssignableTo(tval) {
216				c.Errorf("cannot use %v <%v> as type <%v> in map value", elkv.Value, eval.Type, tval)
217			} else {
218				eval.To(c, tval)
219			}
220			funkeys[i] = ekey.AsX1()
221			funvals[i] = eval.AsX1()
222
223		default:
224			c.Errorf("missing key in map literal: %v", el)
225		}
226	}
227	return exprX1(t, func(env *Env) r.Value {
228		obj := r.MakeMap(rtype)
229		var key, val r.Value
230		for i, funkey := range funkeys {
231			key = funkey(env)
232			val = funvals[i](env)
233			obj.SetMapIndex(key, val)
234		}
235		return obj
236	})
237}
238
239func (c *Comp) compositeLitStruct(t xr.Type, node *ast.CompositeLit) *Expr {
240	rtype := t.ReflectType()
241	n := len(node.Elts)
242	if n == 0 {
243		return exprX1(t, func(env *Env) r.Value {
244			return r.New(rtype).Elem()
245		})
246	}
247
248	var seen map[string]bool
249	var all map[string]xr.StructField
250	inits := make([]func(*Env) r.Value, n)
251	indexes := make([]int, n)
252	var flagkv, flagv bool
253
254	for i, el := range node.Elts {
255		switch elkv := el.(type) {
256		case *ast.KeyValueExpr:
257			flagkv = true
258			if flagv {
259				c.Errorf("mixture of field:value and value in struct literal: %v", node)
260			}
261			switch k := elkv.Key.(type) {
262			case *ast.Ident:
263				name := k.Name
264				if seen[name] {
265					c.Errorf("duplicate field name in struct literal: %v", name)
266				} else if seen == nil {
267					seen = make(map[string]bool)
268					all = listStructFields(t, c.FileComp().Path)
269				}
270				field, ok := all[name]
271				if !ok {
272					c.Errorf("unknown field '%v' in struct literal of type %v", name, t)
273				}
274				expr := c.Expr1(elkv.Value, field.Type)
275				if expr.Const() {
276					expr.ConstTo(field.Type)
277				} else if !expr.Type.AssignableTo(field.Type) {
278					c.Errorf("cannot use %v <%v> as type <%v> in field value", elkv.Value, expr.Type, field.Type)
279				} else {
280					expr.To(c, field.Type)
281				}
282				inits[i] = expr.AsX1()
283				indexes[i] = field.Index[0]
284			default:
285				c.Errorf("invalid field name '%v' in struct literal", k)
286			}
287		default:
288			flagv = true
289			if flagkv {
290				c.Errorf("mixture of field:value and value in struct literal: %v", node)
291			}
292			field := t.Field(i)
293			expr := c.Expr1(el, field.Type)
294			if expr.Const() {
295				expr.ConstTo(field.Type)
296			} else if !expr.Type.AssignableTo(field.Type) {
297				c.Errorf("cannot use %v <%v> as type <%v> in field value", el, expr.Type, field.Type)
298			} else {
299				expr.To(c, field.Type)
300			}
301			if !ast.IsExported(field.Name) && field.Pkg.Path() != c.FileComp().Path {
302				c.Errorf("implicit assignment of unexported field '%v' in struct literal <%v>", field.Name, t)
303			}
304			inits[i] = expr.AsX1()
305			indexes[i] = field.Index[0]
306		}
307	}
308	if nfield := t.NumField(); flagv && n != nfield {
309		var label, plural = "few", "s"
310		if n > nfield {
311			label = "many"
312		} else if n == 1 {
313			plural = ""
314		}
315		c.Errorf("too %s values in struct initializer: <%v> has %d fields, found %d initializer%s",
316			label, t, nfield, n, plural)
317	}
318	return exprX1(t, func(env *Env) r.Value {
319		obj := r.New(rtype).Elem()
320		var val, field r.Value
321		var tfield r.Type
322		for i, init := range inits {
323			val = init(env)
324			if val == Nil || val == None {
325				continue
326			}
327			field = obj.Field(indexes[i])
328			tfield = field.Type()
329			if val.Type() != tfield {
330				val = convert(val, tfield)
331			}
332			field.Set(val)
333		}
334		return obj
335	})
336}
337
338// listStructFields lists the field names of a struct. It ignores embedded fields.
339// Unexported fields are listed only if their package's path matches given pkgpath
340func listStructFields(t xr.Type, pkgpath string) map[string]xr.StructField {
341	list := make(map[string]xr.StructField)
342	for i, n := 0, t.NumField(); i < n; i++ {
343		f := t.Field(i)
344		if ast.IsExported(f.Name) || f.Pkg.Path() == pkgpath {
345			list[f.Name] = f
346		}
347	}
348	return list
349}
350