// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ir

// lvalues are the union of addressable expressions and map-index
// expressions.

import (
	"go/ast"
	"go/types"
)

// An lvalue represents an assignable location that may appear on the
// left-hand side of an assignment.  This is a generalization of a
// pointer to permit updates to elements of maps.
type lvalue interface {
	store(fn *Function, v Value, source ast.Node) // stores v into the location
	load(fn *Function, source ast.Node) Value     // loads the contents of the location
	address(fn *Function) Value                   // address of the location
	typ() types.Type                              // returns the type of the location
}

// An address is an lvalue represented by a true pointer.
type address struct {
	addr Value
	expr ast.Expr // source syntax of the value (not address) [debug mode]
}

func (a *address) load(fn *Function, source ast.Node) Value {
	return emitLoad(fn, a.addr, source)
}

func (a *address) store(fn *Function, v Value, source ast.Node) {
	store := emitStore(fn, a.addr, v, source)
	if a.expr != nil {
		// store.Val is v, converted for assignability.
		emitDebugRef(fn, a.expr, store.Val, false)
	}
}

func (a *address) address(fn *Function) Value {
	if a.expr != nil {
		emitDebugRef(fn, a.expr, a.addr, true)
	}
	return a.addr
}

func (a *address) typ() types.Type {
	return deref(a.addr.Type())
}

type compositeElement struct {
	cv   *CompositeValue
	idx  int
	t    types.Type
	expr ast.Expr
}

func (ce *compositeElement) load(fn *Function, source ast.Node) Value {
	panic("not implemented")
}

func (ce *compositeElement) store(fn *Function, v Value, source ast.Node) {
	v = emitConv(fn, v, ce.t, source)
	ce.cv.Values[ce.idx] = v
	if ce.expr != nil {
		// store.Val is v, converted for assignability.
		emitDebugRef(fn, ce.expr, v, false)
	}
}

func (ce *compositeElement) address(fn *Function) Value {
	panic("not implemented")
}

func (ce *compositeElement) typ() types.Type {
	return ce.t
}

// An element is an lvalue represented by m[k], the location of an
// element of a map.  These locations are not addressable
// since pointers cannot be formed from them, but they do support
// load() and store().
type element struct {
	m, k Value      // map
	t    types.Type // map element type
}

func (e *element) load(fn *Function, source ast.Node) Value {
	l := &MapLookup{
		X:     e.m,
		Index: e.k,
	}
	l.setType(e.t)
	return fn.emit(l, source)
}

func (e *element) store(fn *Function, v Value, source ast.Node) {
	up := &MapUpdate{
		Map:   e.m,
		Key:   e.k,
		Value: emitConv(fn, v, e.t, source),
	}
	fn.emit(up, source)
}

func (e *element) address(fn *Function) Value {
	panic("map elements are not addressable")
}

func (e *element) typ() types.Type {
	return e.t
}

// A lazyAddress is an lvalue whose address is the result of an instruction.
// These work like an *address except a new address.address() Value
// is created on each load, store and address call.
// A lazyAddress can be used to control when a side effect (nil pointer
// dereference, index out of bounds) of using a location happens.
type lazyAddress struct {
	addr func(fn *Function) Value // emit to fn the computation of the address
	t    types.Type               // type of the location
	expr ast.Expr                 // source syntax of the value (not address) [debug mode]
}

func (l *lazyAddress) load(fn *Function, source ast.Node) Value {
	load := emitLoad(fn, l.addr(fn), source)
	return load
}

func (l *lazyAddress) store(fn *Function, v Value, source ast.Node) {
	store := emitStore(fn, l.addr(fn), v, source)
	if l.expr != nil {
		// store.Val is v, converted for assignability.
		emitDebugRef(fn, l.expr, store.Val, false)
	}
}

func (l *lazyAddress) address(fn *Function) Value {
	addr := l.addr(fn)
	if l.expr != nil {
		emitDebugRef(fn, l.expr, addr, true)
	}
	return addr
}

func (l *lazyAddress) typ() types.Type { return l.t }

// A blank is a dummy variable whose name is "_".
// It is not reified: loads are illegal and stores are ignored.
type blank struct{}

func (bl blank) load(fn *Function, source ast.Node) Value {
	panic("blank.load is illegal")
}

func (bl blank) store(fn *Function, v Value, source ast.Node) {
	s := &BlankStore{
		Val: v,
	}
	fn.emit(s, source)
}

func (bl blank) address(fn *Function) Value {
	panic("blank var is not addressable")
}

func (bl blank) typ() types.Type {
	// This should be the type of the blank Ident; the typechecker
	// doesn't provide this yet, but fortunately, we don't need it
	// yet either.
	panic("blank.typ is unimplemented")
}
