// Copyright 2016 The CC 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 cc // import "modernc.org/cc"

import (
	"bytes"
	"fmt"
	"math"
	"math/big"
	"sort"
	"strconv"
	"strings"

	"modernc.org/mathutil"
	"modernc.org/xc"
)

type (
	// StringLitID is the type of an Expression.Value representing the numeric
	// ID of a string literal.
	StringLitID int

	// LongStringLitID is the type of an Expression.Value representing the
	// numeric ID of a long string literal.
	LongStringLitID int

	// StringLitID is the type of an Expression.Value representing the numeric
	// ID of a label name used in &&label.
	ComputedGotoID int
)

var (
	maxConvF32I32 = math.Nextafter32(math.MaxInt32, 0) // https://github.com/golang/go/issues/19405
	maxConvF32U32 = math.Nextafter32(math.MaxUint32, 0)
)

// ModelItem is a single item of a model.
//
// Note about StructAlign: To provide GCC ABI compatibility set, for example,
// Align of Double to 8 and StructAlign of Double to 4.
type ModelItem struct {
	Size        int         // Size of the entity in bytes.
	Align       int         // Alignment of the entity when it's not a struct field.
	StructAlign int         // Alignment of the entity when it's a struct field.
	More        interface{} // Optional user data.
}

// Model describes size and align requirements of predeclared types.
type Model struct {
	Items map[Kind]ModelItem

	BoolType              Type
	CharType              Type
	DoubleComplexType     Type
	DoubleType            Type
	FloatComplexType      Type
	FloatType             Type
	IntType               Type
	LongDoubleComplexType Type
	LongDoubleType        Type
	LongLongType          Type
	LongType              Type
	ShortType             Type
	UCharType             Type
	UIntType              Type
	ULongLongType         Type
	ULongType             Type
	UShortType            Type
	UintPtrType           Type
	VoidType              Type
	longStrType           Type
	ptrDiffType           Type
	sizeType              Type
	strType               Type

	initialized bool
	tweaks      *tweaks
	intConvRank [kindMax]int
	Signed      [kindMax]bool // Signed[Kind] reports whether Kind is a signed integer type.
	promoteTo   [kindMax]Kind
}

func (m *Model) initialize(lx *lexer) {
	m.BoolType = m.makeType(lx, 0, tsBool)
	m.CharType = m.makeType(lx, 0, tsChar)
	m.DoubleComplexType = m.makeType(lx, 0, tsComplex, tsDouble)
	m.DoubleType = m.makeType(lx, 0, tsDouble)
	m.FloatComplexType = m.makeType(lx, 0, tsComplex, tsFloat)
	m.FloatType = m.makeType(lx, 0, tsFloat)
	m.IntType = m.makeType(lx, 0, tsInt)
	m.LongDoubleComplexType = m.makeType(lx, 0, tsComplex, tsDouble, tsLong)
	m.LongDoubleType = m.makeType(lx, 0, tsLong, tsDouble)
	m.LongLongType = m.makeType(lx, 0, tsLong, tsLong)
	m.LongType = m.makeType(lx, 0, tsLong)
	m.ShortType = m.makeType(lx, 0, tsShort)
	m.UCharType = m.makeType(lx, 0, tsUnsigned, tsChar)
	m.UIntType = m.makeType(lx, 0, tsUnsigned)
	m.ULongLongType = m.makeType(lx, 0, tsUnsigned, tsLong, tsLong)
	m.ULongType = m.makeType(lx, 0, tsUnsigned, tsLong)
	m.UShortType = m.makeType(lx, 0, tsUnsigned, tsShort)
	m.UintPtrType = m.makeType(lx, 0, tsUintptr) // Pseudo type.
	m.VoidType = m.makeType(lx, 0, tsVoid)
	m.strType = m.makeType(lx, 0, tsChar).Pointer()

	// [0], 6.3.1.1.
	m.intConvRank = [kindMax]int{
		Bool:      1,
		Char:      2,
		SChar:     2,
		UChar:     2,
		Short:     3,
		UShort:    3,
		Int:       4,
		UInt:      4,
		Long:      5,
		ULong:     5,
		LongLong:  6,
		ULongLong: 6,
		UintPtr:   7,
	}
	m.Signed = [kindMax]bool{
		Char:     true,
		SChar:    true,
		Short:    true,
		Int:      true,
		Long:     true,
		LongLong: true,
	}
	m.promoteTo = [kindMax]Kind{}
	for i := range m.promoteTo {
		m.promoteTo[i] = Kind(i)
	}
	switch {
	case m.tweaks.enableWideEnumValues:
		m.intConvRank[Enum] = m.intConvRank[LongLong]
	default:
		m.intConvRank[Enum] = m.intConvRank[Int]
	}
	for k := Kind(0); k < kindMax; k++ {
		r := m.intConvRank[k]
		if r == 0 || r > m.intConvRank[Int] {
			continue
		}

		// k is an integer type whose conversion rank is less than or
		// equal to the rank of int and unsigned int.
		switch {
		case m.Items[k].Size < m.Items[Int].Size || m.Signed[k]:
			// If an int can represent all values of the original
			// type, the value is converted to an int;
			m.promoteTo[k] = Int
		default:
			// otherwise, it is converted to an unsigned int.
			m.promoteTo[k] = UInt
		}
	}

	m.initialized = true
}

func (m *Model) typ(k Kind) Type {
	switch k {
	case Undefined:
		return undefined
	case Bool:
		return m.BoolType
	case Char:
		return m.CharType
	case Double:
		return m.DoubleType
	case Float:
		return m.FloatType
	case Int:
		return m.IntType
	case LongDouble:
		return m.LongDoubleType
	case LongLong:
		return m.LongLongType
	case Long:
		return m.LongType
	case Short:
		return m.ShortType
	case UChar:
		return m.UCharType
	case UInt:
		return m.UIntType
	case ULongLong:
		return m.ULongLongType
	case ULong:
		return m.ULongType
	case UShort:
		return m.UShortType
	case UintPtr:
		return m.UintPtrType
	case FloatComplex:
		return m.FloatComplexType
	case DoubleComplex:
		return m.DoubleComplexType
	case LongDoubleComplex:
		return m.LongDoubleComplexType
	default:
		panic(k)
	}
}

func (m *Model) enumValueToInt(v interface{}) (interface{}, bool) {
	intSize := m.Items[Int].Size
	if m.tweaks.enableWideEnumValues {
		intSize = m.Items[LongLong].Size
	}
	switch x := v.(type) {
	case byte, int8, int16, uint16, int32:
		return m.MustConvert(x, m.IntType), true
	case uint32:
		switch intSize {
		case 4:
			return m.MustConvert(x, m.IntType), x <= math.MaxUint32
		case 8:
			return m.MustConvert(x, m.IntType), true
		default:
			panic(intSize)
		}
	case int64:
		switch intSize {
		case 4:
			return m.MustConvert(x, m.IntType), x <= math.MaxUint32
		case 8:
			return m.MustConvert(x, m.IntType), true
		default:
			panic(intSize)
		}
	case uint64:
		switch intSize {
		case 4:
			return m.MustConvert(x, m.IntType), x <= math.MaxUint32
		case 8:
			return m.MustConvert(x, m.IntType), x <= math.MaxUint64
		default:
			panic(intSize)
		}
	default:
		panic(fmt.Errorf("%T", x))
	}
}

// sanityCheck reports model errors, if any.
func (m *Model) sanityCheck() error {
	if len(m.Items) == 0 {
		return fmt.Errorf("model has no items")
	}

	tab := map[Kind]struct {
		minSize, maxSize   int
		minAlign, maxAlign int
	}{
		Ptr:               {4, 8, 4, 8},
		UintPtr:           {4, 8, 4, 8},
		Void:              {0, 0, 1, 1},
		Char:              {1, 1, 1, 1},
		SChar:             {1, 1, 1, 1},
		UChar:             {1, 1, 1, 1},
		Short:             {2, 2, 2, 2},
		UShort:            {2, 2, 2, 2},
		Int:               {4, 4, 4, 4},
		UInt:              {4, 4, 4, 4},
		Long:              {4, 8, 4, 8},
		ULong:             {4, 8, 4, 8},
		LongLong:          {8, 8, 8, 8},
		ULongLong:         {8, 8, 8, 8},
		Float:             {4, 4, 4, 4},
		Double:            {8, 8, 8, 8},
		LongDouble:        {8, 16, 8, 16},
		Bool:              {1, 1, 1, 1},
		FloatComplex:      {8, 8, 8, 8},
		DoubleComplex:     {16, 16, 8, 16},
		LongDoubleComplex: {16, 32, 8, 16},
	}
	a := []int{}
	required := map[Kind]bool{}
	seen := map[Kind]bool{}
	for k := range tab {
		required[k] = true
		a = append(a, int(k))
	}
	sort.Ints(a)
	for k, v := range m.Items {
		if seen[k] {
			return fmt.Errorf("model has duplicate item: %s", k)
		}

		seen[k] = true
		if !required[k] {
			return fmt.Errorf("model has invalid type: %s: %#v", k, v)
		}

		for typ, t := range tab {
			if typ == k {
				if v.Size < t.minSize {
					return fmt.Errorf("size %d too small: %s", v.Size, k)
				}

				if v.Size > t.maxSize {
					return fmt.Errorf("size %d too big: %s", v.Size, k)
				}

				if v.Size != 0 && mathutil.PopCount(v.Size) != 1 {
					return fmt.Errorf("size %d is not a power of two: %s", v.Size, k)
				}

				if v.Align < t.minAlign {
					return fmt.Errorf("align %d too small: %s", v.Align, k)
				}

				if v.Align > t.maxAlign {
					return fmt.Errorf("align %d too big: %s", v.Align, k)
				}

				if v.Align < v.Size && v.Align < t.minAlign {
					return fmt.Errorf("align is smaller than size: %s", k)
				}

				if v.StructAlign < 1 {
					return fmt.Errorf("struct align %d too small: %s", v.StructAlign, k)
				}

				if v.StructAlign > t.maxAlign {
					return fmt.Errorf("struct align %d too big: %s", v.Align, k)
				}

				if mathutil.PopCount(v.Align) != 1 {
					return fmt.Errorf("align %d is not a power of two: %s", v.Align, k)
				}

				break
			}
		}
	}
	w := m.Items[Ptr].Size
	if m.Items[Short].Size < w &&
		m.Items[Int].Size < w &&
		m.Items[Long].Size < w &&
		m.Items[LongLong].Size < w {
		return fmt.Errorf("model has no integer type suitable for pointer difference and sizeof")
	}

	for _, typ := range a {
		if !seen[Kind(typ)] {
			return fmt.Errorf("model has no item for type %s", Kind(typ))
		}
	}

	if g, e := w, m.Items[UintPtr].Size; g != e {
		return fmt.Errorf("model uintptr has different sizes than ptr has")
	}
	return nil
}

// MustConvert returns v converted to the type of typ. If the conversion is
// impossible, the method panics.
//
// Conversion an integer type to any pointer type yields an uintptr.
func (m *Model) MustConvert(v interface{}, typ Type) interface{} {
	if typ.Kind() == Enum {
		typ = m.IntType
	}
	mi, ok := m.Items[typ.Kind()]
	if !ok && typ.Kind() != Function {
		panic(fmt.Errorf("internal error: no model item for %s, %s", typ, typ.Kind()))
	}

	w := mi.Size
	switch typ.Kind() {
	case Short:
		switch x := v.(type) {
		case int32:
			switch w {
			case 2:
				return int16(x)
			default:
				panic(w)
			}
		case int64:
			switch w {
			case 2:
				return int16(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case UShort:
		switch x := v.(type) {
		case uint16:
			switch w {
			case 2:
				return x
			default:
				panic(w)
			}
		case int32:
			switch w {
			case 2:
				return uint16(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 2:
				return uint16(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case Int:
		switch x := v.(type) {
		case int8:
			switch w {
			case 4:
				return int32(x)
			default:
				panic(w)
			}
		case byte:
			switch w {
			case 4:
				return int32(x)
			default:
				panic(w)
			}
		case int16:
			switch w {
			case 4:
				return int32(x)
			default:
				panic(w)
			}
		case uint16:
			switch w {
			case 4:
				return int32(x)
			default:
				panic(w)
			}
		case int32:
			switch w {
			case 4:
				return x
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 4:
				return int32(x)
			default:
				panic(w)
			}
		case int64:
			switch w {
			case 4:
				return int32(x)
			default:
				panic(w)
			}
		case uint64:
			switch w {
			case 4:
				return int32(x)
			default:
				panic(w)
			}
		case float32:
			switch w {
			case 4:
				switch {
				case x > maxConvF32I32:
					return int32(math.MaxInt32)
				default:
					return int32(x)
				}
			default:
				panic(w)
			}
		case float64:
			switch w {
			case 4:
				return int32(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case UInt:
		switch x := v.(type) {
		case uint8:
			switch w {
			case 4:
				return uint32(x)
			default:
				panic(w)
			}
		case int16:
			switch w {
			case 4:
				return uint32(x)
			default:
				panic(w)
			}
		case uint16:
			switch w {
			case 4:
				return uint32(x)
			default:
				panic(w)
			}
		case int32:
			switch w {
			case 4:
				return uint32(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 4:
				return x
			default:
				panic(w)
			}
		case int64:
			switch w {
			case 4:
				return uint32(x)
			default:
				panic(w)
			}
		case uint64:
			switch w {
			case 4:
				return uint32(x)
			default:
				panic(w)
			}
		case uintptr:
			switch w {
			case 4:
				return uint32(x)
			default:
				panic(w)
			}
		case float32:
			switch w {
			case 4:
				switch {
				case x > maxConvF32U32:
					return uint32(math.MaxUint32)
				default:
					return uint32(x)
				}
			default:
				panic(w)
			}
		case float64:
			switch w {
			case 4:
				return uint32(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case Long:
		switch x := v.(type) {
		case int16:
			switch w {
			case 4:
				return int32(x)
			case 8:
				return int64(x)
			default:
				panic(w)
			}
		case int32:
			switch w {
			case 4:
				return x
			case 8:
				return int64(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 4:
				return int32(x)
			case 8:
				return int64(x)
			default:
				panic(w)
			}
		case int64:
			switch w {
			case 4:
				return int32(x)
			case 8:
				return x
			default:
				panic(w)
			}
		case uint64:
			switch w {
			case 4:
				return int32(x)
			case 8:
				return int64(x)
			default:
				panic(w)
			}
		case uintptr:
			switch w {
			case 4:
				return int32(x)
			case 8:
				return int64(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case LongLong:
		switch x := v.(type) {
		case int32:
			switch w {
			case 8:
				return int64(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 8:
				return int64(x)
			default:
				panic(w)
			}
		case int64:
			switch w {
			case 8:
				return x
			default:
				panic(w)
			}
		case uint64:
			switch w {
			case 8:
				return int64(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case ULong:
		switch x := v.(type) {
		case uint8:
			switch w {
			case 4:
				return uint32(x)
			case 8:
				return uint64(x)
			default:
				panic(w)
			}
		case int:
			switch w {
			case 4:
				return uint32(x)
			case 8:
				return uint64(x)
			default:
				panic(w)
			}
		case int32:
			switch w {
			case 4:
				return uint32(x)
			case 8:
				return uint64(x)
			default:
				panic(w)
			}
		case int64:
			switch w {
			case 4:
				return uint32(x)
			case 8:
				return uint64(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 4:
				return x
			case 8:
				return uint64(x)
			default:
				panic(w)
			}
		case uint64:
			switch w {
			case 4:
				return uint32(x)
			case 8:
				return x
			default:
				panic(w)
			}
		case uintptr:
			switch w {
			case 4:
				return uint32(x)
			case 8:
				return uint64(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case ULongLong:
		switch x := v.(type) {
		case int32:
			switch w {
			case 8:
				return uint64(x)
			default:
				panic(w)
			}
		case int64:
			switch w {
			case 8:
				return uint64(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 8:
				return uint64(x)
			default:
				panic(w)
			}
		case uint64:
			switch w {
			case 8:
				return x
			default:
				panic(w)
			}
		case uintptr:
			switch w {
			case 8:
				return uint64(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case Float:
		switch x := v.(type) {
		case int32:
			switch w {
			case 4:
				return float32(x)
			case 8:
				return float64(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 4:
				return float32(x)
			case 8:
				return float64(x)
			default:
				panic(w)
			}
		case int64:
			switch w {
			case 4:
				return float32(x)
			case 8:
				return float64(x)
			default:
				panic(w)
			}
		case uint64:
			switch w {
			case 4:
				return float32(x)
			case 8:
				return float64(x)
			default:
				panic(w)
			}
		case float32:
			switch w {
			case 4:
				return x
			case 8:
				return float64(x)
			default:
				panic(w)
			}
		case float64:
			switch w {
			case 4:
				return float32(x)
			case 8:
				return x
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case Double:
		switch x := v.(type) {
		case int32:
			switch w {
			case 8:
				return float64(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 8:
				return float64(x)
			default:
				panic(w)
			}
		case int64:
			switch w {
			case 8:
				return float64(x)
			default:
				panic(w)
			}
		case uint64:
			switch w {
			case 8:
				return float64(x)
			default:
				panic(w)
			}
		case float32:
			switch w {
			case 8:
				return float64(x)
			default:
				panic(w)
			}
		case float64:
			switch w {
			case 8:
				return x
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case Ptr, Function:
		switch x := v.(type) {
		case int32:
			return uintptr(x)
		case uint32:
			return uintptr(x)
		case int64:
			return uintptr(x)
		case uint64:
			return uintptr(x)
		case uintptr:
			return x
		case StringLitID:
			return nil
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case Void:
		return nil
	case Char, SChar:
		switch x := v.(type) {
		case int32:
			switch w {
			case 1:
				return int8(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 1:
				return int8(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case UChar:
		switch x := v.(type) {
		case uint8:
			switch w {
			case 1:
				return x
			default:
				panic(w)
			}
		case int32:
			switch w {
			case 1:
				return byte(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 1:
				return byte(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case UintPtr:
		switch x := v.(type) {
		case int32:
			switch w {
			case 4, 8:
				return uintptr(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 4:
				return uintptr(x)
			default:
				panic(w)
			}
		case uint64:
			switch w {
			case 8:
				return uintptr(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case LongDouble:
		switch x := v.(type) {
		case int32:
			switch w {
			case 8, 16:
				return float64(x)
			default:
				panic(w)
			}
		case uint32:
			switch w {
			case 8, 16:
				return float64(x)
			default:
				panic(w)
			}
		case int64:
			switch w {
			case 8, 16:
				return float64(x)
			default:
				panic(w)
			}
		case uint64:
			switch w {
			case 8, 16:
				return float64(x)
			default:
				panic(w)
			}
		case float32:
			switch w {
			case 8, 16:
				return float64(x)
			default:
				panic(w)
			}
		case float64:
			switch w {
			case 8, 16:
				return x
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case Bool:
		switch x := v.(type) {
		case int32:
			if x != 0 {
				return int32(1)
			}

			return int32(0)
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case FloatComplex:
		switch x := v.(type) {
		case float32:
			switch w {
			case 8:
				return complex(x, 0)
			default:
				panic(w)
			}
		case float64:
			switch w {
			case 8:
				return complex(float32(x), 0)
			default:
				panic(w)
			}
		case complex64:
			switch w {
			case 8:
				return x
			default:
				panic(w)
			}
		case complex128:
			switch w {
			case 8:
				return complex64(x)
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case DoubleComplex:
		switch x := v.(type) {
		case int32:
			switch w {
			case 16:
				return complex(float64(x), 0)
			default:
				panic(w)
			}
		case float64:
			switch w {
			case 16:
				return complex(x, 0)
			default:
				panic(w)
			}
		case complex128:
			switch w {
			case 16:
				return x
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	case LongDoubleComplex:
		switch x := v.(type) {
		case float64:
			switch w {
			case 16:
				return complex(x, 0)
			default:
				panic(w)
			}
		case complex128:
			switch w {
			case 16:
				return x
			default:
				panic(w)
			}
		default:
			panic(fmt.Errorf("internal error %T", x))
		}
	default:
		panic(fmt.Errorf("internal error %s, %s", typ, typ.Kind()))
	}
}

func (m *Model) value2(v interface{}, typ Type) (interface{}, Type) {
	return m.MustConvert(v, typ), typ
}

func (m *Model) charConst(lx *lexer, t xc.Token) (interface{}, Type) {
	switch t.Rune {
	case CHARCONST:
		s := string(t.S())
		typ := m.IntType
		var r rune
		s = s[1 : len(s)-1] // Remove outer 's.
		if len(s) == 1 {
			return rune(s[0]), m.IntType
		}

		runes := []rune(s)
		switch runes[0] {
		case '\\':
			r, _ = decodeEscapeSequence(runes)
			if r < 0 {
				r = -r
			}
		default:
			r = runes[0]
		}
		return r, typ
	case LONGCHARCONST:
		s := t.S()
		typ := m.LongType
		var buf bytes.Buffer
		s = s[2 : len(s)-1]
		runes := []rune(string(s))
		for i := 0; i < len(runes); {
			switch r := runes[i]; {
			case r == '\\':
				r, n := decodeEscapeSequence(runes[i:])
				switch {
				case r < 0:
					buf.WriteByte(byte(-r))
				default:
					buf.WriteRune(r)
				}
				i += n
			default:
				buf.WriteByte(byte(r))
				i++
			}
		}
		s = buf.Bytes()
		runes = []rune(string(s))
		if len(runes) != 1 {
			lx.report.Err(t.Pos(), "invalid character literal %s", t.S())
			return 0, typ
		}

		return runes[0], typ
	default:
		panic("internal error")
	}
}

func (m *Model) getSizeType(lx *lexer) Type {
	if t := m.sizeType; t != nil {
		return t
	}

	b := lx.scope.Lookup(NSIdentifiers, xc.Dict.SID("size_t"))
	if b.Node == nil {
		w := m.Items[Ptr].Size
		switch {
		case m.Items[Short].Size >= w:
			return m.ShortType
		case m.Items[Int].Size >= w:
			return m.IntType
		case m.Items[Long].Size >= w:
			return m.LongType
		default:
			return m.LongLongType
		}
	}

	d := b.Node.(*DirectDeclarator)
	if !d.TopDeclarator().RawSpecifier().IsTypedef() {
		lx.report.Err(d.Pos(), "size_t is not a typedef name")
		m.sizeType = undefined
		return undefined
	}

	m.sizeType = b.Node.(*DirectDeclarator).top().declarator.Type
	return m.sizeType
}

func (m *Model) getPtrDiffType(lx *lexer) Type {
	if t := m.ptrDiffType; t != nil {
		return t
	}

	b := lx.scope.Lookup(NSIdentifiers, xc.Dict.SID("ptrdiff_t"))
	if b.Node == nil {
		w := m.Items[Ptr].Size
		switch {
		case m.Items[Short].Size >= w:
			return m.ShortType
		case m.Items[Int].Size >= w:
			return m.IntType
		case m.Items[Long].Size >= w:
			return m.LongType
		default:
			return m.LongLongType
		}
	}

	d := b.Node.(*DirectDeclarator)
	if !d.TopDeclarator().RawSpecifier().IsTypedef() {
		lx.report.Err(d.Pos(), "ptrdiff_t is not a typedef name")
		m.ptrDiffType = undefined
		return undefined
	}

	m.ptrDiffType = b.Node.(*DirectDeclarator).top().declarator.Type
	return m.ptrDiffType
}

func (m *Model) getLongStrType(lx *lexer, tok xc.Token) Type {
	if t := m.longStrType; t != nil {
		return t
	}

	b := lx.scope.Lookup(NSIdentifiers, xc.Dict.SID("wchar_t"))
	if b.Node == nil {
		m.longStrType = m.IntType.Pointer()
		return m.longStrType
	}

	d := b.Node.(*DirectDeclarator)
	if !d.TopDeclarator().RawSpecifier().IsTypedef() {
		lx.report.Err(d.Pos(), "wchar_t is not a typedef name")
		m.longStrType = undefined
		return m.longStrType
	}

	m.longStrType = b.Node.(*DirectDeclarator).top().declarator.Type.Pointer()
	return m.longStrType
}

func (m *Model) strConst(lx *lexer, t xc.Token) (interface{}, Type) {
	s := t.S()
	typ := m.strType
	var buf bytes.Buffer
	switch t.Rune {
	case LONGSTRINGLITERAL:
		typ = m.getLongStrType(lx, t)
		s = s[1:] // Remove leading 'L'.
		fallthrough
	case STRINGLITERAL:
		s = s[1 : len(s)-1] // Remove outer "s.
		runes := []rune(string(s))
		for i := 0; i < len(runes); {
			switch r := runes[i]; {
			case r == '\\':
				r, n := decodeEscapeSequence(runes[i:])
				switch {
				case r < 0:
					buf.WriteByte(byte(-r))
				default:
					buf.WriteRune(r)
				}
				i += n
			default:
				buf.WriteByte(byte(r))
				i++
			}
		}
	default:
		panic("internal error")
	}
	s = buf.Bytes()
	switch t.Rune {
	case LONGSTRINGLITERAL:
		return LongStringLitID(xc.Dict.ID(s)), typ
	case STRINGLITERAL:
		return StringLitID(xc.Dict.ID(s)), typ
	default:
		panic("internal error")
	}
}

func (m *Model) floatConst(lx *lexer, t xc.Token) (interface{}, Type) {
	const (
		f = 1 << iota
		j
		l
	)
	k := 0
	s := t.S()
	i := len(s) - 1
more:
	switch c := s[i]; c {
	case 'i', 'j':
		k |= j
		i--
		goto more
	case 'l', 'L':
		k |= l
		i--
		goto more
	case 'f', 'F':
		k |= f
		i--
		goto more
	}
	if k&j != 0 && !lx.tweaks.enableImaginarySuffix {
		lx.report.Err(t.Pos(), "imaginary suffixes not enabled")
		k &^= j
	}
	ss := string(s[:i+1])
	var v float64
	var err error
	switch {
	case strings.Contains(ss, "p"):
		var bf *big.Float
		bf, _, err = big.ParseFloat(ss, 0, 53, big.ToNearestEven)
		switch {
		case err != nil:
			lx.report.Err(t.Pos(), "invalid floating point constant %s", ss)
			v = 0
		default:
			v, _ = bf.Float64()
		}
	default:
		v, err = strconv.ParseFloat(ss, 64)
	}
	if err != nil {
		lx.report.Err(t.Pos(), "invalid floating point constant %s", ss)
		v = 0
	}
	switch k {
	case 0:
		return m.value2(v, m.DoubleType)
	case l:
		return m.value2(v, m.LongDoubleType)
	case j:
		return m.value2(complex(0, v), m.DoubleComplexType)
	case j | l:
		return m.value2(complex(0, v), m.LongDoubleComplexType)
	case f:
		return m.value2(v, m.FloatType)
	case f | j:
		return m.value2(complex(0, v), m.FloatComplexType)
	default:
		lx.report.Err(t.Pos(), "invalid literal %s", t.S())
		return 0.0, m.DoubleType
	}
}

func (m *Model) intConst(lx *lexer, t xc.Token) (interface{}, Type) {
	const (
		l = 1 << iota
		ll
		u
	)
	k := 0
	s := t.S()
	i := len(s) - 1
more:
	switch c := s[i]; c {
	case 'u', 'U':
		k |= u
		i--
		goto more
	case 'l', 'L':
		if i > 0 && (s[i-1] == 'l' || s[i-1] == 'L') {
			k |= ll
			i -= 2
			goto more
		}

		k |= l
		i--
		goto more
	}
	n, err := strconv.ParseUint(string(s[:i+1]), 0, 64)
	if err != nil {
		lx.report.Err(t.Pos(), "invalid integer constant: %s", s)
	}

	switch k {
	case 0:
		switch b := mathutil.BitLenUint64(n); {
		case b < 32:
			return m.value2(n, m.IntType)
		case b < 33:
			return m.value2(n, m.UIntType)
		case b < 64:
			if m.Items[Long].Size == 8 {
				return m.value2(n, m.LongType)
			}

			return m.value2(n, m.LongLongType)
		default:
			if m.Items[ULong].Size == 8 {
				return m.value2(n, m.ULongType)
			}

			return m.value2(n, m.ULongLongType)
		}
	case l:
		return m.value2(n, m.LongType)
	case ll:
		return m.value2(n, m.LongLongType)
	case u:
		return m.value2(n, m.UIntType)
	case u | l:
		return m.value2(n, m.ULongType)
	case u | ll:
		return m.value2(n, m.ULongLongType)
	default:
		panic("internal error")
	}
}

func (m *Model) cBool(v bool) interface{} {
	if v {
		return m.MustConvert(int32(1), m.IntType)

	}
	return m.MustConvert(int32(0), m.IntType)
}

func (m *Model) binOp(lx *lexer, a, b operand) (interface{}, interface{}, Type) {
	av, at := a.eval(lx)
	bv, bt := b.eval(lx)
	t := at
	if IsArithmeticType(at) && IsArithmeticType(bt) {
		t = m.BinOpType(at, bt)
	}
	if av == nil || bv == nil || t.Kind() == Undefined {
		return nil, nil, t
	}

	return m.MustConvert(av, t), m.MustConvert(bv, t), t
}

// BinOpType returns the evaluation type of a binop b, ie. the type operands
// are converted to before performing the operation. Operands must be
// arithmetic types.
//
// See [0], 6.3.1.8 - Usual arithmetic conversions.
func (m *Model) BinOpType(a, b Type) Type {
	ak := a.Kind()
	bk := b.Kind()

	if ak == LongDoubleComplex || bk == LongDoubleComplex {
		return m.LongDoubleComplexType
	}

	if ak == DoubleComplex || bk == DoubleComplex {
		return m.DoubleComplexType
	}

	if ak == FloatComplex || bk == FloatComplex {
		return m.FloatComplexType
	}

	// First, if the corresponding real type of either operand is long
	// double, the other operand is converted, without change of type
	// domain, to a type whose corresponding real type is long double.
	if ak == LongDouble || bk == LongDouble {
		return m.LongDoubleType
	}

	// Otherwise, if the corresponding real type of either operand is
	// double, the other operand is converted, without change of type
	// domain, to a type whose corresponding real type is double.
	if ak == Double || bk == Double {
		return m.DoubleType
	}

	// Otherwise, if the corresponding real type of either operand is float, the other
	// operand is converted, without change of type domain, to a type whose
	// corresponding real type is float.
	if ak == Float || bk == Float {
		return m.FloatType
	}

	// Otherwise, the integer promotions are performed on both operands.
	ak = m.promoteTo[ak]
	bk = m.promoteTo[bk]

	// Then the following rules are applied to the promoted operands:
	ar := m.intConvRank[ak]
	br := m.intConvRank[bk]

	// If both operands have the same type, then no further conversion is
	// needed.
	if ak == bk {
		return m.typ(ak)
	}

	// Otherwise, if both operands have signed integer types or both have
	// unsigned integer types, the operand with the type of lesser integer
	// conversion rank is converted to the type of the operand with greater
	// rank.
	if m.Signed[ak] == m.Signed[bk] {
		switch {
		case ar < br:
			return m.typ(bk)
		default:
			return m.typ(ak)
		}
	}

	// Make a the unsigned type and b the signed type.
	if m.Signed[ak] {
		a, b = b, a
		ak, bk = bk, ak
		ar, br = br, ar
	}

	// Otherwise, if the operand that has unsigned integer type has rank
	// greater or equal to the rank of the type of the other operand, then
	// the operand with signed integer type is converted to the type of the
	// operand with unsigned integer type.
	if ar >= br {
		return m.typ(ak)
	}

	// Otherwise, if the type of the operand with signed integer type can
	// represent all of the values of the type of the operand with unsigned
	// integer type, then the operand with unsigned integer type is
	// converted to the type of the operand with signed integer type.
	as := m.Items[ak].Size
	bs := m.Items[bk].Size
	if bs > as {
		return m.typ(bk)
	}

	// Otherwise, both operands are converted to the unsigned integer type
	// corresponding to the type of the operand with signed integer type.
	return m.typ(unsigned(bk))
}

func (m *Model) promote(t Type) Type {
	if !IsIntType(t) {
		return t
	}

	return m.BinOpType(t, t)
}

func (m *Model) makeType(lx *lexer, attr int, ts ...int) Type {
	d := m.makeDeclarator(attr, ts...)
	return d.setFull(lx)
}

func (m *Model) makeDeclarator(attr int, ts ...int) *Declarator {
	s := &spec{attr, tsEncode(ts...)}
	d := &Declarator{specifier: s}
	dd := &DirectDeclarator{declarator: d, specifier: s}
	d.DirectDeclarator = dd
	return d
}

func (m *Model) checkArithmeticType(lx *lexer, a ...operand) (r bool) {
	r = true
	for _, v := range a {
		_, t := v.eval(lx)
		if !IsArithmeticType(t) {
			lx.report.Err(v.Pos(), "not an arithmetic type (have '%s')", t)
			r = false
		}
	}
	return r
}

func (m *Model) checkIntegerOrBoolType(lx *lexer, a ...operand) (r bool) {
	r = true
	for _, v := range a {
		_, t := v.eval(lx)
		if !IsIntType(t) && !(t.Kind() == Bool) {
			lx.report.Err(v.Pos(), "not an integer or bool type (have '%s')", t)
			r = false
		}
	}
	return r
}
