diff options
Diffstat (limited to 'vendor/modernc.org/cc/v3/check.go')
-rw-r--r-- | vendor/modernc.org/cc/v3/check.go | 5268 |
1 files changed, 5268 insertions, 0 deletions
diff --git a/vendor/modernc.org/cc/v3/check.go b/vendor/modernc.org/cc/v3/check.go new file mode 100644 index 00000000..50887870 --- /dev/null +++ b/vendor/modernc.org/cc/v3/check.go @@ -0,0 +1,5268 @@ +// Copyright 2019 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/v3" + +import ( + "fmt" + "go/token" + "math" + "math/big" + "math/bits" + "path/filepath" + "strconv" + "strings" + + "modernc.org/mathutil" + "modernc.org/strutil" +) + +const longDoublePrec = 256 + +type mode = int + +var ( + idBuiltinConstantPImpl = dict.sid("__builtin_constant_p_impl") + idClosure = dict.sid("0closure") // Must be invalid indentifier. + idWcharT = dict.sid("wchar_t") + idWinWchar = dict.sid("WCHAR") + + _ fmt.State +) + +const ( + // [2], 6.6 Constant expressions, 6 + // + // An integer constant expression shall have integer type and shall + // only have operands that are integer constants, enumeration + // constants, character constants, sizeof expressions whose results are + // integer constants, _Alignof expressions, and floating constants that + // are the immediate operands of casts. Cast operators in an integer + // constant expression shall only convert arithmetic types to integer + // types, except as part of an operand to the sizeof or _Alignof + // operator. + mIntConstExpr = 1 << iota + + mIntConstExprFloat // As mIntConstExpr plus accept floating point constants. + mIntConstExprAnyCast // As mIntConstExpr plus accept any cast. +) + +// Parameter represents a function parameter. +type Parameter struct { + d *Declarator + typ Type +} + +// NewParameter returns a newly created parameter +func NewParameter(d *Declarator, t Type) *Parameter { + return &Parameter{d, t} +} + +func (p *Parameter) Declarator() *Declarator { return p.d } +func (p *Parameter) Name() StringID { return p.d.Name() } +func (p *Parameter) Type() Type { return p.typ } + +func (n *TranslationUnit) check(ctx *context) { + for n := n; n != nil; n = n.TranslationUnit { + n.ExternalDeclaration.check(ctx) + } + for ; n != nil; n = n.TranslationUnit { + n.ExternalDeclaration.checkFnBodies(ctx) + } +} + +func (n *ExternalDeclaration) checkFnBodies(ctx *context) { + if n == nil { + return + } + + switch n.Case { + case ExternalDeclarationFuncDef: // FunctionDefinition + n.FunctionDefinition.checkBody(ctx) + } +} + +// https://gcc.gnu.org/onlinedocs/gcc/Inline.html +// +// If you specify both inline and extern in the function definition, then the +// definition is used only for inlining. In no case is the function compiled on +// its own, not even if you refer to its address explicitly. Such an address +// becomes an external reference, as if you had only declared the function, and +// had not defined it. +// +// This combination of inline and extern has almost the effect of a macro. The +// way to use it is to put a function definition in a header file with these +// keywords, and put another copy of the definition (lacking inline and extern) +// in a library file. The definition in the header file causes most calls to +// the function to be inlined. If any uses of the function remain, they refer +// to the single copy in the library. +func (n *Declarator) isExternInline() bool { + return n.IsExtern() && n.Type() != nil && n.Type().Inline() +} + +// DeclarationSpecifiers Declarator DeclarationList CompoundStatement +func (n *FunctionDefinition) checkBody(ctx *context) { + if n == nil { + return + } + + if n.checked { + return + } + + n.checked = true + if n.Declarator.isExternInline() && !ctx.cfg.CheckExternInlineFnBodies { + return + } + + ctx.checkFn = n + rd := ctx.readDelta + ctx.readDelta = 1 + n.CompoundStatement.check(ctx) + ctx.checkFn = nil + for k, v := range n.ComputedGotos { + if _, ok := n.Labels[k]; !ok { + ctx.errNode(v, "label %s undefined", k) + } + } + for k, v := range n.Gotos { + if _, ok := n.Labels[k]; !ok { + ctx.errNode(v, "label %s undefined", k) + } + } + for _, n := range n.InitDeclarators { + d := n.Declarator + if d.Type().IsIncomplete() && d.Linkage != External { + ctx.errNode(d, "declarator has incomplete type") + } + if ctx.cfg.RejectUninitializedDeclarators && d.Linkage == None && d.Write == 0 && !d.AddressTaken && d.Read != 0 { + switch d.Type().Kind() { + case Array, Struct, Union, Invalid: + // nop + default: + ctx.errNode(d, "%s may be used uninitialized in this function", d.Name()) + } + } + } + for _, n := range n.CompositeLiterals { + switch t := n.Operand.Type(); t.Kind() { + case Invalid: + ctx.errNode(n, "composite literal has invalid type") + default: + if t.IsIncomplete() { + ctx.errNode(n, "composite literal has incomplete type") + } + } + } + ctx.readDelta = rd +} + +func (n *ExternalDeclaration) check(ctx *context) { + if n == nil { + return + } + + switch n.Case { + case ExternalDeclarationFuncDef: // FunctionDefinition + n.FunctionDefinition.checkDeclarator(ctx) + case ExternalDeclarationDecl: // Declaration + n.Declaration.check(ctx, true) + case ExternalDeclarationAsm: // AsmFunctionDefinition + n.AsmFunctionDefinition.check(ctx) + case ExternalDeclarationAsmStmt: // AsmStatement + n.AsmStatement.check(ctx) + case ExternalDeclarationEmpty: // ';' + // nop + case ExternalDeclarationPragma: // PragmaSTDC + n.PragmaSTDC.check(ctx) + default: + panic(todo("")) + } +} + +func (n *PragmaSTDC) check(ctx *context) { + // nop +} + +func (n *AsmFunctionDefinition) check(ctx *context) { + if n == nil { + return + } + + typ, inline, noret := n.DeclarationSpecifiers.check(ctx, false) + typ.setFnSpecs(inline, noret) + n.Declarator.check(ctx, n.DeclarationSpecifiers, typ, true) + n.AsmStatement.check(ctx) +} + +func (n *AsmStatement) check(ctx *context) { + if n == nil { + return + } + + n.Asm.check(ctx) + n.AttributeSpecifierList.check(ctx, nil) +} + +func (n *Declaration) check(ctx *context, tld bool) { + if n == nil { + return + } + + typ, _, _ := n.DeclarationSpecifiers.check(ctx, false) + n.InitDeclaratorList.check(ctx, n.DeclarationSpecifiers, typ, tld) +} + +func (n *InitDeclaratorList) check(ctx *context, td typeDescriptor, typ Type, tld bool) { + for ; n != nil; n = n.InitDeclaratorList { + n.AttributeSpecifierList.check(ctx, typ.baseP()) + n.InitDeclarator.check(ctx, td, typ, tld) + } +} + +func (n *InitDeclarator) check(ctx *context, td typeDescriptor, typ Type, tld bool) { + if n == nil { + return + } + + if f := ctx.checkFn; f != nil { + f.InitDeclarators = append(f.InitDeclarators, n) + } + if attr := n.AttributeSpecifierList.check(ctx, typ.baseP()); len(attr) != 0 { + typ = &attributedType{typ, attr} + } + switch n.Case { + case InitDeclaratorDecl: // Declarator AttributeSpecifierList + n.Declarator.check(ctx, td, typ, tld) + case InitDeclaratorInit: // Declarator AttributeSpecifierList '=' Initializer + typ := n.Declarator.check(ctx, td, typ, tld) + n.Declarator.hasInitializer = true + n.Declarator.Write++ + n.Initializer.check(ctx, &n.Initializer.list, typ, n.Declarator.StorageClass, nil, 0, nil, nil, false) + n.Initializer.setConstZero() + n.initializer = &InitializerValue{typ: typ, initializer: n.Initializer} + if ctx.cfg.TrackAssignments { + setLHS(map[*Declarator]struct{}{n.Declarator: {}}, n.Initializer) + } + default: + panic(todo("")) + } +} + +func (n *Initializer) setConstZero() { + switch n.Case { + case InitializerExpr: // AssignmentExpression + if op := n.AssignmentExpression.Operand; op != nil { + n.isConst = op.IsConst() + n.isZero = op.IsZero() + } + case InitializerInitList: // '{' InitializerList ',' '}' + li := n.InitializerList + li.setConstZero() + n.isConst = li.IsConst() + n.isZero = li.IsZero() + default: + panic(todo("%v:", n.Position())) + } +} + +func (n *InitializerList) setConstZero() { + if n == nil { + return + } + + n0 := n + n0.isConst = true + n0.isZero = true + for ; n != nil; n = n.InitializerList { + in := n.Initializer + in.setConstZero() + n0.isConst = n0.isConst && in.isConst + n0.isZero = n0.isZero && in.isZero + } +} + +// [0], 6.7.8 Initialization +func (n *Initializer) check(ctx *context, list *[]*Initializer, t Type, sc StorageClass, fld Field, off uintptr, il *InitializerList, designatorList *DesignatorList, inList bool) *InitializerList { + // trc("==== %v: case %v, t %v, off %v, designatorList != nil %v, inList %v", n.Position(), n.Case, t.Alias(), off, designatorList != nil, inList) + // if fld != nil { + // trc("\tfld %q", fld.Name()) + // } + + // 3 - The type of the entity to be initialized shall be an array of + // unknown size or an object type that is not a variable length array + // type. + if t.Kind() == Array && t.IsVLA() { + ctx.errNode(n, "cannot initialize a variable length array: %v", t) + if il != nil { + return il.InitializerList + } + + return nil + } + + defer func(d int) { ctx.readDelta = d }(ctx.readDelta) + + ctx.readDelta = 1 + n.typ = t + single := n.single() + var op Operand + if single != nil { + op = single.AssignmentExpression.check(ctx, false) + single.typ = t + single.Field = fld + single.Offset = off + } + + // 11: The initializer for a scalar shall be a single expression, optionally + // enclosed in braces. The initial value of the object is that of the + // expression (after conversion); the same type constraints and conversions as + // for simple assignment apply, taking the type of the scalar to be the + // unqualified version of its declared type. + if t.IsScalarType() && single != nil { + if designatorList != nil { + panic(todo("", n.Position())) + } + + //TODO check compatible + *list = append(*list, single) + switch { + case t.Kind() == op.Type().Kind(): + single.AssignmentExpression.InitializerOperand = op + default: + single.AssignmentExpression.InitializerOperand = op.convertTo(ctx, n, t) + } + if il != nil { + return il.InitializerList + } + + return nil + } + + // 12: The rest of this subclause deals with initializers for objects that have + // aggregate or union type. + + k := t.Kind() + + // 13: The initializer for a structure or union object that has automatic + // storage duration shall be either an initializer list as described below, or + // a single expression that has compatible structure or union type. In the + // latter case, the initial value of the object, including unnamed members, is + // that of the expression. + if n.Case == InitializerExpr && sc == Automatic && (k == Struct || k == Union || k == Vector) && t.IsCompatible(op.Type()) { + if designatorList != nil { + panic(todo("", n.Position())) + } + + *list = append(*list, single) + if il != nil { + return il.InitializerList + } + + return nil + } + + if k == Array && single != nil { + et := t.Elem() + switch { + case isCharType(et): + // 14: An array of character type may be initialized by a character string + // literal, optionally enclosed in braces. Successive characters of the + // character string literal (including the terminating null character if there + // is room or if the array is of unknown size) initialize the elements of the + // array. + if x, ok := op.Value().(StringValue); ok { + if designatorList != nil { + panic(todo("", n.Position())) + } + + *list = append(*list, single) + str := StringID(x).String() + if t.IsIncomplete() { + t.setLen(uintptr(len(str)) + 1) + } + if il != nil { + return il.InitializerList + } + + return nil + } + case isWCharType(et): + // 15: An array with element type compatible with wchar_t may be initialized by + // a wide string literal, optionally enclosed in braces. Successive wide + // characters of the wide string literal (including the terminating null wide + // character if there is room or if the array is of unknown size) initialize + // the elements of the array. + if x, ok := op.Value().(WideStringValue); ok { + if designatorList != nil { + panic(todo("", n.Position())) + } + + *list = append(*list, single) + str := []rune(StringID(x).String()) + if t.IsIncomplete() { + t.setLen(uintptr(len(str)) + 1) + } + if il != nil { + panic(todo("")) + } + + return nil + } + } + } + + // 16: Otherwise, the initializer for an object that has aggregate or union + // type shall be a brace-enclosed list of initializers for the elements or + // named members. + if n.Case == InitializerExpr { + if il != nil { + switch t.Kind() { + case Array: + return il.checkArray(ctx, list, t, sc, off, designatorList, inList) + case Struct: + return il.checkStruct(ctx, list, t, sc, off, designatorList, inList) + case Union: + return il.checkUnion(ctx, list, t, sc, off, designatorList, inList) + case Vector: + return il.InitializerList //TODO + default: + panic(todo("", n.Position(), t, t.Kind())) + } + } + + var l *InitializerList + Inspect(n.AssignmentExpression, func(m Node, b bool) bool { + if x, ok := m.(*PostfixExpression); ok && x.Case == PostfixExpressionComplit { + if !b { + return true + } + + if l == nil { + l = x.InitializerList + return true + } + + l = nil + return false + } + return true + }) + if l != nil { + l.check(ctx, list, t, sc, off, designatorList, inList) + return nil + } + + ctx.errNode(n, "initializer for an object that has aggregate or union type shall be a brace-enclosed list of initializers for the elements or named members: %v", t) + return nil + } + + n.InitializerList.check(ctx, list, t, sc, off, designatorList, inList) + if il != nil { + return il.InitializerList + } + + return nil +} + +func (n *InitializerList) checkArray(ctx *context, list *[]*Initializer, t Type, sc StorageClass, off uintptr, designatorList *DesignatorList, inList bool) *InitializerList { + elem := t.Elem() + esz := elem.Size() + length := t.Len() + var i, maxI uintptr + nestedDesignator := designatorList != nil + retOnDesignator := false +loop: + for n != nil { + switch { + case retOnDesignator && n.Designation != nil: + return n + case designatorList == nil && !inList && n.Designation != nil: + designatorList = n.Designation.DesignatorList + fallthrough + case designatorList != nil: + d := designatorList.Designator + designatorList = designatorList.DesignatorList + switch d.Case { + case DesignatorIndex: // '[' ConstantExpression ']' + switch x := d.ConstantExpression.check(ctx, ctx.mode|mIntConstExpr, false).Value().(type) { + case Int64Value: + i = uintptr(x) + case Uint64Value: + i = uintptr(x) + default: + panic(todo("%v: %T", n.Position(), x)) + } + if !inList && i > maxI { + maxI = i + } + case DesignatorField: // '.' IDENTIFIER + panic(todo("", n.Position(), d.Position())) + case DesignatorField2: // IDENTIFIER ':' + panic(todo("", n.Position(), d.Position())) + default: + panic(todo("")) + } + + n = n.Initializer.check(ctx, list, elem, sc, nil, off+i*esz, n, designatorList, designatorList != nil) + designatorList = nil + if nestedDesignator { + retOnDesignator = true + } + i++ + default: + if !t.IsIncomplete() && i >= length { + break loop + } + + if i > maxI { + maxI = i + } + n = n.Initializer.check(ctx, list, elem, sc, nil, off+i*esz, n, nil, inList) + i++ + } + } + if t.IsIncomplete() { + t.setLen(maxI + 1) + } + return n +} + +func (n *InitializerList) checkStruct(ctx *context, list *[]*Initializer, t Type, sc StorageClass, off uintptr, designatorList *DesignatorList, inList bool) *InitializerList { + // trc("==== (A) %v: t %v, off %v, dl %v, inList %v", n.Position(), t, off, designatorList != nil, inList) + // defer trc("==== (Z) %v: t %v, off %v, dl %v, inList %v", n.Position(), t, off, designatorList != nil, inList) + t = t.underlyingType() + // trc("%v: %v, off %v", n.Position(), t, off) //TODO- + nf := t.NumField() + i := []int{0} + var f Field + nestedDesignator := designatorList != nil + retOnDesignator := false + for n != nil { + switch { + case retOnDesignator && n.Designation != nil: + return n + case designatorList == nil && !inList && n.Designation != nil: + designatorList = n.Designation.DesignatorList + fallthrough + case designatorList != nil: + d := designatorList.Designator + designatorList = designatorList.DesignatorList + var nm StringID + switch d.Case { + case DesignatorIndex: // '[' ConstantExpression ']' + panic(todo("", n.Position(), d.Position())) + case DesignatorField: // '.' IDENTIFIER + nm = d.Token2.Value + case DesignatorField2: // IDENTIFIER ':' + nm = d.Token.Value + default: + panic(todo("")) + } + + f, xa, ok := t.FieldByName2(nm) + if !ok { + panic(todo("%v: t %v %q", d.Position(), t, nm)) + } + + t0 := t + switch { + case len(xa) != 1: + var f2 Field + var off2 uintptr + for len(xa) != 1 { + f2 = t.FieldByIndex(xa[:1]) + off2 += f2.Offset() + t = f2.Type() + xa = xa[1:] + } + n = n.Initializer.check(ctx, list, t, sc, f, off+off2, n, designatorList, designatorList != nil) + if t.Kind() == Union { + t = t0 + } + default: + n = n.Initializer.check(ctx, list, f.Type(), sc, f, off+f.Offset(), n, designatorList, designatorList != nil) + } + designatorList = nil + if nestedDesignator { + retOnDesignator = true + } + i[0] = xa[0] + 1 + default: + // [0], 6.7.8 Initialization + // + // 9 - Except where explicitly stated otherwise, for the + // purposes of this subclause unnamed members of objects of + // structure and union type do not participate in + // initialization. Unnamed members of structure objects have + // indeterminate value even after initialization. + for ; ; i[0]++ { + if i[0] >= nf { + return n + } + + f = t.FieldByIndex(i) + if f.Name() != 0 || !f.Type().IsBitFieldType() { + n = n.Initializer.check(ctx, list, f.Type(), sc, f, off+f.Offset(), n, nil, inList) + i[0]++ + break + } + } + } + } + return n +} + +func spos(n Node) string { + p := n.Position() + p.Filename = filepath.Base(p.Filename) + return p.String() +} + +func (n *InitializerList) checkUnion(ctx *context, list *[]*Initializer, t Type, sc StorageClass, off uintptr, designatorList *DesignatorList, inList bool) *InitializerList { + // trc("==== %v: t %v, off %v, dl %v, inList %v", n.Position(), t, off, designatorList != nil, inList) + t = t.underlyingType() + // trc("%v: %v, off %v", n.Position(), t, off) //TODO- + nf := t.NumField() + i := []int{0} + for pass := 0; n != nil; pass++ { + switch { + case designatorList == nil && !inList && n.Designation != nil: + designatorList = n.Designation.DesignatorList + fallthrough + case designatorList != nil: + d := designatorList.Designator + designatorList = designatorList.DesignatorList + var nm StringID + switch d.Case { + case DesignatorIndex: // '[' ConstantExpression ']' + panic(todo("", n.Position(), d.Position())) + case DesignatorField: // '.' IDENTIFIER + nm = d.Token2.Value + case DesignatorField2: // IDENTIFIER ':' + nm = d.Token.Value + default: + panic(todo("")) + } + + f, xa, ok := t.FieldByName2(nm) + if !ok { + panic(todo("", d.Position())) + } + + if !inList && pass == 0 { + n.Initializer.field0 = f + } + switch { + case len(xa) != 1: + var f2 Field + var off2 uintptr + for len(xa) != 1 { + f2 = t.FieldByIndex(xa[:1]) + off2 += f2.Offset() + t = f2.Type() + xa = xa[1:] + } + next := n.Initializer.check(ctx, list, t, sc, f, off+off2+f.Offset(), n, designatorList, designatorList != nil) + if designatorList != nil && designatorList.DesignatorList != nil { + panic(todo("", n.Position(), d.Position())) + } + + return next + default: + next := n.Initializer.check(ctx, list, f.Type(), sc, f, off+f.Offset(), n, designatorList, designatorList != nil) + if designatorList != nil && designatorList.DesignatorList != nil { + panic(todo("", n.Position(), d.Position())) + } + + return next + } + default: + // [0], 6.7.8 Initialization + // + // 9 - Except where explicitly stated otherwise, for the + // purposes of this subclause unnamed members of objects of + // structure and union type do not participate in + // initialization. Unnamed members of structure objects have + // indeterminate value even after initialization. + for ; ; i[0]++ { + if i[0] >= nf { + panic(todo("")) + } + + f := t.FieldByIndex(i) + if f.Name() != 0 || !f.Type().IsBitFieldType() { + next := n.Initializer.check(ctx, list, f.Type(), sc, f, off+f.Offset(), n, nil, inList) + return next + } + } + panic(todo("", n.Position())) + } + } + return nil +} + +// Accept a single initializer, optionally enclosed in braces, but nested +// braces. Implements eg. [0]6.7.8.11. +// +// 42 // ok +// {42} // ok +// {{42}} // not ok +func (n *Initializer) single() *Initializer { + switch n.Case { + case InitializerExpr: // AssignmentExpression + return n + case InitializerInitList: // '{' InitializerList ',' '}' + if n.InitializerList == nil { // + return nil + } + + if n.InitializerList.InitializerList == nil { + if in := n.InitializerList.Initializer; in.Case == InitializerExpr { + return in + } + } + } + return nil +} + +// [0], 6.7.8 Initialization +func (n *InitializerList) check(ctx *context, list *[]*Initializer, t Type, sc StorageClass, off uintptr, designatorList *DesignatorList, inList bool) { + switch t.Kind() { + case Array, Vector: + if n == nil { // {} + if t.IsIncomplete() { + t.setLen(0) + } + return + } + + n.checkArray(ctx, list, t, sc, off, designatorList, inList) + case Struct: + if n == nil { // {} + return + } + + n.checkStruct(ctx, list, t, sc, off, designatorList, inList) + case Union: + if n == nil { // {} + return + } + + n.checkUnion(ctx, list, t, sc, off, designatorList, inList) + default: + if n == nil || t == nil || t.Kind() == Invalid { + return + } + + n.Initializer.check(ctx, list, t, sc, nil, off, nil, designatorList, inList) + } +} + +func setLHS(lhs map[*Declarator]struct{}, rhs Node) { + inCall := 0 + Inspect(rhs, func(n Node, enter bool) bool { + switch x := n.(type) { + case *PostfixExpression: + switch x.Case { + case PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')' + switch { + case enter: + inCall++ + if d := x.Declarator(); d != nil { + for v := range lhs { + d.setLHS(v) + } + } + default: + inCall-- + } + } + case *PrimaryExpression: + if inCall != 0 || !enter { + break + } + + if d := x.Declarator(); d != nil { + for v := range lhs { + d.setLHS(v) + } + } + } + return true + }) +} + +func (n *AssignmentExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + if n.Operand != nil { + return n.Operand + } + + if ctx.cfg.TrackAssignments && n.AssignmentExpression != nil { + defer func() { + lhs := map[*Declarator]struct{}{} + Inspect(n.UnaryExpression, func(n Node, enter bool) bool { + if !enter { + return true + } + + if x, ok := n.(*PrimaryExpression); ok { + lhs[x.Declarator()] = struct{}{} + } + return true + }) + setLHS(lhs, n.AssignmentExpression) + }() + } + + //TODO check for "modifiable lvalue" in left operand + n.Operand = noOperand + switch n.Case { + case AssignmentExpressionCond: // ConditionalExpression + n.Operand = n.ConditionalExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.ConditionalExpression.IsSideEffectsFree + case AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression + l := n.UnaryExpression.check(ctx, isAsmArg) + if d := n.UnaryExpression.Declarator(); d != nil { + d.Read -= ctx.readDelta + } + if d := n.UnaryExpression.Operand.Declarator(); d != nil { + d.Write++ + if l.Type().Kind() == Array && !d.IsParameter && l.Type().String() != "va_list" { + ctx.errNode(n.UnaryExpression, "assignment to expression with array type") + break + } + } + + if !l.IsLValue() { + //TODO ctx.errNode(n.UnaryExpression, "expected lvalue") + break + } + + r := n.AssignmentExpression.check(ctx, isAsmArg) + _ = r //TODO check assignability + n.Operand = l.(*lvalue).Operand + case AssignmentExpressionMul: // UnaryExpression "*=" AssignmentExpression + l := n.UnaryExpression.check(ctx, isAsmArg) + if d := n.UnaryExpression.Operand.Declarator(); d != nil { + d.SubjectOfAsgnOp = true + d.Read += ctx.readDelta + d.Write++ + } + if !l.IsLValue() { + //TODO panic(n.Position().String()) // report error + break + } + + r := n.AssignmentExpression.check(ctx, isAsmArg) + //TODO check assignability + if l.Type().IsArithmeticType() { + op, _ := usualArithmeticConversions(ctx, n, l, r, true) + n.promote = op.Type() + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: l.Type()} + case AssignmentExpressionDiv: // UnaryExpression "/=" AssignmentExpression + l := n.UnaryExpression.check(ctx, isAsmArg) + if d := n.UnaryExpression.Operand.Declarator(); d != nil { + d.SubjectOfAsgnOp = true + d.Read += ctx.readDelta + d.Write++ + } + if !l.IsLValue() { + //TODO panic(n.Position().String()) // report error + break + } + + r := n.AssignmentExpression.check(ctx, isAsmArg) + //TODO check assignability + if l.Type().IsArithmeticType() { + op, _ := usualArithmeticConversions(ctx, n, l, r, true) + n.promote = op.Type() + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: l.Type()} + case AssignmentExpressionMod: // UnaryExpression "%=" AssignmentExpression + l := n.UnaryExpression.check(ctx, isAsmArg) + if d := n.UnaryExpression.Operand.Declarator(); d != nil { + d.SubjectOfAsgnOp = true + d.Read += ctx.readDelta + d.Write++ + } + if !l.IsLValue() { + //TODO panic(n.Position().String()) // report error + break + } + + r := n.AssignmentExpression.check(ctx, isAsmArg) + //TODO check assignability + if l.Type().IsArithmeticType() { + op, _ := usualArithmeticConversions(ctx, n, l, r, true) + n.promote = op.Type() + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: l.Type()} + case AssignmentExpressionAdd: // UnaryExpression "+=" AssignmentExpression + l := n.UnaryExpression.check(ctx, isAsmArg) + if d := n.UnaryExpression.Operand.Declarator(); d != nil { + d.SubjectOfAsgnOp = true + d.Read += ctx.readDelta + d.Write++ + } + if !l.IsLValue() { + //TODO panic(n.Position().String()) // report error + break + } + + r := n.AssignmentExpression.check(ctx, isAsmArg) + //TODO check assignability + n.promote = n.UnaryExpression.Operand.Type() + if l.Type().IsArithmeticType() { + op, _ := usualArithmeticConversions(ctx, n, l, r, true) + n.promote = op.Type() + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: l.Type()} + case AssignmentExpressionSub: // UnaryExpression "-=" AssignmentExpression + l := n.UnaryExpression.check(ctx, isAsmArg) + if d := n.UnaryExpression.Operand.Declarator(); d != nil { + d.SubjectOfAsgnOp = true + d.Read += ctx.readDelta + d.Write++ + } + if !l.IsLValue() { + //TODO panic(n.Position().String()) // report error + break + } + + r := n.AssignmentExpression.check(ctx, isAsmArg) + //TODO check assignability + n.promote = n.UnaryExpression.Operand.Type() + if l.Type().IsArithmeticType() { + op, _ := usualArithmeticConversions(ctx, n, l, r, true) + n.promote = op.Type() + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: l.Type()} + case AssignmentExpressionLsh: // UnaryExpression "<<=" AssignmentExpression + l := n.UnaryExpression.check(ctx, isAsmArg) + if d := n.UnaryExpression.Operand.Declarator(); d != nil { + d.SubjectOfAsgnOp = true + d.Read += ctx.readDelta + d.Write++ + } + if !l.IsLValue() { + //TODO panic(n.Position().String()) // report error + break + } + + r := n.AssignmentExpression.check(ctx, isAsmArg) + //TODO check assignability + if !l.Type().IsIntegerType() || !r.Type().IsIntegerType() { + //TODO report error + break + } + + n.promote = r.integerPromotion(ctx, n).Type() + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: l.Type()}).integerPromotion(ctx, n) + case AssignmentExpressionRsh: // UnaryExpression ">>=" AssignmentExpression + l := n.UnaryExpression.check(ctx, isAsmArg) + if d := n.UnaryExpression.Operand.Declarator(); d != nil { + d.SubjectOfAsgnOp = true + d.Read += ctx.readDelta + d.Write++ + } + if !l.IsLValue() { + //TODO panic(n.Position().String()) // report error + break + } + + r := n.AssignmentExpression.check(ctx, isAsmArg) + //TODO check assignability + if !l.Type().IsIntegerType() || !r.Type().IsIntegerType() { + //TODO report error + break + } + + n.promote = r.integerPromotion(ctx, n).Type() + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: l.Type()}).integerPromotion(ctx, n) + case AssignmentExpressionAnd: // UnaryExpression "&=" AssignmentExpression + l := n.UnaryExpression.check(ctx, isAsmArg) + if d := n.UnaryExpression.Operand.Declarator(); d != nil { + d.SubjectOfAsgnOp = true + d.Read += ctx.readDelta + d.Write++ + } + if !l.IsLValue() { + //TODO panic(n.Position().String()) // report error + break + } + + r := n.AssignmentExpression.check(ctx, isAsmArg) + //TODO check assignability + if !l.Type().IsIntegerType() || !r.Type().IsIntegerType() { + //TODO report error + break + } + + op, _ := usualArithmeticConversions(ctx, n, l, r, true) + n.promote = op.Type() + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: l.Type()} + case AssignmentExpressionXor: // UnaryExpression "^=" AssignmentExpression + l := n.UnaryExpression.check(ctx, isAsmArg) + if d := n.UnaryExpression.Operand.Declarator(); d != nil { + d.SubjectOfAsgnOp = true + d.Read += ctx.readDelta + d.Write++ + } + if !l.IsLValue() { + //TODO panic(n.Position().String()) // report error + break + } + + r := n.AssignmentExpression.check(ctx, isAsmArg) + //TODO check assignability + if !l.Type().IsIntegerType() || !r.Type().IsIntegerType() { + //TODO report error + break + } + + op, _ := usualArithmeticConversions(ctx, n, l, r, true) + n.promote = op.Type() + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: l.Type()} + case AssignmentExpressionOr: // UnaryExpression "|=" AssignmentExpression + l := n.UnaryExpression.check(ctx, isAsmArg) + if d := n.UnaryExpression.Operand.Declarator(); d != nil { + d.SubjectOfAsgnOp = true + d.Read += ctx.readDelta + d.Write++ + } + if !l.IsLValue() { + //TODO panic(n.Position().String()) // report error + break + } + + r := n.AssignmentExpression.check(ctx, isAsmArg) + //TODO check assignability + if !l.Type().IsIntegerType() || !r.Type().IsIntegerType() { + //TODO report error + break + } + + op, _ := usualArithmeticConversions(ctx, n, l, r, true) + n.promote = op.Type() + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: l.Type()} + default: + panic(todo("")) + } + return n.Operand +} + +func (n *UnaryExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case UnaryExpressionPostfix: // PostfixExpression + n.Operand = n.PostfixExpression.check(ctx, false, isAsmArg) + n.IsSideEffectsFree = n.PostfixExpression.IsSideEffectsFree + case UnaryExpressionInc: // "++" UnaryExpression + op := n.UnaryExpression.check(ctx, isAsmArg) + if d := op.Declarator(); d != nil { + d.SubjectOfIncDec = true + d.Read += ctx.readDelta + d.Write++ + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: op.Type()} + case UnaryExpressionDec: // "--" UnaryExpression + op := n.UnaryExpression.check(ctx, isAsmArg) + if d := op.Declarator(); d != nil { + d.SubjectOfIncDec = true + d.Read += ctx.readDelta + d.Write++ + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: op.Type()} + case UnaryExpressionAddrof: // '&' CastExpression + ctx.not(n, mIntConstExpr) + op := n.CastExpression.addrOf(ctx) + n.IsSideEffectsFree = n.CastExpression.IsSideEffectsFree + if op.Type().IsBitFieldType() { + //TODO report error + break + } + + d := n.CastExpression.Declarator() + if d != nil { + setAddressTaken(n, d, "'&' CastExpression") + if d.td.register() { + //TODO report error + } + } + + // [0], 6.5.3.2 + // + // The operand of the unary & operator shall be either a + // function designator, the result of a [] or unary * operator, + // or an lvalue that designates an object that is not a + // bit-field and is not declared with the register + // storage-class specifier. + //TODO + if x, ok := op.(*funcDesignator); ok { + n.Operand = x + break + } + + n.Operand = op + case UnaryExpressionDeref: // '*' CastExpression + ctx.not(n, mIntConstExpr) + op := n.CastExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.CastExpression.IsSideEffectsFree + if x, ok := op.(*funcDesignator); ok { + n.Operand = x + break + } + + if op.Type().Kind() == Function { + n.Operand = op + break + } + + if op.Type().Decay().Kind() != Ptr { + //TODO report error + break + } + + n.Operand = &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: op.Type().Elem()}} + case UnaryExpressionPlus: // '+' CastExpression + op := n.CastExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.CastExpression.IsSideEffectsFree + if !op.Type().IsArithmeticType() { + //TODO report error + break + } + + if op.Type().IsIntegerType() { + op = op.integerPromotion(ctx, n) + } + n.Operand = op + case UnaryExpressionMinus: // '-' CastExpression + op := n.CastExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.CastExpression.IsSideEffectsFree + if op.Type().Kind() == Vector { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: op.Type()} + break + } + + if !op.Type().IsArithmeticType() { + //TODO report error + break + } + + if op.Type().IsIntegerType() { + op = op.integerPromotion(ctx, n) + } + if v := op.Value(); v != nil { + op = (&operand{abi: &ctx.cfg.ABI, typ: op.Type(), value: v.neg()}).normalize(ctx, n) + } + n.Operand = op + case UnaryExpressionCpl: // '~' CastExpression + op := n.CastExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.CastExpression.IsSideEffectsFree + if op.Type().Kind() == Vector { + if !op.Type().Elem().IsIntegerType() { + ctx.errNode(n, "operand must be integer") + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: op.Type()} + break + } + + if op.Type().IsComplexType() { + n.Operand = op + break + } + + if !op.Type().IsIntegerType() { + ctx.errNode(n, "operand must be integer") + break + } + + op = op.integerPromotion(ctx, n) + if v := op.Value(); v != nil { + op = (&operand{abi: &ctx.cfg.ABI, typ: op.Type(), value: v.cpl()}).normalize(ctx, n) + } + n.Operand = op + case UnaryExpressionNot: // '!' CastExpression + op := n.CastExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.CastExpression.IsSideEffectsFree + op2 := &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Int)} + switch { + case op.IsZero(): + op2.value = Int64Value(1) + case op.IsNonZero(): + op2.value = Int64Value(0) + } + n.Operand = op2 + case UnaryExpressionSizeofExpr: // "sizeof" UnaryExpression + n.IsSideEffectsFree = true + rd := ctx.readDelta + // [0]6.5.3.4, 2: If the type of the operand is a variable length array type, + // the operand is evaluated; otherwise, the operand is not evaluated and the + // result is an integer constant. + switch op := n.UnaryExpression.Operand; { + case op != nil && op.Type() != nil && op.Type().IsVLA(): + ctx.readDelta = 1 + default: + ctx.readDelta = 0 + } + ctx.push(ctx.mode &^ mIntConstExpr) + op := n.UnaryExpression.check(ctx, isAsmArg) + ctx.pop() + ctx.readDelta = rd + if op.Type().IsIncomplete() { + break + } + + sz := op.Type().Size() + if d := n.UnaryExpression.Declarator(); d != nil && d.IsParameter { + sz = op.Type().Decay().Size() + } + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(ULongLong), value: Uint64Value(sz)}).convertTo(ctx, n, sizeT(ctx, n.lexicalScope, n.Token)) + case UnaryExpressionSizeofType: // "sizeof" '(' TypeName ')' + n.IsSideEffectsFree = true + rd := ctx.readDelta + ctx.readDelta = 0 + ctx.push(ctx.mode) + if ctx.mode&mIntConstExpr != 0 { + ctx.mode |= mIntConstExprAnyCast + } + t := n.TypeName.check(ctx, false, false, nil) + ctx.pop() + ctx.readDelta = rd + if t.IsIncomplete() { + break + } + + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(ULongLong), value: Uint64Value(t.Size())}).convertTo(ctx, n, sizeT(ctx, n.lexicalScope, n.Token)) + case UnaryExpressionLabelAddr: // "&&" IDENTIFIER + abi := &ctx.cfg.ABI + n.Operand = &operand{abi: abi, typ: abi.Ptr(n, abi.Type(Void))} + n.IsSideEffectsFree = true + ctx.not(n, mIntConstExpr) + f := ctx.checkFn + if f == nil { + //TODO report error + break + } + + if f.ComputedGotos == nil { + f.ComputedGotos = map[StringID]*UnaryExpression{} + } + f.ComputedGotos[n.Token2.Value] = n + case UnaryExpressionAlignofExpr: // "_Alignof" UnaryExpression + n.IsSideEffectsFree = true + ctx.push(ctx.mode &^ mIntConstExpr) + op := n.UnaryExpression.check(ctx, isAsmArg) + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(ULongLong), value: Uint64Value(op.Type().Align())}).convertTo(ctx, n, sizeT(ctx, n.lexicalScope, n.Token)) + ctx.pop() + case UnaryExpressionAlignofType: // "_Alignof" '(' TypeName ')' + n.IsSideEffectsFree = true + ctx.push(ctx.mode) + if ctx.mode&mIntConstExpr != 0 { + ctx.mode |= mIntConstExprAnyCast + } + t := n.TypeName.check(ctx, false, false, nil) + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(ULongLong), value: Uint64Value(t.Align())}).convertTo(ctx, n, sizeT(ctx, n.lexicalScope, n.Token)) + ctx.pop() + case UnaryExpressionImag: // "__imag__" UnaryExpression + ctx.not(n, mIntConstExpr) + n.UnaryExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.UnaryExpression.IsSideEffectsFree + n.Operand = complexPart(ctx, n.UnaryExpression.Operand) + case UnaryExpressionReal: // "__real__" UnaryExpression + ctx.not(n, mIntConstExpr) + n.UnaryExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.UnaryExpression.IsSideEffectsFree + n.Operand = complexPart(ctx, n.UnaryExpression.Operand) + default: + panic(todo("")) + } + return n.Operand +} + +func complexPart(ctx *context, op Operand) Operand { + var k Kind + switch op.Type().Kind() { + case ComplexChar: + k = Char + case ComplexDouble: + k = Double + case ComplexFloat: + k = Float + case ComplexInt: + k = Int + case ComplexLong: + k = Long + case ComplexLongDouble: + k = LongDouble + case ComplexLongLong: + k = LongLong + case ComplexShort: + k = Short + case ComplexUInt: + k = UInt + case ComplexULong: + k = ULong + case ComplexULongLong: + k = ULongLong + case ComplexUShort: + k = UShort + default: + //TODO report err + return noOperand + } + + abi := &ctx.cfg.ABI + typ := abi.Type(k) + return &operand{abi: abi, typ: typ} +} + +func sizeT(ctx *context, s Scope, tok Token) Type { + if t := ctx.sizeT; t != nil { + return t + } + + t := ctx.stddef(idSizeT, s, tok) + if t.Kind() != Invalid { + ctx.sizeT = t + } + return t +} + +func (n *CastExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case CastExpressionUnary: // UnaryExpression + n.Operand = n.UnaryExpression.addrOf(ctx) + n.IsSideEffectsFree = n.UnaryExpression.IsSideEffectsFree + case CastExpressionCast: // '(' TypeName ')' CastExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *UnaryExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case UnaryExpressionPostfix: // PostfixExpression + n.Operand = n.PostfixExpression.addrOf(ctx) + n.IsSideEffectsFree = n.PostfixExpression.IsSideEffectsFree + case UnaryExpressionInc: // "++" UnaryExpression + panic(n.Position().String()) + case UnaryExpressionDec: // "--" UnaryExpression + panic(n.Position().String()) + case UnaryExpressionAddrof: // '&' CastExpression + panic(n.Position().String()) + case UnaryExpressionDeref: // '*' CastExpression + n.Operand = n.CastExpression.check(ctx, false) + n.IsSideEffectsFree = n.CastExpression.IsSideEffectsFree + case UnaryExpressionPlus: // '+' CastExpression + panic(n.Position().String()) + case UnaryExpressionMinus: // '-' CastExpression + panic(n.Position().String()) + case UnaryExpressionCpl: // '~' CastExpression + panic(n.Position().String()) + case UnaryExpressionNot: // '!' CastExpression + panic(n.Position().String()) + case UnaryExpressionSizeofExpr: // "sizeof" UnaryExpression + panic(n.Position().String()) + case UnaryExpressionSizeofType: // "sizeof" '(' TypeName ')' + panic(n.Position().String()) + case UnaryExpressionLabelAddr: // "&&" IDENTIFIER + panic(n.Position().String()) + case UnaryExpressionAlignofExpr: // "_Alignof" UnaryExpression + panic(n.Position().String()) + case UnaryExpressionAlignofType: // "_Alignof" '(' TypeName ')' + panic(n.Position().String()) + case UnaryExpressionImag: // "__imag__" UnaryExpression + n.Operand = n.UnaryExpression.addrOf(ctx) + n.IsSideEffectsFree = n.UnaryExpression.IsSideEffectsFree + case UnaryExpressionReal: // "__real__" UnaryExpression + n.Operand = n.UnaryExpression.addrOf(ctx) + n.IsSideEffectsFree = n.UnaryExpression.IsSideEffectsFree + default: + panic(todo("")) + } + return n.Operand +} + +func (n *PostfixExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case PostfixExpressionPrimary: // PrimaryExpression + n.Operand = n.PrimaryExpression.addrOf(ctx) + n.IsSideEffectsFree = n.PrimaryExpression.IsSideEffectsFree + case PostfixExpressionIndex: // PostfixExpression '[' Expression ']' + pe := n.PostfixExpression.check(ctx, false, false) + if d := n.PostfixExpression.Declarator(); d != nil && d.Type().Kind() != Ptr { + setAddressTaken(n, d, "PostfixExpression '[' Expression ']'") + d.Read += ctx.readDelta + } + e := n.Expression.check(ctx, false) + n.IsSideEffectsFree = n.PostfixExpression.IsSideEffectsFree && n.Expression.IsSideEffectsFree + t := pe.Type().Decay() + if t.Kind() == Invalid { + break + } + + if t.Kind() == Ptr { + if t := e.Type(); t.Kind() != Invalid && !t.IsIntegerType() { + ctx.errNode(n.Expression, "index must be integer type, have %v", e.Type()) + break + } + + n.Operand = n.indexAddr(ctx, &n.Token, pe, e) + break + } + + if pe.Type().Kind() == Vector { + if t := e.Type(); t.Kind() != Invalid && !t.IsIntegerType() { + ctx.errNode(n.Expression, "index must be integer type, have %v", e.Type()) + break + } + + n.Operand = n.index(ctx, pe, e) + break + } + + t = e.Type().Decay() + if t.Kind() == Invalid { + break + } + + if t.Kind() == Ptr { + if t := pe.Type(); t.Kind() != Invalid && !t.IsIntegerType() { + ctx.errNode(n.Expression, "index must be integer type, have %v", pe.Type()) + break + } + + n.Operand = n.indexAddr(ctx, &n.Token, e, pe) + break + } + + ctx.errNode(n, "invalid index expression %v[%v]", pe.Type(), e.Type()) + case PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')' + panic(n.Position().String()) + case PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER + op := n.PostfixExpression.addrOf(ctx) + n.IsSideEffectsFree = n.PostfixExpression.IsSideEffectsFree + if d := n.PostfixExpression.Declarator(); d != nil { + setAddressTaken(n, d, "PostfixExpression '.' IDENTIFIER") + d.Read += ctx.readDelta + } + st := op.Type().Elem() + if k := st.Kind(); k == Invalid || k != Struct && k != Union { + //TODO report error + break + } + + f, ok := st.FieldByName(n.Token2.Value) + if !ok { + ctx.errNode(&n.Token2, "unknown or ambiguous field: %s", n.Token2.Value) + break + } + + n.Field = f + ft := f.Type() + if f.IsBitField() { + //TODO report error + break + } + + ot := ctx.cfg.ABI.Ptr(n, ft) + switch { + case op.IsConst(): + switch x := op.Value().(type) { + case Uint64Value: + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: ot, value: x + Uint64Value(f.Offset())} + return n.Operand + case nil: + // nop + default: + //TODO panic(todo(" %v: %T", n.Position(), x)) + } + + fallthrough + default: + n.Operand = &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: ot, offset: op.Offset() + f.Offset()}, declarator: op.Declarator()} + } + case PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER + op := n.PostfixExpression.check(ctx, false, false) + n.IsSideEffectsFree = n.PostfixExpression.IsSideEffectsFree + if d := n.PostfixExpression.Declarator(); d != nil { + d.Read += ctx.readDelta + } + t := op.Type() + if k := t.Decay().Kind(); k == Invalid || k != Ptr { + //TODO report error + break + } + + st := t.Elem() + if k := st.Kind(); k == Invalid || k != Struct && k != Union { + //TODO report error + break + } + + f, ok := st.FieldByName(n.Token2.Value) + if !ok { + //TODO report error + break + } + + n.Field = f + ft := f.Type() + if f.IsBitField() { + //TODO report error + break + } + + ot := ctx.cfg.ABI.Ptr(n, ft) + switch { + case op.IsConst(): + switch x := op.Value().(type) { + case Uint64Value: + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: ot, value: x + Uint64Value(f.Offset())} + return n.Operand + case nil: + // nop + default: + panic(todo(" %T", x)) + } + + fallthrough + default: + n.Operand = &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: ot, offset: op.Offset() + f.Offset()}, declarator: op.Declarator()} + } + case PostfixExpressionInc: // PostfixExpression "++" + panic(n.Position().String()) + case PostfixExpressionDec: // PostfixExpression "--" + panic(n.Position().String()) + case PostfixExpressionComplit: // '(' TypeName ')' '{' InitializerList ',' '}' + //TODO IsSideEffectsFree + if f := ctx.checkFn; f != nil { + f.CompositeLiterals = append(f.CompositeLiterals, n) + } + t := n.TypeName.check(ctx, false, false, nil) + var v *InitializerValue + if n.InitializerList != nil { + n.InitializerList.isConst = true + n.InitializerList.check(ctx, &n.InitializerList.list, t, Automatic, 0, nil, false) + n.InitializerList.setConstZero() + v = &InitializerValue{typ: ctx.cfg.ABI.Ptr(n, t), initializer: n.InitializerList} + } + n.Operand = &lvalue{Operand: (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Ptr(n, t), value: v}).normalize(ctx, n)} + case PostfixExpressionTypeCmp: // "__builtin_types_compatible_p" '(' TypeName ',' TypeName ')' + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *PostfixExpression) indexAddr(ctx *context, nd Node, pe, e Operand) Operand { + var x uintptr + hasx := false + switch v := e.Value().(type) { + case Int64Value: + x = uintptr(v) + hasx = true + case Uint64Value: + x = uintptr(v) + hasx = true + } + off := x * pe.Type().Elem().Size() + switch y := pe.Value().(type) { + case StringValue, WideStringValue: + if hasx { + return &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Ptr(n, pe.Type().Elem()), value: pe.Value(), offset: off}} + } + case Uint64Value: + if hasx { + return &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Ptr(n, pe.Type().Elem()), value: y + Uint64Value(off)}} + } + } + + if d := pe.Declarator(); d != nil && hasx { + r := &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Ptr(n, pe.Type().Elem()), offset: pe.Offset() + off}, declarator: d} + return r + } + + return &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Ptr(n, pe.Type().Elem())}} +} + +func (n *PrimaryExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case PrimaryExpressionIdent: // IDENTIFIER + n.IsSideEffectsFree = true + n.check(ctx, false, false) + if d := n.Operand.Declarator(); d != nil { + switch d.Type().Kind() { + case Function: + // nop //TODO ? + default: + setAddressTaken(n, d, "&IDENTIFIER") + n.Operand = &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Ptr(n, d.Type())}, declarator: d} + } + return n.Operand + } + if ctx.cfg.RejectLateBinding && !ctx.cfg.ignoreUndefinedIdentifiers { + ctx.errNode(n, "front-end: undefined: %s", n.Token.Value) + return noOperand + } + + //TODO + case PrimaryExpressionInt: // INTCONST + panic(n.Position().String()) + case PrimaryExpressionFloat: // FLOATCONST + panic(n.Position().String()) + case PrimaryExpressionEnum: // ENUMCONST + panic(n.Position().String()) + case PrimaryExpressionChar: // CHARCONST + panic(n.Position().String()) + case PrimaryExpressionLChar: // LONGCHARCONST + panic(n.Position().String()) + case PrimaryExpressionString: // STRINGLITERAL + panic(n.Position().String()) + case PrimaryExpressionLString: // LONGSTRINGLITERAL + panic(n.Position().String()) + case PrimaryExpressionExpr: // '(' Expression ')' + n.Operand = n.Expression.addrOf(ctx) + n.IsSideEffectsFree = n.Expression.IsSideEffectsFree + case PrimaryExpressionStmt: // '(' CompoundStatement ')' + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *Expression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + n.Operand = noOperand //TODO- + switch n.Case { + case ExpressionAssign: // AssignmentExpression + n.Operand = n.AssignmentExpression.addrOf(ctx) + n.IsSideEffectsFree = n.AssignmentExpression.IsSideEffectsFree + case ExpressionComma: // Expression ',' AssignmentExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *AssignmentExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + n.Operand = noOperand //TODO- + switch n.Case { + case AssignmentExpressionCond: // ConditionalExpression + n.Operand = n.ConditionalExpression.addrOf(ctx) + n.IsSideEffectsFree = n.ConditionalExpression.IsSideEffectsFree + case AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression + panic(n.Position().String()) + case AssignmentExpressionMul: // UnaryExpression "*=" AssignmentExpression + panic(n.Position().String()) + case AssignmentExpressionDiv: // UnaryExpression "/=" AssignmentExpression + panic(n.Position().String()) + case AssignmentExpressionMod: // UnaryExpression "%=" AssignmentExpression + panic(n.Position().String()) + case AssignmentExpressionAdd: // UnaryExpression "+=" AssignmentExpression + panic(n.Position().String()) + case AssignmentExpressionSub: // UnaryExpression "-=" AssignmentExpression + panic(n.Position().String()) + case AssignmentExpressionLsh: // UnaryExpression "<<=" AssignmentExpression + panic(n.Position().String()) + case AssignmentExpressionRsh: // UnaryExpression ">>=" AssignmentExpression + panic(n.Position().String()) + case AssignmentExpressionAnd: // UnaryExpression "&=" AssignmentExpression + panic(n.Position().String()) + case AssignmentExpressionXor: // UnaryExpression "^=" AssignmentExpression + panic(n.Position().String()) + case AssignmentExpressionOr: // UnaryExpression "|=" AssignmentExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *ConditionalExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + n.Operand = noOperand //TODO- + switch n.Case { + case ConditionalExpressionLOr: // LogicalOrExpression + n.Operand = n.LogicalOrExpression.addrOf(ctx) + n.IsSideEffectsFree = n.LogicalOrExpression.IsSideEffectsFree + case ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *LogicalOrExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case LogicalOrExpressionLAnd: // LogicalAndExpression + n.Operand = n.LogicalAndExpression.addrOf(ctx) + n.IsSideEffectsFree = n.LogicalAndExpression.IsSideEffectsFree + case LogicalOrExpressionLOr: // LogicalOrExpression "||" LogicalAndExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *LogicalAndExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case LogicalAndExpressionOr: // InclusiveOrExpression + n.Operand = n.InclusiveOrExpression.addrOf(ctx) + n.IsSideEffectsFree = n.InclusiveOrExpression.IsSideEffectsFree + case LogicalAndExpressionLAnd: // LogicalAndExpression "&&" InclusiveOrExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *InclusiveOrExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case InclusiveOrExpressionXor: // ExclusiveOrExpression + n.Operand = n.ExclusiveOrExpression.addrOf(ctx) + n.IsSideEffectsFree = n.ExclusiveOrExpression.IsSideEffectsFree + case InclusiveOrExpressionOr: // InclusiveOrExpression '|' ExclusiveOrExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *ExclusiveOrExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case ExclusiveOrExpressionAnd: // AndExpression + n.Operand = n.AndExpression.addrOf(ctx) + n.IsSideEffectsFree = n.AndExpression.IsSideEffectsFree + case ExclusiveOrExpressionXor: // ExclusiveOrExpression '^' AndExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *AndExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case AndExpressionEq: // EqualityExpression + n.Operand = n.EqualityExpression.addrOf(ctx) + n.IsSideEffectsFree = n.EqualityExpression.IsSideEffectsFree + case AndExpressionAnd: // AndExpression '&' EqualityExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *EqualityExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case EqualityExpressionRel: // RelationalExpression + n.Operand = n.RelationalExpression.addrOf(ctx) + n.IsSideEffectsFree = n.RelationalExpression.IsSideEffectsFree + case EqualityExpressionEq: // EqualityExpression "==" RelationalExpression + panic(n.Position().String()) + case EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *RelationalExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case RelationalExpressionShift: // ShiftExpression + n.Operand = n.ShiftExpression.addrOf(ctx) + n.IsSideEffectsFree = n.ShiftExpression.IsSideEffectsFree + case RelationalExpressionLt: // RelationalExpression '<' ShiftExpression + panic(n.Position().String()) + case RelationalExpressionGt: // RelationalExpression '>' ShiftExpression + panic(n.Position().String()) + case RelationalExpressionLeq: // RelationalExpression "<=" ShiftExpression + panic(n.Position().String()) + case RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *ShiftExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case ShiftExpressionAdd: // AdditiveExpression + n.Operand = n.AdditiveExpression.addrOf(ctx) + n.IsSideEffectsFree = n.AdditiveExpression.IsSideEffectsFree + case ShiftExpressionLsh: // ShiftExpression "<<" AdditiveExpression + panic(n.Position().String()) + case ShiftExpressionRsh: // ShiftExpression ">>" AdditiveExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *AdditiveExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case AdditiveExpressionMul: // MultiplicativeExpression + n.Operand = n.MultiplicativeExpression.addrOf(ctx) + n.IsSideEffectsFree = n.MultiplicativeExpression.IsSideEffectsFree + case AdditiveExpressionAdd: // AdditiveExpression '+' MultiplicativeExpression + panic(n.Position().String()) + case AdditiveExpressionSub: // AdditiveExpression '-' MultiplicativeExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *MultiplicativeExpression) addrOf(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case MultiplicativeExpressionCast: // CastExpression + n.Operand = n.CastExpression.addrOf(ctx) + n.IsSideEffectsFree = n.CastExpression.IsSideEffectsFree + case MultiplicativeExpressionMul: // MultiplicativeExpression '*' CastExpression + panic(n.Position().String()) + case MultiplicativeExpressionDiv: // MultiplicativeExpression '/' CastExpression + panic(n.Position().String()) + case MultiplicativeExpressionMod: // MultiplicativeExpression '%' CastExpression + panic(n.Position().String()) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *TypeName) check(ctx *context, inUnion, isPacked bool, list *[]*TypeSpecifier) Type { + if n == nil { + return noType + } + + n.typ = n.SpecifierQualifierList.check(ctx, inUnion, isPacked, list) + if n.AbstractDeclarator != nil { + n.typ = n.AbstractDeclarator.check(ctx, n.typ) + } + for list := n.SpecifierQualifierList; list != nil; list = list.SpecifierQualifierList { + if expr, ok := list.AttributeSpecifier.Has(idVectorSize, idVectorSize2); ok { + n.vectorize(ctx, expr) + break + } + } + return n.typ +} + +func (n *TypeName) vectorize(ctx *context, expr *ExpressionList) { + dst := &n.typ + elem := n.typ + switch n.typ.Kind() { + case Function: + dst = &n.typ.(*functionType).result + elem = n.typ.Result() + } + + sz := expr.vectorSize(ctx) + if sz == 0 { + sz = elem.Size() + } + if sz%elem.Size() != 0 { + ctx.errNode(expr, "vector size must be a multiple of the base size") + } + b := n.typ.base() + b.size = sz + b.kind = byte(Vector) + *dst = &vectorType{ + typeBase: b, + elem: elem, + length: sz / elem.Size(), + } +} + +func (n *AbstractDeclarator) check(ctx *context, typ Type) Type { + if n == nil { + return typ + } + + n.typ = noType //TODO- + switch n.Case { + case AbstractDeclaratorPtr: // Pointer + n.typ = n.Pointer.check(ctx, typ) + case AbstractDeclaratorDecl: // Pointer DirectAbstractDeclarator + typ = n.Pointer.check(ctx, typ) + n.typ = n.DirectAbstractDeclarator.check(ctx, typ) + default: + panic(todo("")) + } + return n.typ +} + +func (n *DirectAbstractDeclarator) check(ctx *context, typ Type) Type { + if n == nil { + return typ + } + + switch n.Case { + case DirectAbstractDeclaratorDecl: // '(' AbstractDeclarator ')' + if n.AbstractDeclarator == nil { + // [0], 6.7.6, 128) + // + // As indicated by the syntax, empty parentheses in a + // type name are interpreted as ‘‘function with no + // parameter specification’’, rather than redundant + // parentheses around the omitted identifier. + panic(todo("")) //TODO + } + + return n.AbstractDeclarator.check(ctx, typ) + case DirectAbstractDeclaratorArr: // DirectAbstractDeclarator '[' TypeQualifiers AssignmentExpression ']' + return n.DirectAbstractDeclarator.check(ctx, checkArray(ctx, &n.Token, typ, n.AssignmentExpression, true, false)) + case DirectAbstractDeclaratorStaticArr: // DirectAbstractDeclarator '[' "static" TypeQualifiers AssignmentExpression ']' + return n.DirectAbstractDeclarator.check(ctx, checkArray(ctx, &n.Token, typ, n.AssignmentExpression, false, false)) + case DirectAbstractDeclaratorArrStatic: // DirectAbstractDeclarator '[' TypeQualifiers "static" AssignmentExpression ']' + return n.DirectAbstractDeclarator.check(ctx, checkArray(ctx, &n.Token, typ, n.AssignmentExpression, false, false)) + case DirectAbstractDeclaratorArrStar: // DirectAbstractDeclarator '[' '*' ']' + return n.DirectAbstractDeclarator.check(ctx, checkArray(ctx, &n.Token, typ, nil, true, true)) + case DirectAbstractDeclaratorFunc: // DirectAbstractDeclarator '(' ParameterTypeList ')' + ft := &functionType{typeBase: typeBase{kind: byte(Function)}, result: typ} + n.ParameterTypeList.check(ctx, ft) + return n.DirectAbstractDeclarator.check(ctx, ft) + } + + panic(internalErrorf("%v: %v", n.Position(), n.Case)) +} + +func (n *ParameterTypeList) check(ctx *context, ft *functionType) { + if n == nil { + return + } + + switch n.Case { + case ParameterTypeListList: // ParameterList + n.ParameterList.check(ctx, ft) + case ParameterTypeListVar: // ParameterList ',' "..." + ft.variadic = true + n.ParameterList.check(ctx, ft) + default: + panic(todo("")) + } +} + +func (n *ParameterList) check(ctx *context, ft *functionType) { + for ; n != nil; n = n.ParameterList { + p := n.ParameterDeclaration.check(ctx, ft) + ft.params = append(ft.params, p) + } +} + +func (n *ParameterDeclaration) check(ctx *context, ft *functionType) *Parameter { + if n == nil { + return nil + } + + switch n.Case { + case ParameterDeclarationDecl: // DeclarationSpecifiers Declarator AttributeSpecifierList + typ, _, _ := n.DeclarationSpecifiers.check(ctx, false) + n.Declarator.IsParameter = true + if n.typ = n.Declarator.check(ctx, n.DeclarationSpecifiers, typ, false); n.typ.Kind() == Void { + panic(n.Position().String()) + } + if n.AttributeSpecifierList != nil { + //TODO panic(n.Position().String()) + } + n.AttributeSpecifierList.check(ctx, n.typ.baseP()) + return &Parameter{d: n.Declarator, typ: n.typ} + case ParameterDeclarationAbstract: // DeclarationSpecifiers AbstractDeclarator + n.typ, _, _ = n.DeclarationSpecifiers.check(ctx, false) + if n.AbstractDeclarator != nil { + n.typ = n.AbstractDeclarator.check(ctx, n.typ) + } + return &Parameter{typ: n.typ} + default: + panic(todo("")) + } +} + +func (n *Pointer) check(ctx *context, typ Type) (t Type) { + if n == nil || typ == nil { + return typ + } + + switch n.Case { + case PointerTypeQual: // '*' TypeQualifiers + n.TypeQualifiers.check(ctx, &n.typeQualifiers) + case PointerPtr: // '*' TypeQualifiers Pointer + n.TypeQualifiers.check(ctx, &n.typeQualifiers) + typ = n.Pointer.check(ctx, typ) + case PointerBlock: // '^' TypeQualifiers + n.TypeQualifiers.check(ctx, &n.typeQualifiers) + default: + panic(todo("")) + } + r := ctx.cfg.ABI.Ptr(n, typ).(*pointerType) + if n.typeQualifiers != nil { + r.typeQualifiers = n.typeQualifiers.check(ctx, (*DeclarationSpecifiers)(nil), false) + } + return r +} + +func (n *TypeQualifiers) check(ctx *context, typ **typeBase) { + for ; n != nil; n = n.TypeQualifiers { + switch n.Case { + case TypeQualifiersTypeQual: // TypeQualifier + if *typ == nil { + *typ = &typeBase{} + } + n.TypeQualifier.check(ctx, *typ) + case TypeQualifiersAttribute: // AttributeSpecifier + if *typ == nil { + *typ = &typeBase{} + } + n.AttributeSpecifier.check(ctx, *typ) + default: + panic(todo("")) + } + } +} + +func (n *TypeQualifier) check(ctx *context, typ *typeBase) { + if n == nil { + return + } + + switch n.Case { + case TypeQualifierConst: // "const" + typ.flags |= fConst + case TypeQualifierRestrict: // "restrict" + typ.flags |= fRestrict + case TypeQualifierVolatile: // "volatile" + typ.flags |= fVolatile + case TypeQualifierAtomic: // "_Atomic" + typ.flags |= fAtomic + default: + panic(todo("")) + } +} + +func (n *SpecifierQualifierList) check(ctx *context, inUnion, isPacked bool, list *[]*TypeSpecifier) Type { + n0 := n + typ := &typeBase{} + for ; n != nil; n = n.SpecifierQualifierList { + switch n.Case { + case SpecifierQualifierListTypeSpec: // TypeSpecifier SpecifierQualifierList + n.TypeSpecifier.check(ctx, typ, inUnion) + if list != nil && n.TypeSpecifier.Case != TypeSpecifierAtomic { + *list = append(*list, n.TypeSpecifier) + } + case SpecifierQualifierListTypeQual: // TypeQualifier SpecifierQualifierList + n.TypeQualifier.check(ctx, typ) + case SpecifierQualifierListAlignSpec: // AlignmentSpecifier SpecifierQualifierList + n.AlignmentSpecifier.check(ctx) + case SpecifierQualifierListAttribute: // AttributeSpecifier SpecifierQualifierList + n.AttributeSpecifier.check(ctx, typ) + default: + panic(todo("")) + } + } + return typ.check(ctx, n0, true) +} + +func (n *TypeSpecifier) check(ctx *context, typ *typeBase, inUnion bool) { + if n == nil { + return + } + + switch n.Case { + case + TypeSpecifierVoid, // "void" + TypeSpecifierChar, // "char" + TypeSpecifierShort, // "short" + TypeSpecifierInt, // "int" + TypeSpecifierInt8, // "__int8" + TypeSpecifierInt16, // "__int16" + TypeSpecifierInt32, // "__int32" + TypeSpecifierInt64, // "__int64" + TypeSpecifierInt128, // "__int128" + TypeSpecifierLong, // "long" + TypeSpecifierFloat, // "float" + TypeSpecifierFloat16, // "__fp16" + TypeSpecifierDecimal32, // "_Decimal32" + TypeSpecifierDecimal64, // "_Decimal64" + TypeSpecifierDecimal128, // "_Decimal128" + TypeSpecifierFloat32, // "_Float32" + TypeSpecifierFloat32x, // "_Float32x" + TypeSpecifierFloat64, // "_Float64" + TypeSpecifierFloat64x, // "_Float64x" + TypeSpecifierFloat128, // "_Float128" + TypeSpecifierFloat80, // "__float80" + TypeSpecifierDouble, // "double" + TypeSpecifierSigned, // "signed" + TypeSpecifierUnsigned, // "unsigned" + TypeSpecifierBool, // "_Bool" + TypeSpecifierComplex: // "_Complex" + // nop + case TypeSpecifierStructOrUnion: // StructOrUnionSpecifier + n.StructOrUnionSpecifier.check(ctx, typ, inUnion) + case TypeSpecifierEnum: // EnumSpecifier + n.EnumSpecifier.check(ctx) + case TypeSpecifierTypedefName: // TYPEDEFNAME + // nop + case TypeSpecifierTypeofExpr: // "typeof" '(' Expression ')' + op := n.Expression.check(ctx, false) + n.typ = op.Type() + case TypeSpecifierTypeofType: // "typeof" '(' TypeName ')' + n.typ = n.TypeName.check(ctx, false, false, nil) + case TypeSpecifierAtomic: // AtomicTypeSpecifier + t := n.AtomicTypeSpecifier.check(ctx) + typ.kind = t.base().kind + typ.flags |= fAtomic + n.typ = typ + case + TypeSpecifierFract, // "_Fract" + TypeSpecifierSat, // "_Sat" + TypeSpecifierAccum: // "_Accum" + // nop + default: + panic(todo("")) + } +} + +func (n *AtomicTypeSpecifier) check(ctx *context) Type { + if n == nil { + return nil + } + + return n.TypeName.check(ctx, false, false, &n.list) +} + +func (n *EnumSpecifier) check(ctx *context) { + if n == nil { + return + } + + switch n.Case { + case EnumSpecifierDef: // "enum" AttributeSpecifierList IDENTIFIER '{' EnumeratorList ',' '}' + n.AttributeSpecifierList.check(ctx, nil) + min, max := n.EnumeratorList.check(ctx) + var tmin, tmax Type + switch min := min.(type) { + case Int64Value: + switch { + case min >= 0 && ctx.cfg.UnsignedEnums: + tmin = n.requireUint(ctx, uint64(min)) + switch max := max.(type) { + case Int64Value: + tmax = n.requireUint(ctx, uint64(max)) + case Uint64Value: + tmax = n.requireUint(ctx, uint64(max)) + case nil: + panic(todo("%v:", n.Position())) + } + default: + tmin = n.requireInt(ctx, int64(min)) + switch max := max.(type) { + case Int64Value: + tmax = n.requireInt(ctx, int64(max)) + case Uint64Value: + tmax = n.requireInt(ctx, int64(max)) + case nil: + panic(todo("%v:", n.Position())) + } + } + case Uint64Value: + tmin = n.requireUint(ctx, uint64(min)) + switch max := max.(type) { + case Int64Value: + if max < 0 { + panic(todo("%v: min %v max %v", n.Position(), min, max)) + } + + tmax = n.requireUint(ctx, uint64(max)) + case Uint64Value: + tmax = n.requireUint(ctx, uint64(max)) + case nil: + _ = max + panic(todo("%v:", n.Position())) + } + case nil: + panic(todo("%v: %v %T", n.Position(), n.Case, min)) + } + switch { + case tmin.Size() > tmax.Size(): + n.typ = tmin + default: + n.typ = tmax + } + if !n.typ.IsIntegerType() || n.typ.Size() == 0 { //TODO- + panic(todo("")) + } + + reg := n.lexicalScope.Parent() == nil + for list := n.EnumeratorList; list != nil; list = list.EnumeratorList { + en := list.Enumerator + en.Operand = en.Operand.convertTo(ctx, en, n.typ) + if reg { + ctx.enums[en.Token.Value] = en.Operand + } + } + case EnumSpecifierTag: // "enum" AttributeSpecifierList IDENTIFIER + n.typ = &taggedType{ + resolutionScope: n.lexicalScope, + tag: n.Token2.Value, + typeBase: &typeBase{kind: byte(Enum)}, + } + default: + panic(todo("")) + } +} + +func (n *EnumSpecifier) requireInt(ctx *context, m int64) (r Type) { + var w int + switch { + case m < 0: + w = mathutil.BitLenUint64(uint64(-m)) + default: + w = mathutil.BitLenUint64(uint64(m)) + 1 + } + w = mathutil.Max(w, 32) + abi := ctx.cfg.ABI + for k0, v := range intConvRank { + k := Kind(k0) + if k == Bool || k == Enum || v == 0 || !abi.isSignedInteger(k) { + continue + } + + t := abi.Types[k] + if int(t.Size)*8 < w { + continue + } + + if r == nil || t.Size < r.Size() { + r = abi.Type(k) + } + } + if r == nil || r.Size() == 0 { //TODO- + panic(todo("")) + } + return r +} + +func (n *EnumSpecifier) requireUint(ctx *context, m uint64) (r Type) { + w := mathutil.BitLenUint64(m) + w = mathutil.Max(w, 32) + abi := ctx.cfg.ABI + for k0, v := range intConvRank { + k := Kind(k0) + if k == Bool || k == Enum || v == 0 || abi.isSignedInteger(k) { + continue + } + + t := abi.Types[k] + if int(t.Size)*8 < w { + continue + } + + if r == nil || t.Size < r.Size() { + r = abi.Type(k) + } + } + if r == nil || r.Size() == 0 { //TODO- + panic(todo("")) + } + return r +} + +func (n *EnumeratorList) check(ctx *context) (min, max Value) { + var iota Value + for ; n != nil; n = n.EnumeratorList { + iota, min, max = n.Enumerator.check(ctx, iota, min, max) + } + return min, max +} + +func (n *Enumerator) check(ctx *context, iota, min, max Value) (Value, Value, Value) { + if n == nil { + return nil, nil, nil + } + + if iota == nil { + iota = Int64Value(0) + } + switch n.Case { + case EnumeratorIdent: // IDENTIFIER AttributeSpecifierList + n.AttributeSpecifierList.check(ctx, nil) + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Int), value: iota}).normalize(ctx, n) + case EnumeratorExpr: // IDENTIFIER AttributeSpecifierList '=' ConstantExpression + n.AttributeSpecifierList.check(ctx, nil) + n.Operand = n.ConstantExpression.check(ctx, ctx.mode|mIntConstExpr, false) + iota = n.Operand.Value() + default: + panic(todo("")) + } + switch x := iota.(type) { + case Int64Value: + switch m := min.(type) { + case Int64Value: + if x < m { + min = x + } + case Uint64Value: + if x < 0 || Uint64Value(x) < m { + min = x + } + case nil: + min = x + } + switch m := max.(type) { + case Int64Value: + if x > m { + max = x + } + case Uint64Value: + if x >= 0 && Uint64Value(x) > m { + max = x + } + case nil: + max = x + } + x++ + iota = x + case Uint64Value: + switch m := min.(type) { + case Int64Value: + if m < 0 { + break + } + + if x < Uint64Value(m) { + min = x + } + case Uint64Value: + if x < m { + min = x + } + case nil: + min = x + } + switch m := max.(type) { + case Int64Value: + if m < 0 { + max = x + break + } + + if x > Uint64Value(m) { + max = x + } + + case Uint64Value: + if x > m { + max = x + } + case nil: + max = x + } + x++ + iota = x + case nil: + //TODO report type + } + + return iota, min, max +} + +func (n *ConstantExpression) check(ctx *context, mode mode, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + ctx.push(mode) + n.Operand = n.ConditionalExpression.check(ctx, isAsmArg) + ctx.pop() + return n.Operand +} + +func (n *StructOrUnionSpecifier) check(ctx *context, typ *typeBase, inUnion bool) Type { + if n == nil { + return noType + } + + switch n.Case { + case StructOrUnionSpecifierDef: // StructOrUnion AttributeSpecifierList IDENTIFIER '{' StructDeclarationList '}' + typ.kind = byte(n.StructOrUnion.check(ctx)) + attr := n.AttributeSpecifierList.check(ctx, typ) + fields := n.StructDeclarationList.check(ctx, inUnion || typ.Kind() == Union, typ.IsPacked()) + m := make(map[StringID]*field, len(fields)) + x := 0 + for _, v := range fields { + if v.name != 0 { + v.x = x + v.xs = []int{x} + x++ + m[v.name] = v + } + } + t := (&structType{ + attr: attr, + fields: fields, + m: m, + tag: n.Token.Value, + typeBase: typ, + }).check(ctx, n) + if typ.Kind() == Union { + var k Kind + for _, v := range fields { + if k == Invalid { + k = v.typ.Kind() + continue + } + + if v.typ.Kind() != k { + k = Invalid + break + } + } + t.common = k + } + n.typ = t + if nm := n.Token.Value; nm != 0 && n.lexicalScope.Parent() == nil { + ctx.structTypes[nm] = t + } + case StructOrUnionSpecifierTag: // StructOrUnion AttributeSpecifierList IDENTIFIER + typ.kind = byte(n.StructOrUnion.check(ctx)) + attr := n.AttributeSpecifierList.check(ctx, typ.baseP()) + n.typ = &taggedType{ + resolutionScope: n.lexicalScope, + tag: n.Token.Value, + typeBase: typ, + } + if attr != nil { + n.typ = &attributedType{n.typ, attr} + } + default: + panic(todo("")) + } + return n.typ +} + +func (n *StructDeclarationList) check(ctx *context, inUnion, isPacked bool) (s []*field) { + for ; n != nil; n = n.StructDeclarationList { + s = append(s, n.StructDeclaration.check(ctx, inUnion, isPacked)...) + } + return s +} + +func (n *StructDeclaration) check(ctx *context, inUnion, isPacked bool) (s []*field) { + if n == nil || n.Empty { + return nil + } + + typ := n.SpecifierQualifierList.check(ctx, inUnion, isPacked, nil) + if n.StructDeclaratorList != nil { + return n.StructDeclaratorList.check(ctx, n.SpecifierQualifierList, typ, inUnion, isPacked) + } + + return []*field{{typ: typ, inUnion: inUnion}} +} + +func (n *StructDeclaratorList) check(ctx *context, td typeDescriptor, typ Type, inUnion, isPacked bool) (s []*field) { + for ; n != nil; n = n.StructDeclaratorList { + s = append(s, n.StructDeclarator.check(ctx, td, typ, inUnion, isPacked)) + } + return s +} + +func (n *StructDeclarator) check(ctx *context, td typeDescriptor, typ Type, inUnion, isPacked bool) *field { + if n == nil { + return nil + } + + if isPacked { + typ.baseP().flags |= fPacked + } + if n.Declarator != nil { + typ = n.Declarator.check(ctx, td, typ, false) + } + if attr := n.AttributeSpecifierList.check(ctx, typ.baseP()); len(attr) != 0 { + typ = &attributedType{typ, attr} + } + sf := &field{ + typ: typ, + d: n, + inUnion: inUnion, + } + switch n.Case { + case StructDeclaratorDecl: // Declarator + sf.name = n.Declarator.Name() + case StructDeclaratorBitField: // Declarator ':' ConstantExpression AttributeSpecifierList + sf.isBitField = true + sf.typ = &bitFieldType{Type: typ, field: sf} + sf.name = n.Declarator.Name() + if op := n.ConstantExpression.check(ctx, ctx.mode|mIntConstExpr, false); op.Type().IsIntegerType() { + switch x := op.Value().(type) { + case Int64Value: + if x < 0 || x > 64 { + panic("TODO") + } + sf.bitFieldWidth = byte(x) + case Uint64Value: + if x > 64 { + panic("TODO") + } + sf.bitFieldWidth = byte(x) + default: + //dbg("%T", x) + panic(PrettyString(op)) + } + } else { + //dbg("", n.ConstantExpression) + panic(n.Declarator.Position()) + } + n.AttributeSpecifierList.check(ctx, sf.typ.baseP()) + default: + panic(todo("")) + } + return sf +} + +func (n *StructOrUnion) check(ctx *context) Kind { + if n == nil { + return Invalid + } + + switch n.Case { + case StructOrUnionStruct: // "struct" + return Struct + case StructOrUnionUnion: // "union" + return Union + default: + panic(todo("")) + } +} + +func (n *CastExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case CastExpressionUnary: // UnaryExpression + n.Operand = n.UnaryExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.UnaryExpression.IsSideEffectsFree + case CastExpressionCast: // '(' TypeName ')' CastExpression + t := n.TypeName.check(ctx, false, false, nil) + ctx.push(ctx.mode) + if m := ctx.mode; m&mIntConstExpr != 0 && m&mIntConstExprAnyCast == 0 { + if t := n.TypeName.Type(); t != nil && t.Kind() != Int { + ctx.mode &^= mIntConstExpr + } + ctx.mode |= mIntConstExprFloat + } + op := n.CastExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.CastExpression.IsSideEffectsFree + ctx.pop() + n.Operand = op.convertTo(ctx, n, t) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *PostfixExpression) check(ctx *context, implicitFunc, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- +out: + switch n.Case { + case PostfixExpressionPrimary: // PrimaryExpression + n.Operand = n.PrimaryExpression.check(ctx, implicitFunc, isAsmArg) + n.IsSideEffectsFree = n.PrimaryExpression.IsSideEffectsFree + case PostfixExpressionIndex: // PostfixExpression '[' Expression ']' + pe := n.PostfixExpression.check(ctx, false, isAsmArg) + if d := pe.Declarator(); d != nil { + d.Read += ctx.readDelta + } + e := n.Expression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.PostfixExpression.IsSideEffectsFree && n.Expression.IsSideEffectsFree + t := pe.Type().Decay() + if t.Kind() == Invalid { + break + } + + if t.Kind() == Ptr { + if t := e.Type(); t.Kind() != Invalid && !t.IsIntegerType() { + ctx.errNode(n.Expression, "index must be integer type, have %v", e.Type()) + break + } + + n.Operand = n.index(ctx, pe, e) + break + } + + if pe.Type().Kind() == Vector { + if t := e.Type(); t.Kind() != Invalid && !t.IsIntegerType() { + ctx.errNode(n.Expression, "index must be integer type, have %v", e.Type()) + break + } + + n.Operand = n.index(ctx, pe, e) + break + } + + t = e.Type().Decay() + if t.Kind() == Invalid { + break + } + + if t.Kind() == Ptr { + if t := pe.Type(); t.Kind() != Invalid && !t.IsIntegerType() { + ctx.errNode(n.Expression, "index must be integer type, have %v", pe.Type()) + break + } + + n.Operand = n.index(ctx, e, pe) + break + } + + ctx.errNode(n, "invalid index expression %v[%v]", pe.Type(), e.Type()) + case PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')' + op := n.PostfixExpression.check(ctx, true, isAsmArg) + Inspect(n.PostfixExpression, func(n Node, enter bool) bool { + if !enter { + return true + } + + if x, ok := n.(*PrimaryExpression); ok { + if d := x.Declarator(); d != nil { + d.called = true + } + } + return true + }) + args := n.ArgumentExpressionList.check(ctx, n.PostfixExpression.Declarator(), isAsmArg) + switch op.Declarator().Name() { + case idBuiltinConstantPImpl: + if len(args) < 2 { + panic(todo("")) + } + + var v Int64Value + if args[1].Value() != nil { + v = 1 + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Int), value: v} + default: + switch n.PostfixExpression.Operand.Value().(type) { + case StringValue, WideStringValue: + if isAsmArg { + // asm("foo": "bar" (a)) + // ^ + break out + } + } + + n.Operand = n.checkCall(ctx, n, op.Type(), args, n.ArgumentExpressionList) + } + case PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER + op := n.PostfixExpression.check(ctx, false, isAsmArg) + n.IsSideEffectsFree = n.PostfixExpression.IsSideEffectsFree + if d := op.Declarator(); d != nil { + d.Read += ctx.readDelta + } + st := op.Type() + st0 := st.underlyingType() + if k := st.Kind(); k == Invalid || k != Struct && k != Union { + ctx.errNode(n.PostfixExpression, "select expression of wrong type: %s (%s)", st, st0) + break + } + + f, ok := st.FieldByName(n.Token2.Value) + if !ok { + ctx.errNode(n.PostfixExpression, "unknown or ambiguous field %q of type %s (%s)", n.Token2.Value, st, st0) + break + } + + n.Field = f + ft := f.Type() + if f.IsBitField() { + ft = &bitFieldType{Type: ft, field: f.(*field)} + n.Operand = &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: ft}} + break + } + + n.Operand = &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: ft, offset: op.Offset() + f.Offset()}} + case PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER + op := n.PostfixExpression.check(ctx, false, isAsmArg) + n.IsSideEffectsFree = n.PostfixExpression.IsSideEffectsFree + if d := op.Declarator(); d != nil { + d.Read += ctx.readDelta + } + t := op.Type() + if k := t.Decay().Kind(); k == Invalid || k != Ptr { + //TODO report error + break + } + + st := t.Elem() + if k := st.Kind(); k == Invalid || k != Struct && k != Union { + //TODO report error + break + } + + f, ok := st.FieldByName(n.Token2.Value) + if !ok { + //TODO report error + break + } + + n.Field = f + ft := f.Type() + if f.IsBitField() { + ft = &bitFieldType{Type: ft, field: f.(*field)} + } + n.Operand = &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: ft}} + case PostfixExpressionInc: // PostfixExpression "++" + op := n.PostfixExpression.check(ctx, false, isAsmArg) + if d := op.Declarator(); d != nil { + d.SubjectOfIncDec = true + d.Read += ctx.readDelta + d.Write++ + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: op.Type()} + case PostfixExpressionDec: // PostfixExpression "--" + op := n.PostfixExpression.check(ctx, false, isAsmArg) + if d := op.Declarator(); d != nil { + d.SubjectOfIncDec = true + d.Read += ctx.readDelta + d.Write++ + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: op.Type()} + case PostfixExpressionComplit: // '(' TypeName ')' '{' InitializerList ',' '}' + //TODO IsSideEffectsFree + if f := ctx.checkFn; f != nil { + f.CompositeLiterals = append(f.CompositeLiterals, n) + } + t := n.TypeName.check(ctx, false, false, nil) + var v *InitializerValue + if n.InitializerList != nil { + n.InitializerList.check(ctx, &n.InitializerList.list, t, Automatic, 0, nil, false) + n.InitializerList.setConstZero() + v = &InitializerValue{typ: t, initializer: n.InitializerList} + } + n.Operand = &lvalue{Operand: (&operand{abi: &ctx.cfg.ABI, typ: t, value: v}).normalize(ctx, n)} + case PostfixExpressionTypeCmp: // "__builtin_types_compatible_p" '(' TypeName ',' TypeName ')' + n.IsSideEffectsFree = true + t1 := n.TypeName.check(ctx, false, false, nil) + t2 := n.TypeName2.check(ctx, false, false, nil) + v := 0 + switch { + case t1.IsArithmeticType() && t2.IsArithmeticType(): + if t1.Kind() == t2.Kind() { + v = 1 + } + default: + ctx.errNode(n, "ICE: __builtin_types_compatible_p(%v, %v)", t1, t2) + } + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Int), value: Int64Value(v)} + case PostfixExpressionChooseExpr: // "__builtin_choose_expr" '(' ConstantExpression ',' AssignmentExpression ',' AssignmentExpression ')' + n.Operand = noOperand + expr1 := n.AssignmentExpression.check(ctx, isAsmArg) + if expr1 == nil { + ctx.errNode(n, "first argument of __builtin_choose_expr must be a constant expression") + break + } + + if !expr1.IsConst() { + ctx.errNode(n, "first argument of __builtin_choose_expr must be a constant expression: %v %v", expr1.Value(), expr1.Type()) + break + } + + switch { + case expr1.IsNonZero(): + n.Operand = n.AssignmentExpression2.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.AssignmentExpression2.IsSideEffectsFree + default: + n.Operand = n.AssignmentExpression3.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.AssignmentExpression3.IsSideEffectsFree + } + default: + panic(todo("")) + } + return n.Operand +} + +func (n *PostfixExpression) index(ctx *context, pe, e Operand) Operand { + var x uintptr + hasx := false + switch v := e.Value().(type) { + case Int64Value: + x = uintptr(v) + hasx = true + case Uint64Value: + x = uintptr(v) + hasx = true + } + off := x * pe.Type().Elem().Size() + switch v := pe.Value().(type) { + case StringValue: + if hasx { + s := StringID(v).String() + var v byte + switch { + case x > uintptr(len(s)): + //TODO report err + return noOperand + case x < uintptr(len(s)): + v = s[x] + } + + return (&operand{abi: &ctx.cfg.ABI, typ: pe.Type().Elem(), value: Int64Value(v)}).normalize(ctx, n) + } + case WideStringValue: + if hasx { + s := []rune(StringID(v).String()) + var v rune + switch { + case x > uintptr(len(s)): + //TODO report err + return noOperand + case x < uintptr(len(s)): + v = s[x] + } + + return (&operand{abi: &ctx.cfg.ABI, typ: pe.Type().Elem(), value: Int64Value(v)}).normalize(ctx, n) + } + } + + if d := pe.Declarator(); d != nil && hasx { + return &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: pe.Type().Elem(), offset: pe.Offset() + off}, declarator: d} + } + + return &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: pe.Type().Elem()}} +} + +func (n *PostfixExpression) checkCall(ctx *context, nd Node, f Type, args []Operand, argList *ArgumentExpressionList) (r Operand) { + r = noOperand + switch f.Kind() { + case Invalid: + return noOperand + case Function: + // ok + case Ptr: + if e := f.Elem(); e.Kind() == Function { + f = e + break + } + + ctx.errNode(nd, "expected function pointer type: %v, %v", f, f.Kind()) + return r + default: + ctx.errNode(nd, "expected function type: %v, %v", f, f.Kind()) + return r + } + + r = &operand{abi: &ctx.cfg.ABI, typ: f.Result()} + params := f.Parameters() + if len(params) == 1 && params[0].Type().Kind() == Void { + params = nil + if len(args) != 0 { + //TODO report error + return r + } + } + + for i, arg := range args { + var t Type + switch { + case i < len(params): + //TODO check assignability + t = params[i].Type().Decay() + default: + t = defaultArgumentPromotion(ctx, nd, arg).Type() + } + argList.AssignmentExpression.promote = t + argList = argList.ArgumentExpressionList + } + return r +} + +func defaultArgumentPromotion(ctx *context, n Node, op Operand) Operand { + t := op.Type().Decay() + if arithmeticTypes[t.Kind()] { + if t.IsIntegerType() { + return op.integerPromotion(ctx, n) + } + + switch t.Kind() { + case Float: + return op.convertTo(ctx, n, ctx.cfg.ABI.Type(Double)) + } + } + return op +} + +func (n *ArgumentExpressionList) check(ctx *context, f *Declarator, isAsmArg bool) (r []Operand) { + for ; n != nil; n = n.ArgumentExpressionList { + op := n.AssignmentExpression.check(ctx, isAsmArg) + if op.Type() == nil { + ctx.errNode(n, "operand has usupported, invalid or incomplete type") + op = noOperand + } else if op.Type().IsComplexType() { + ctx.checkFn.CallSiteComplexExpr = append(ctx.checkFn.CallSiteComplexExpr, n.AssignmentExpression) + } + r = append(r, op) + if !ctx.cfg.TrackAssignments { + continue + } + + Inspect(n.AssignmentExpression, func(n Node, enter bool) bool { + if !enter { + return true + } + + if x, ok := n.(*PrimaryExpression); ok { + x.Declarator().setLHS(f) + } + return true + }) + } + return r +} + +func (n *Expression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case ExpressionAssign: // AssignmentExpression + n.Operand = n.AssignmentExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.AssignmentExpression.IsSideEffectsFree + case ExpressionComma: // Expression ',' AssignmentExpression + op := n.Expression.check(ctx, isAsmArg) + n.Operand = n.AssignmentExpression.check(ctx, isAsmArg) + if !op.IsConst() && n.Operand.IsConst() { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: n.Operand.Type()} + } + n.IsSideEffectsFree = n.Expression.IsSideEffectsFree && n.AssignmentExpression.IsSideEffectsFree + default: + panic(todo("")) + } + return n.Operand +} + +func (n *PrimaryExpression) check(ctx *context, implicitFunc, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case PrimaryExpressionIdent: // IDENTIFIER + n.IsSideEffectsFree = true + return n.checkIdentifier(ctx, implicitFunc) + case PrimaryExpressionInt: // INTCONST + n.IsSideEffectsFree = true + n.Operand = n.intConst(ctx) + case PrimaryExpressionFloat: // FLOATCONST + n.IsSideEffectsFree = true + if ctx.mode&mIntConstExpr != 0 && ctx.mode&mIntConstExprFloat == 0 { + ctx.errNode(n, "invalid integer constant expression") + break + } + + n.Operand = n.floatConst(ctx) + case PrimaryExpressionEnum: // ENUMCONST + n.IsSideEffectsFree = true + if e := n.resolvedIn.enumerator(n.Token.Value, n.Token); e != nil { + op := e.Operand.(*operand) + op.typ = ctx.cfg.ABI.Type(Int) // [0] 6.4.4.3/2 + n.Operand = op + break + } + + //TODO report err + case PrimaryExpressionChar: // CHARCONST + n.IsSideEffectsFree = true + s := []rune(n.Token.Value.String()) + var v Value + switch { + case s[0] <= 255: + // If an integer character constant contains a single character or escape + // sequence, its value is the one that results when an object with type char + // whose value is that of the single character or escape sequence is converted + // to type int. + switch { + case ctx.cfg.ABI.SignedChar: + v = Int64Value(int8(s[0])) + default: + v = Int64Value(s[0]) + } + default: + v = Int64Value(s[0]) + } + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Int), value: v}).normalize(ctx, n) + case PrimaryExpressionLChar: // LONGCHARCONST + n.IsSideEffectsFree = true + s := []rune(n.Token.Value.String()) + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: wcharT(ctx, n.lexicalScope, n.Token), value: Int64Value(s[0])}).normalize(ctx, n) + case PrimaryExpressionString: // STRINGLITERAL + n.IsSideEffectsFree = true + ctx.not(n, mIntConstExpr) + typ := ctx.cfg.ABI.Type(Char) + b := typ.base() + b.align = byte(typ.Align()) + b.fieldAlign = byte(typ.FieldAlign()) + b.kind = byte(Array) + sz := uintptr(len(n.Token.Value.String())) + 1 //TODO set sz in cpp + arr := &arrayType{typeBase: b, decay: ctx.cfg.ABI.Ptr(n, typ), elem: typ, length: sz} + arr.setLen(sz) + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: arr, value: StringValue(n.Token.Value)}).normalize(ctx, n) + case PrimaryExpressionLString: // LONGSTRINGLITERAL + n.IsSideEffectsFree = true + ctx.not(n, mIntConstExpr) + typ := wcharT(ctx, n.lexicalScope, n.Token) + b := typ.base() + b.align = byte(typ.Align()) + b.fieldAlign = byte(typ.FieldAlign()) + b.kind = byte(Array) + sz := uintptr(len([]rune(n.Token.Value.String()))) + 1 //TODO set sz in cpp + arr := &arrayType{typeBase: b, decay: ctx.cfg.ABI.Ptr(n, typ), elem: typ, length: sz} + arr.setLen(sz) + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: arr, value: WideStringValue(n.Token.Value)}).normalize(ctx, n) + case PrimaryExpressionExpr: // '(' Expression ')' + n.Operand = n.Expression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.Expression.IsSideEffectsFree + case PrimaryExpressionStmt: // '(' CompoundStatement ')' + //TODO IsSideEffectsFree + ctx.not(n, mIntConstExpr) + n.Operand = n.CompoundStatement.check(ctx) + if n.Operand == noOperand { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Void)} + } + default: + panic(todo("")) + } + return n.Operand +} + +func wcharT(ctx *context, s Scope, tok Token) Type { + if t := ctx.wcharT; t != nil { + return t + } + + t := ctx.stddef(idWCharT, s, tok) + if t.Kind() != Invalid { + ctx.wcharT = t + } + return t +} + +func (n *PrimaryExpression) checkIdentifier(ctx *context, implicitFunc bool) Operand { + ctx.not(n, mIntConstExpr) + var d *Declarator + nm := n.Token.Value + if n.resolvedIn == nil { + if ctx.cfg.RejectLateBinding && !ctx.cfg.ignoreUndefinedIdentifiers { + ctx.errNode(n, "front-end: undefined: %s", n.Token.Value) + return noOperand + } + + out: + for s := n.lexicalScope; s != nil; s = s.Parent() { + for _, v := range s[nm] { + switch x := v.(type) { + case *Enumerator: + break out + case *Declarator: + if x.IsTypedefName { + d = nil + break out + } + + n.resolvedIn = s + n.resolvedTo = x + d = x + t := d.Type() + if t != nil && t.Kind() == Function { + if d.fnDef { + break out + } + + continue + } + + if t != nil && !t.IsIncomplete() { + break out + } + case *EnumSpecifier, *StructOrUnionSpecifier, *StructDeclarator, *LabeledStatement: + // nop + default: + panic(todo("")) + } + } + } + } + if d == nil && n.resolvedIn != nil { + d = n.resolvedIn.declarator(n.Token.Value, n.Token) + } + if d == nil && !ctx.cfg.DisableBuiltinResolution { + d = builtin(ctx, nm) + } + if d == nil { + _, ok := gccKeywords[nm] + if !ok && implicitFunc { + d := &Declarator{ + DirectDeclarator: &DirectDeclarator{ + lexicalScope: ctx.ast.Scope, + Case: DirectDeclaratorFuncIdent, + DirectDeclarator: &DirectDeclarator{ + lexicalScope: ctx.ast.Scope, + Case: DirectDeclaratorIdent, + Token: Token{Value: nm}, + }, + }, + implicit: true, + } + ed := &ExternalDeclaration{ + Case: ExternalDeclarationDecl, + Declaration: &Declaration{ + DeclarationSpecifiers: &DeclarationSpecifiers{ + Case: DeclarationSpecifiersTypeSpec, + TypeSpecifier: &TypeSpecifier{ + Case: TypeSpecifierInt, + }, + }, + InitDeclaratorList: &InitDeclaratorList{ + InitDeclarator: &InitDeclarator{ + Case: InitDeclaratorDecl, + Declarator: d, + }, + }, + }, + } + ed.check(ctx) + n.Operand = &funcDesignator{Operand: &operand{abi: &ctx.cfg.ABI, typ: d.Type()}, declarator: d} + return n.Operand + } + + if !ctx.cfg.ignoreUndefinedIdentifiers { + ctx.errNode(n, "front-end: undefined: %s", n.Token.Value) + } + return noOperand + } + if d == nil { + if !ctx.cfg.ignoreUndefinedIdentifiers { + ctx.errNode(n, "front-end: undefined: %s", n.Token.Value) + } + return noOperand + } + + switch d.Linkage { + case Internal: + if d.IsStatic() { + break + } + + fallthrough + case External: + s := n.resolvedIn + if s.Parent() == nil { + break + } + + for s.Parent() != nil { + s = s.Parent() + } + + if d2 := s.declarator(n.Token.Value, Token{}); d2 != nil { + d = d2 + } + } + + if d.Type() == nil { + ctx.errNode(d, "unresolved type of: %s", n.Token.Value) + return noOperand + } + + d.Read += ctx.readDelta + switch t := d.Type(); t.Kind() { + case Function: + n.Operand = &funcDesignator{Operand: &operand{abi: &ctx.cfg.ABI, typ: t}, declarator: d} + default: + n.Operand = &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: t}, declarator: d} + } + if !ctx.capture { + return n.Operand + } + + for s := n.lexicalScope; s != nil; s = s.Parent() { + if _, ok := s[nm]; ok { + return n.Operand // d in fn scope + } + + if _, ok := s[idClosure]; ok { // d in outer scope + if ctx.closure == nil { + ctx.closure = map[StringID]struct{}{} //TODO capture the PrimaryExpression, not the declarator name + } + ctx.closure[nm] = struct{}{} + return n.Operand + } + } + panic(todo("")) +} + +func builtin(ctx *context, nm StringID) *Declarator { + id := dict.sid("__builtin_" + nm.String()) + a := ctx.ast.Scope[id] + if len(a) == 0 { + return nil + } + + switch x := a[0].(type) { + case *Declarator: + if x.fnDef || x.IsFunctionPrototype() { + return x + } + } + return nil +} + +func (n *PrimaryExpression) floatConst(ctx *context) Operand { + s0 := n.Token.String() + s := s0 + var cplx, suff string +loop2: + for i := len(s) - 1; i > 0; i-- { + switch s0[i] { + case 'l', 'L': + s = s[:i] + if ctx.cfg.LongDoubleIsDouble { + break + } + + suff += "l" + case 'f', 'F': + s = s[:i] + suff += "f" + case 'i', 'I', 'j', 'J': + s = s[:i] + cplx += "i" + default: + break loop2 + } + } + + if len(suff) > 1 || len(cplx) > 1 { + ctx.errNode(n, "invalid number format") + return noOperand + } + + var v float64 + var err error + prec := uint(64) + if suff == "l" { + prec = longDoublePrec + } + var bf *big.Float + switch { + case suff == "l" || strings.Contains(s, "p") || strings.Contains(s, "P"): + bf, _, err = big.ParseFloat(strings.ToLower(s), 0, prec, big.ToNearestEven) + if err == nil { + v, _ = bf.Float64() + } + default: + v, err = strconv.ParseFloat(s, 64) + } + if err != nil { + switch { + case !strings.HasPrefix(s, "-") && strings.Contains(err.Error(), "value out of range"): + // linux_386/usr/include/math.h + // + // /* Value returned on overflow. With IEEE 754 floating point, this is + // +Infinity, otherwise the largest representable positive value. */ + // #if __GNUC_PREREQ (3, 3) + // # define HUGE_VAL (__builtin_huge_val ()) + // #else + // /* This may provoke compiler warnings, and may not be rounded to + // +Infinity in all IEEE 754 rounding modes, but is the best that can + // be done in ISO C while remaining a constant expression. 10,000 is + // greater than the maximum (decimal) exponent for all supported + // floating-point formats and widths. */ + // # define HUGE_VAL 1e10000 + // #endif + v = math.Inf(1) + default: + ctx.errNode(n, "%v", err) + return noOperand + } + } + + // [0]6.4.4.2 + switch suff { + case "": + switch { + case cplx != "": + return (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(ComplexDouble), value: Complex128Value(complex(0, v))}).normalize(ctx, n) + default: + return (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Double), value: Float64Value(v)}).normalize(ctx, n) + } + case "f": + switch { + case cplx != "": + return (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(ComplexFloat), value: Complex64Value(complex(0, float32(v)))}).normalize(ctx, n) + default: + return (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Float), value: Float32Value(float32(v))}).normalize(ctx, n) + } + case "l": + switch { + case cplx != "": + return (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(ComplexLongDouble), value: Complex256Value{&Float128Value{N: big.NewFloat(0)}, &Float128Value{N: bf}}}).normalize(ctx, n) + default: + return (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(LongDouble), value: &Float128Value{N: bf}}).normalize(ctx, n) + } + default: + //dbg("%q %q %q %q %v", s0, s, suff, cplx, err) + panic("TODO") + } +} + +func (n *PrimaryExpression) intConst(ctx *context) Operand { + var val uint64 + s0 := n.Token.String() + s := strings.TrimRight(s0, "uUlL") + var decadic bool + switch { + case strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X"): + var err error + if val, err = strconv.ParseUint(s[2:], 16, 64); err != nil { + ctx.errNode(n, "%v", err) + return nil + } + case strings.HasPrefix(s, "0b") || strings.HasPrefix(s, "0B"): + var err error + if val, err = strconv.ParseUint(s[2:], 2, 64); err != nil { + ctx.errNode(n, "%v", err) + return nil + } + case strings.HasPrefix(s, "0"): + var err error + if val, err = strconv.ParseUint(s, 8, 64); err != nil { + ctx.errNode(n, "%v", err) + return nil + } + default: + decadic = true + var err error + if val, err = strconv.ParseUint(s, 10, 64); err != nil { + ctx.errNode(n, "%v", err) + return nil + } + } + + suffix := s0[len(s):] + switch suffix = strings.ToLower(suffix); suffix { + case "": + if decadic { + return intConst(ctx, n, s0, val, Int, Long, LongLong) + } + + return intConst(ctx, n, s0, val, Int, UInt, Long, ULong, LongLong, ULongLong) + case "u": + return intConst(ctx, n, s0, val, UInt, ULong, ULongLong) + case "l": + if decadic { + return intConst(ctx, n, s0, val, Long, LongLong) + } + + return intConst(ctx, n, s0, val, Long, ULong, LongLong, ULongLong) + case "lu", "ul": + return intConst(ctx, n, s0, val, ULong, ULongLong) + case "ll": + if decadic { + return intConst(ctx, n, s0, val, LongLong) + } + + return intConst(ctx, n, s0, val, LongLong, ULongLong) + case "llu", "ull": + return intConst(ctx, n, s0, val, ULongLong) + default: + ctx.errNode(n, "invalid suffix: %v", s0) + return nil + } +} + +func intConst(ctx *context, n Node, s string, val uint64, list ...Kind) Operand { + abi := ctx.cfg.ABI + b := bits.Len64(val) + for _, k := range list { + sign := 0 + if abi.isSignedInteger(k) { + sign = 1 + } + if abi.size(k)*8 >= b+sign { + switch { + case sign == 0: + return (&operand{abi: &ctx.cfg.ABI, typ: abi.Type(k), value: Uint64Value(val)}).normalize(ctx, n) + default: + return (&operand{abi: &ctx.cfg.ABI, typ: abi.Type(k), value: Int64Value(val)}).normalize(ctx, n) + } + } + } + + ctx.errNode(n, "invalid integer constant %v", s) + return nil +} + +func (n *ConditionalExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case ConditionalExpressionLOr: // LogicalOrExpression + n.Operand = n.LogicalOrExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.LogicalOrExpression.IsSideEffectsFree + case ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression + op := n.LogicalOrExpression.check(ctx, isAsmArg) + // The first operand shall have scalar type. + if !op.Type().Decay().IsScalarType() { + //TODO report error + break + } + + a := n.Expression.check(ctx, isAsmArg) + b := n.ConditionalExpression.check(ctx, isAsmArg) + at := a.Type().Decay() + bt := b.Type().Decay() + + n.IsSideEffectsFree = n.LogicalOrExpression.IsSideEffectsFree && (n.Expression == nil || n.Expression.IsSideEffectsFree) && n.ConditionalExpression.IsSideEffectsFree + var val Value + if op.Value() != nil { + switch { + case op.IsZero(): + n.IsSideEffectsFree = n.LogicalOrExpression.IsSideEffectsFree && n.ConditionalExpression.IsSideEffectsFree + default: + n.IsSideEffectsFree = n.LogicalOrExpression.IsSideEffectsFree && n.Expression.IsSideEffectsFree + } + if a.Value() != nil && b.Value() != nil { //TODO not needed both non nil + switch { + case op.IsZero(): + val = b.Value() + default: + val = a.Value() + } + } + } + + if a.Type().Kind() == Invalid && b.Type().Kind() == Invalid { + return noOperand + } + + // One of the following shall hold for the second and third + // operands: + //TODO — both operands have the same structure or union type; + //TODO — one operand is a pointer to an object or incomplete type and the other is a pointer to a + //TODO qualified or unqualified version of void. + switch { + // — both operands have arithmetic type; + case a.Type().IsArithmeticType() && b.Type().IsArithmeticType(): + // If both the second and third operands have + // arithmetic type, the result type that would be + // determined by the usual arithmetic conversions, were + // they applied to those two operands, + // is the type of the result. + op, _ := usualArithmeticConversions(ctx, n, a, b, true) + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: op.Type(), value: val}).normalize(ctx, n) + // — both operands have void type; + case a.Type().Kind() == Void && b.Type().Kind() == Void: + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: val}).normalize(ctx, n) + // — one operand is a pointer and the other is a null pointer constant; + case (a.Type().Kind() == Ptr || a.Type().Kind() == Function) && b.IsZero(): + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: val}).normalize(ctx, n) + case (b.Type().Kind() == Ptr || b.Type().Kind() == Function) && a.IsZero(): + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: b.Type(), value: val}).normalize(ctx, n) + // — both operands are pointers to qualified or unqualified versions of compatible types; + case at.Kind() == Ptr && bt.Kind() == Ptr: + //TODO check compatible + //TODO if !at.isCompatibleIgnoreQualifiers(bt) { + //TODO trc("%v: XXXX %v ? %v", n.Token2.Position(), at, bt) + //TODO ctx.assignmentCompatibilityErrorCond(&n.Token2, at, bt) + //TODO } + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: n.Expression.Operand.Type(), value: val}).normalize(ctx, n) + case a.Type().Kind() == Ptr && a.Type().Elem().Kind() == Function && b.Type().Kind() == Function: + //TODO check compatible + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: val}).normalize(ctx, n) + case b.Type().Kind() == Ptr && b.Type().Elem().Kind() == Function && a.Type().Kind() == Function: + //TODO check compatible + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: b.Type(), value: val}).normalize(ctx, n) + case a.Type().Kind() != Invalid: + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: val}).normalize(ctx, n) + case b.Type().Kind() != Invalid: + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: b.Type(), value: val}).normalize(ctx, n) + default: + panic(todo("")) + } + default: + panic(todo("")) + } + return n.Operand +} + +func (n *LogicalOrExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case LogicalOrExpressionLAnd: // LogicalAndExpression + n.Operand = n.LogicalAndExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.LogicalAndExpression.IsSideEffectsFree + case LogicalOrExpressionLOr: // LogicalOrExpression "||" LogicalAndExpression + lop := n.LogicalOrExpression.check(ctx, isAsmArg) + rop := n.LogicalAndExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.LogicalOrExpression.IsSideEffectsFree && n.LogicalAndExpression.IsSideEffectsFree || + lop.Value() != nil && lop.IsNonZero() && n.LogicalOrExpression.IsSideEffectsFree + var v Value + if lop.Value() != nil && rop.Value() != nil { //TODO lop.IsNonZero shortcut + switch { + case n.LogicalOrExpression.Operand.IsNonZero() || n.LogicalAndExpression.Operand.IsNonZero(): + v = Int64Value(1) + default: + v = Int64Value(0) + } + } + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Int), value: v}).normalize(ctx, n) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *LogicalAndExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case LogicalAndExpressionOr: // InclusiveOrExpression + n.Operand = n.InclusiveOrExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.InclusiveOrExpression.IsSideEffectsFree + case LogicalAndExpressionLAnd: // LogicalAndExpression "&&" InclusiveOrExpression + lop := n.LogicalAndExpression.check(ctx, isAsmArg) + rop := n.InclusiveOrExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.LogicalAndExpression.IsSideEffectsFree && n.InclusiveOrExpression.IsSideEffectsFree || + lop.Value() != nil && lop.IsZero() && n.LogicalAndExpression.IsSideEffectsFree + var v Value + if lop.Value() != nil && rop.Value() != nil { //TODO lop.IsZero shortcut + switch { + case n.LogicalAndExpression.Operand.IsNonZero() && n.InclusiveOrExpression.Operand.IsNonZero(): + v = Int64Value(1) + default: + v = Int64Value(0) + } + } + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Int), value: v}).normalize(ctx, n) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *InclusiveOrExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case InclusiveOrExpressionXor: // ExclusiveOrExpression + n.Operand = n.ExclusiveOrExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.ExclusiveOrExpression.IsSideEffectsFree + case InclusiveOrExpressionOr: // InclusiveOrExpression '|' ExclusiveOrExpression + a := n.InclusiveOrExpression.check(ctx, isAsmArg) + b := n.ExclusiveOrExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.InclusiveOrExpression.IsSideEffectsFree && n.ExclusiveOrExpression.IsSideEffectsFree + n.promote = noType + if a.Type().Kind() == Vector || b.Type().Kind() == Vector { + n.Operand = checkBinaryVectorIntegerArtithmetic(ctx, n, a, b) + break + } + + if !a.Type().IsIntegerType() || !b.Type().IsIntegerType() { + ctx.errNode(n, "operands must be integers") + break + } + + a, b = usualArithmeticConversions(ctx, &n.Token, a, b, true) + n.promote = a.Type() + if a.Value() == nil || b.Value() == nil { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: a.Type()} + break + } + + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: a.Value().or(b.Value())}).normalize(ctx, n) + default: + panic(todo("")) + } + return n.Operand +} + +func checkBinaryVectorIntegerArtithmetic(ctx *context, n Node, a, b Operand) Operand { + var rt Type + if a.Type().Kind() == Vector { + rt = a.Type() + a = &operand{abi: &ctx.cfg.ABI, typ: a.Type().Elem()} + } + if b.Type().Kind() == Vector { + if rt == nil { + rt = b.Type() + } + b = &operand{abi: &ctx.cfg.ABI, typ: b.Type().Elem()} + } + a, b = usualArithmeticConversions(ctx, n, a, b, true) + if !a.Type().IsIntegerType() || !b.Type().IsIntegerType() { + ctx.errNode(n, "operands must be integers") + } + return &operand{abi: &ctx.cfg.ABI, typ: rt} +} + +func (n *ExclusiveOrExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case ExclusiveOrExpressionAnd: // AndExpression + n.Operand = n.AndExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.AndExpression.IsSideEffectsFree + case ExclusiveOrExpressionXor: // ExclusiveOrExpression '^' AndExpression + a := n.ExclusiveOrExpression.check(ctx, isAsmArg) + b := n.AndExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.ExclusiveOrExpression.IsSideEffectsFree && n.AndExpression.IsSideEffectsFree + if a.Type().Kind() == Vector || b.Type().Kind() == Vector { + n.Operand = checkBinaryVectorIntegerArtithmetic(ctx, n, a, b) + break + } + + if !a.Type().IsIntegerType() || !b.Type().IsIntegerType() { + ctx.errNode(n, "operands must be integers") + break + } + + a, b = usualArithmeticConversions(ctx, &n.Token, a, b, true) + n.promote = a.Type() + if a.Value() == nil || b.Value() == nil { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: a.Type()} + break + } + + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: a.Value().xor(b.Value())}).normalize(ctx, n) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *AndExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case AndExpressionEq: // EqualityExpression + n.Operand = n.EqualityExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.EqualityExpression.IsSideEffectsFree + case AndExpressionAnd: // AndExpression '&' EqualityExpression + a := n.AndExpression.check(ctx, isAsmArg) + b := n.EqualityExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.AndExpression.IsSideEffectsFree && n.EqualityExpression.IsSideEffectsFree + if a.Type().Kind() == Vector || b.Type().Kind() == Vector { + n.Operand = checkBinaryVectorIntegerArtithmetic(ctx, n, a, b) + break + } + + if !a.Type().IsIntegerType() || !b.Type().IsIntegerType() { + ctx.errNode(n, "operands must be integers") + break + } + + a, b = usualArithmeticConversions(ctx, &n.Token, a, b, true) + n.promote = a.Type() + if a.Value() == nil || b.Value() == nil { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: a.Type()} + break + } + + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: a.Value().and(b.Value())}).normalize(ctx, n) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *EqualityExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + switch n.Case { + case EqualityExpressionRel: // RelationalExpression + n.Operand = n.RelationalExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.RelationalExpression.IsSideEffectsFree + case + EqualityExpressionEq, // EqualityExpression "==" RelationalExpression + EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression + + op := &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Int)} + n.Operand = op + lo := n.EqualityExpression.check(ctx, isAsmArg) + ro := n.RelationalExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.EqualityExpression.IsSideEffectsFree && n.RelationalExpression.IsSideEffectsFree + lt := lo.Type().Decay() + rt := ro.Type().Decay() + n.promote = noType + ok := false + switch { + case lo.Type().Kind() == Vector && ro.Type().Kind() == Vector: + n.Operand = checkVectorComparison(ctx, n, lo.Type(), ro.Type()) + return n.Operand + case lt.IsArithmeticType() && rt.IsArithmeticType(): + op, _ := usualArithmeticConversions(ctx, n, lo, ro, true) + n.promote = op.Type() + ok = true + case lt.Kind() == Ptr && (rt.Kind() == Ptr || rt.IsIntegerType()): + n.promote = lt + //TODO + case (lt.Kind() == Ptr || lt.IsIntegerType()) && rt.Kind() == Ptr: + n.promote = rt + //TODO + case lt.Kind() == Function: + n.promote = ctx.cfg.ABI.Ptr(n, lt) + case rt.Kind() == Function: + n.promote = ctx.cfg.ABI.Ptr(n, rt) + default: + //TODO report error + } + if n.promote.Kind() == Invalid || !ok { + break + } + + lo = lo.convertTo(ctx, n, n.promote) + ro = ro.convertTo(ctx, n, n.promote) + if a, b := lo.Value(), ro.Value(); a != nil && b != nil { + switch n.Case { + case EqualityExpressionEq: // EqualityExpression "==" RelationalExpression + op.value = a.eq(b) + case EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression + op.value = a.neq(b) + } + } + default: + panic(todo("")) + } + return n.Operand +} + +func checkVectorComparison(ctx *context, n Node, a, b Type) (r Operand) { + a = a.underlyingType() + b = b.underlyingType() + rt := *a.(*vectorType) + rt.elem = ctx.cfg.ABI.Type(Int) + r = &operand{abi: &ctx.cfg.ABI, typ: &rt} + x := a.Elem() + y := b.Elem() + if x.Kind() != y.Kind() { + ctx.errNode(n, "cannot compare vectors of different element types: %s and %s", x, y) + } + return r +} + +func (n *RelationalExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case RelationalExpressionShift: // ShiftExpression + n.Operand = n.ShiftExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.ShiftExpression.IsSideEffectsFree + case + RelationalExpressionLt, // RelationalExpression '<' ShiftExpression + RelationalExpressionGt, // RelationalExpression '>' ShiftExpression + RelationalExpressionLeq, // RelationalExpression "<=" ShiftExpression + RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression + + n.promote = noType + op := &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Type(Int)} + n.Operand = op + lo := n.RelationalExpression.check(ctx, isAsmArg) + ro := n.ShiftExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.RelationalExpression.IsSideEffectsFree && n.ShiftExpression.IsSideEffectsFree + if lo.Type().Kind() == Vector && ro.Type().Kind() == Vector { + n.Operand = checkVectorComparison(ctx, n, lo.Type(), ro.Type()) + break + } + + if lo.Type().IsComplexType() || ro.Type().IsComplexType() { + ctx.errNode(&n.Token, "complex numbers are not ordered") + break + } + + lt := lo.Type().Decay() + rt := ro.Type().Decay() + n.promote = noType + ok := true + switch { + case lt.IsRealType() && rt.IsRealType(): + op, _ := usualArithmeticConversions(ctx, n, lo, ro, true) + n.promote = op.Type() + case lt.Kind() == Ptr && (rt.Kind() == Ptr || rt.IsIntegerType()): + n.promote = lt + //TODO + case (lt.Kind() == Ptr || lt.IsIntegerType()) && rt.Kind() == Ptr: + n.promote = rt + //TODO + default: + //TODO report error + ok = false + } + if n.promote.Kind() == Invalid || !ok { + break + } + + lo = lo.convertTo(ctx, n, n.promote) + ro = ro.convertTo(ctx, n, n.promote) + if a, b := lo.Value(), ro.Value(); a != nil && b != nil { + switch n.Case { + case RelationalExpressionLt: // RelationalExpression '<' ShiftExpression + op.value = a.lt(b) + case RelationalExpressionGt: // RelationalExpression '>' ShiftExpression + op.value = a.gt(b) + case RelationalExpressionLeq: // RelationalExpression "<=" ShiftExpression + op.value = a.le(b) + case RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression + op.value = a.ge(b) + } + } + default: + panic(todo("")) + } + return n.Operand +} + +func (n *ShiftExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case ShiftExpressionAdd: // AdditiveExpression + n.Operand = n.AdditiveExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.AdditiveExpression.IsSideEffectsFree + case ShiftExpressionLsh: // ShiftExpression "<<" AdditiveExpression + a := n.ShiftExpression.check(ctx, isAsmArg) + b := n.AdditiveExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.ShiftExpression.IsSideEffectsFree && n.AdditiveExpression.IsSideEffectsFree + if a.Type().Kind() == Vector || b.Type().Kind() == Vector { + n.Operand = checkBinaryVectorIntegerArtithmetic(ctx, n, a, b) + break + } + + if !a.Type().IsIntegerType() || !b.Type().IsIntegerType() { + //TODO report err + break + } + + a = a.integerPromotion(ctx, n) + b = b.integerPromotion(ctx, n) + n.promote = b.Type() + if a.Value() == nil || b.Value() == nil { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: a.Type()} + break + } + + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: a.Value().lsh(b.Value())}).normalize(ctx, n) + case ShiftExpressionRsh: // ShiftExpression ">>" AdditiveExpression + a := n.ShiftExpression.check(ctx, isAsmArg) + b := n.AdditiveExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.ShiftExpression.IsSideEffectsFree && n.AdditiveExpression.IsSideEffectsFree + if a.Type().Kind() == Vector || b.Type().Kind() == Vector { + n.Operand = checkBinaryVectorIntegerArtithmetic(ctx, n, a, b) + break + } + + if !a.Type().IsIntegerType() || !b.Type().IsIntegerType() { + //TODO report err + break + } + + a = a.integerPromotion(ctx, n) + b = b.integerPromotion(ctx, n) + n.promote = b.Type() + if a.Value() == nil || b.Value() == nil { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: a.Type()} + break + } + + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: a.Value().rsh(b.Value())}).normalize(ctx, n) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *AdditiveExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case AdditiveExpressionMul: // MultiplicativeExpression + n.Operand = n.MultiplicativeExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.MultiplicativeExpression.IsSideEffectsFree + case AdditiveExpressionAdd: // AdditiveExpression '+' MultiplicativeExpression + n.promote = noType + a := n.AdditiveExpression.check(ctx, isAsmArg) + b := n.MultiplicativeExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.AdditiveExpression.IsSideEffectsFree && n.MultiplicativeExpression.IsSideEffectsFree + if a.Type().Kind() == Vector || b.Type().Kind() == Vector { + n.Operand = checkBinaryVectorArtithmetic(ctx, n, a, b) + break + } + + if t := a.Type().Decay(); t.Kind() == Ptr && b.Type().IsScalarType() { + var x uintptr + hasx := false + switch v := b.Value().(type) { + case Int64Value: + x = uintptr(v) + hasx = true + case Uint64Value: + x = uintptr(v) + hasx = true + } + off := x * a.Type().Elem().Size() + switch y := a.Value().(type) { + case StringValue: + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Ptr(n, a.Type().Elem()), value: y, offset: a.Offset() + off} + default: + switch { + case a.Value() == nil && a.Declarator() != nil && hasx: + n.Operand = &lvalue{Operand: &operand{abi: &ctx.cfg.ABI, typ: ctx.cfg.ABI.Ptr(n, a.Type().Elem()), offset: a.Offset() + off}, declarator: a.Declarator()} + default: + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: t} + } + } + break + } + + if t := b.Type().Decay(); t.Kind() == Ptr && a.Type().IsScalarType() { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: t} + break + } + + if !a.Type().IsArithmeticType() || !b.Type().IsArithmeticType() { + //TODO report error + break + } + + a, b = usualArithmeticConversions(ctx, &n.Token, a, b, true) + n.promote = a.Type() + if a.Value() == nil || b.Value() == nil { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: a.Type()} + break + } + + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: a.Value().add(b.Value())}).normalize(ctx, n) + case AdditiveExpressionSub: // AdditiveExpression '-' MultiplicativeExpression + n.promote = noType + a := n.AdditiveExpression.check(ctx, isAsmArg) + b := n.MultiplicativeExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.AdditiveExpression.IsSideEffectsFree && n.MultiplicativeExpression.IsSideEffectsFree + if a.Type().Kind() == Vector || b.Type().Kind() == Vector { + n.Operand = checkBinaryVectorArtithmetic(ctx, n, a, b) + break + } + + if a.Type().Decay().Kind() == Ptr && b.Type().Decay().Kind() == Ptr { + var val Value + if a.Value() != nil && b.Value() != nil { + ae := a.Type().Decay().Elem() + be := b.Type().Decay().Elem() + switch { + case ae.Size() == be.Size(): + var d int64 + switch x := a.Value().(type) { + case Int64Value: + d = int64(x) + case Uint64Value: + d = int64(x) + } + switch x := b.Value().(type) { + case Int64Value: + val = Int64Value(d - int64(x)) + case Uint64Value: + val = Int64Value(d - int64(x)) + } + default: + ctx.errNode(n, "difference of pointers of differently sized elements") + } + } + pt := ptrdiffT(ctx, n.lexicalScope, n.Token) + n.promote = pt + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: pt, value: val} + if val != nil { + n.Operand = n.Operand.convertTo(ctx, n, a.Type()) + } + break + } + + if t := a.Type().Decay(); t.Kind() == Ptr && b.Type().IsScalarType() { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: t} + break + } + + if !a.Type().IsArithmeticType() || !b.Type().IsArithmeticType() { + //TODO report error + break + } + + a, b = usualArithmeticConversions(ctx, &n.Token, a, b, true) + n.promote = a.Type() + if a.Value() == nil || b.Value() == nil { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: a.Type()} + break + } + + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: a.Value().sub(b.Value())}).normalize(ctx, n) + default: + panic(todo("")) + } + return n.Operand +} + +func checkBinaryVectorArtithmetic(ctx *context, n Node, a, b Operand) Operand { + var rt Type + if a.Type().Kind() == Vector { + rt = a.Type() + a = &operand{abi: &ctx.cfg.ABI, typ: a.Type().Elem()} + } + if b.Type().Kind() == Vector { + if rt == nil { + rt = b.Type() + } + b = &operand{abi: &ctx.cfg.ABI, typ: b.Type().Elem()} + } + usualArithmeticConversions(ctx, n, a, b, true) + return &operand{abi: &ctx.cfg.ABI, typ: rt} +} + +func ptrdiffT(ctx *context, s Scope, tok Token) Type { + if t := ctx.ptrdiffT; t != nil { + return t + } + + t := ctx.stddef(idPtrdiffT, s, tok) + if t.Kind() != Invalid { + ctx.ptrdiffT = t + } + return t +} + +func (n *MultiplicativeExpression) check(ctx *context, isAsmArg bool) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand //TODO- + switch n.Case { + case MultiplicativeExpressionCast: // CastExpression + n.Operand = n.CastExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.CastExpression.IsSideEffectsFree + case MultiplicativeExpressionMul: // MultiplicativeExpression '*' CastExpression + a := n.MultiplicativeExpression.check(ctx, isAsmArg) + b := n.CastExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.MultiplicativeExpression.IsSideEffectsFree && n.CastExpression.IsSideEffectsFree + if a.Type().Kind() == Vector || b.Type().Kind() == Vector { + n.Operand = checkBinaryVectorArtithmetic(ctx, n, a, b) + break + } + + if !a.Type().IsArithmeticType() || !b.Type().IsArithmeticType() { + break + } + + a, b = usualArithmeticConversions(ctx, &n.Token, a, b, true) + n.promote = a.Type() + if a.Value() == nil || b.Value() == nil { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: a.Type()} + break + } + + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: a.Value().mul(b.Value())}).normalize(ctx, n) + case MultiplicativeExpressionDiv: // MultiplicativeExpression '/' CastExpression + a := n.MultiplicativeExpression.check(ctx, isAsmArg) + b := n.CastExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.MultiplicativeExpression.IsSideEffectsFree && n.CastExpression.IsSideEffectsFree + if a.Type().Kind() == Vector || b.Type().Kind() == Vector { + n.Operand = checkBinaryVectorArtithmetic(ctx, n, a, b) + break + } + + if !a.Type().IsArithmeticType() || !b.Type().IsArithmeticType() { + break + } + + a, b = usualArithmeticConversions(ctx, &n.Token, a, b, true) + n.promote = a.Type() + if a.Value() == nil || b.Value() == nil { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: a.Type()} + break + } + + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: a.Value().div(b.Value())}).normalize(ctx, n) + case MultiplicativeExpressionMod: // MultiplicativeExpression '%' CastExpression + a := n.MultiplicativeExpression.check(ctx, isAsmArg) + b := n.CastExpression.check(ctx, isAsmArg) + n.IsSideEffectsFree = n.MultiplicativeExpression.IsSideEffectsFree && n.CastExpression.IsSideEffectsFree + if a.Type().Kind() == Vector || b.Type().Kind() == Vector { + n.Operand = checkBinaryVectorArtithmetic(ctx, n, a, b) + break + } + + if !a.Type().IsArithmeticType() || !b.Type().IsArithmeticType() { + break + } + + if !a.Type().IsIntegerType() || !b.Type().IsIntegerType() { + ctx.errNode(&n.Token, "the operands of the %% operator shall have integer type.") // [0] 6.5.5, 2 + break + } + + a, b = usualArithmeticConversions(ctx, &n.Token, a, b, true) + n.promote = a.Type() + if a.Value() == nil || b.Value() == nil { + n.Operand = &operand{abi: &ctx.cfg.ABI, typ: a.Type()} + break + } + + n.Operand = (&operand{abi: &ctx.cfg.ABI, typ: a.Type(), value: a.Value().mod(b.Value())}).normalize(ctx, n) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *Declarator) check(ctx *context, td typeDescriptor, typ Type, tld bool) Type { + if n == nil { + n.typ = ctx.cfg.ABI.Type(Int) + return noType + } + + typ = n.Pointer.check(ctx, typ) + n.td = td + if attr := n.AttributeSpecifierList.check(ctx, typ.baseP()); len(attr) != 0 { + typ = &attributedType{typ, attr} + } + n.typ = n.DirectDeclarator.check(ctx, typ) + + hasStorageSpecifiers := td.typedef() || td.extern() || td.static() || + td.auto() || td.register() || td.threadLocal() + + typ = n.typ + if typ == nil { + n.typ = ctx.cfg.ABI.Type(Int) + ctx.errNode(n, "declarator has unsupported, invalid or incomplete type") + return noType + } + + if typ.Kind() == Array && typ.IsVLA() { + if f := ctx.checkFn; f != nil { + f.VLAs = append(f.VLAs, n) + } + } + + // 6.2.2 Linkages of identifiers + n.Linkage = None + switch { + case tld && td.static(): + // 3: If the declaration of a file scope identifier for an object or a function + // contains the storage-class specifier static, the identifier has internal + // linkage. + n.Linkage = Internal + case td.extern(): + //TODO + // + // 4: For an identifier declared with the storage-class specifier extern in a + // scope in which a prior declaration of that identifier is visible, 23) if the + // prior declaration specifies internal or external linkage, the linkage of the + // identifier at the later declaration is the same as the linkage specified at + // the prior declaration. If no prior declaration is visible, or if the prior + // declaration specifies no linkage, then the identifier has external linkage. + n.Linkage = External + case + !n.IsParameter && typ.Kind() == Function && !hasStorageSpecifiers, + tld && !hasStorageSpecifiers: + + // 5: If the declaration of an identifier for a function has no storage-class + // specifier, its linkage is determined exactly as if it were declared with the + // storage-class specifier extern. + n.Linkage = External + } + + // 6.2.4 Storage durations of objects + switch { + case n.Linkage == External, n.Linkage == Internal, td.static(): + // 2: An object whose identifier is declared with external or internal linkage, + // or with the storage-class specifier static has static storage duration. Its + // lifetime is the entire execution of the program and its stored value is + // initialized only once, prior to + // program startup. + n.StorageClass = Static + case n.Linkage == None && !td.static(): + // 4: An object whose identifier is declared with no linkage and without the + // storage-class specifier static has automatic storage duration. + n.StorageClass = Automatic + } + switch { + case n.typ.Kind() == Invalid: + ctx.errNode(n, "declarator has incomplete type") + } + if n.IsTypedefName { + if k, ok := complexTypedefs[n.Name()]; ok { + abi := ctx.cfg.ABI + t := n.typ.Alias() + t.setKind(k) + abi.types[k] = t + abi.Types[k] = ABIType{Size: t.Size(), Align: t.Align(), FieldAlign: t.FieldAlign()} + } + } + switch expr, ok := n.AttributeSpecifierList.Has(idVectorSize, idVectorSize2); { + case ok: + n.vectorize(ctx, expr) + default: + switch x := td.(type) { + case *DeclarationSpecifiers: + for ; x != nil; x = x.DeclarationSpecifiers { + if expr, ok := x.AttributeSpecifier.Has(idVectorSize, idVectorSize2); ok { + n.vectorize(ctx, expr) + break + } + } + } + } + return n.typ +} + +func (n *Declarator) vectorize(ctx *context, expr *ExpressionList) { + dst := &n.typ + elem := n.typ + switch n.typ.Kind() { + case Function: + dst = &n.typ.(*functionType).result + elem = n.typ.Result() + } + + sz := expr.vectorSize(ctx) + if sz == 0 { + sz = elem.Size() + } + if elem.Size() == 0 { + ctx.errNode(expr, "vector element has zero size") + return + } + + if sz%elem.Size() != 0 { + ctx.errNode(expr, "vector size must be a multiple of the base size") + } + b := n.typ.base() + b.size = sz + b.kind = byte(Vector) + *dst = &vectorType{ + typeBase: b, + elem: elem, + length: sz / elem.Size(), + } +} + +func (n *ExpressionList) vectorSize(ctx *context) (r uintptr) { + if n.ExpressionList != nil { + ctx.errNode(n, "expected single expression") + return 0 + } + + switch x := n.AssignmentExpression.Operand.Value().(type) { + case Int64Value: + if x <= 0 { + ctx.errNode(n, "expected integer greater than zero") + return 0 + } + + r = uintptr(x) + case Uint64Value: + r = uintptr(x) + case nil: + ctx.errNode(n, "expected constant expression") + r = 0 + default: + panic(todo("%T", x)) + } + if bits.OnesCount64(uint64(r)) != 1 { + ctx.errNode(n, "expected a power of two") + r = 0 + } + return r +} + +func (n *DirectDeclarator) check(ctx *context, typ Type) Type { + if n == nil { + return noType + } + + switch n.Case { + case DirectDeclaratorIdent: // IDENTIFIER Asm + n.Asm.check(ctx) + return typ + case DirectDeclaratorDecl: // '(' AttributeSpecifierList Declarator ')' + n.AttributeSpecifierList.check(ctx, typ.baseP()) + return n.Declarator.check(ctx, noTypeDescriptor, typ, false) + case DirectDeclaratorArr: // DirectDeclarator '[' TypeQualifiers AssignmentExpression ']' + return n.DirectDeclarator.check(ctx, checkArray(ctx, &n.Token, typ, n.AssignmentExpression, true, false)) + case DirectDeclaratorStaticArr: // DirectDeclarator '[' "static" TypeQualifiers AssignmentExpression ']' + return n.DirectDeclarator.check(ctx, checkArray(ctx, &n.Token, typ, n.AssignmentExpression, false, false)) + case DirectDeclaratorArrStatic: // DirectDeclarator '[' TypeQualifiers "static" AssignmentExpression ']' + return n.DirectDeclarator.check(ctx, checkArray(ctx, &n.Token, typ, n.AssignmentExpression, false, false)) + case DirectDeclaratorStar: // DirectDeclarator '[' TypeQualifiers '*' ']' + return n.DirectDeclarator.check(ctx, checkArray(ctx, &n.Token, typ, nil, true, true)) + case DirectDeclaratorFuncParam: // DirectDeclarator '(' ParameterTypeList ')' + ft := &functionType{typeBase: typeBase{kind: byte(Function)}, result: typ} + if typ != nil && typ.Inline() { + ft.typeBase.flags = fInline + } + n.ParameterTypeList.check(ctx, ft) + return n.DirectDeclarator.check(ctx, ft) + case DirectDeclaratorFuncIdent: // DirectDeclarator '(' IdentifierList ')' + ft := &functionType{typeBase: typeBase{kind: byte(Function)}, result: typ, paramList: n.IdentifierList.check(ctx)} + if typ != nil && typ.Inline() { + ft.typeBase.flags = fInline + } + if n.idListNoDeclList { + n.checkIdentList(ctx, ft) + } + return n.DirectDeclarator.check(ctx, ft) + } + + panic(internalErrorf("%v: %v", n.Position(), n.Case)) +} + +func (n *DirectDeclarator) checkIdentList(ctx *context, ft *functionType) { + s := n.paramScope + for _, nm := range ft.paramList { + d := s[nm][0].(*Declarator) + d.check(ctx, noTypeDescriptor, ctx.cfg.ABI.Type(Int), false) + ft.params = append(ft.params, &Parameter{d, d.Type()}) + } +} + +func checkArray(ctx *context, n Node, typ Type, expr *AssignmentExpression, exprIsOptional, noExpr bool) Type { //TODO pass and use typeQualifiers + if typ == nil { + ctx.errNode(n, "array of invalid or incomplete type") + return noType + } + + b := typ.base() + b.align = byte(typ.Align()) + b.fieldAlign = byte(typ.FieldAlign()) + b.kind = byte(Array) + switch { + case expr != nil && noExpr: + panic(todo("")) + case expr != nil: + op := expr.check(ctx, false) + if op.Type().Kind() == Invalid { + return noType + } + + if !op.Type().IsIntegerType() { + //TODO report err + return noType + } + + var length uintptr + var vla bool + var vlaExpr *AssignmentExpression + switch x := op.Value().(type) { + case nil: + vla = true + vlaExpr = expr + case Int64Value: + length = uintptr(x) + case Uint64Value: + length = uintptr(x) + } + switch { + case vla: + b.size = ctx.cfg.ABI.Types[Ptr].Size + default: + if typ.IsIncomplete() { + //TODO report error + return noType + } + + b.size = length * typ.Size() + } + return &arrayType{typeBase: b, decay: ctx.cfg.ABI.Ptr(n, typ), elem: typ, length: length, vla: vla, expr: vlaExpr} + case noExpr: + // nop + case !exprIsOptional: + panic(todo("")) + } + b.flags |= fIncomplete + return &arrayType{typeBase: b, decay: ctx.cfg.ABI.Ptr(n, typ), elem: typ} +} + +func (n *IdentifierList) check(ctx *context) (r []StringID) { + for ; n != nil; n = n.IdentifierList { + tok := n.Token2.Value + if tok == 0 { + tok = n.Token.Value + } + r = append(r, tok) + } + return r +} + +func (n *Asm) check(ctx *context) { + if n == nil { + return + } + + n.AsmQualifierList.check(ctx) + n.AsmArgList.check(ctx) +} + +func (n *AsmArgList) check(ctx *context) { + for ; n != nil; n = n.AsmArgList { + n.AsmExpressionList.check(ctx) + } +} + +func (n *AsmExpressionList) check(ctx *context) { + if ctx.cfg.DoNotTypecheckAsm { + return + } + + for ; n != nil; n = n.AsmExpressionList { + n.AsmIndex.check(ctx) + n.AssignmentExpression.check(ctx, true) + } +} + +func (n *AsmIndex) check(ctx *context) { + if n == nil { + return + } + + n.Expression.check(ctx, true) +} + +func (n *AsmQualifierList) check(ctx *context) { + for ; n != nil; n = n.AsmQualifierList { + n.AsmQualifier.check(ctx) + } +} + +func (n *AsmQualifier) check(ctx *context) { + if n == nil { + return + } + + switch n.Case { + case AsmQualifierVolatile: // "volatile" + //TODO + case AsmQualifierInline: // "inline" + //TODO + case AsmQualifierGoto: // "goto" + //TODO + default: + panic(todo("")) + } +} + +func (n *AttributeSpecifierList) check(ctx *context, t *typeBase) (a []*AttributeSpecifier) { + for ; n != nil; n = n.AttributeSpecifierList { + a = append(a, n.AttributeSpecifier.check(ctx, t)) + } + return a +} + +func (n *AttributeSpecifier) check(ctx *context, t *typeBase) *AttributeSpecifier { + if n == nil { + return nil + } + + n.AttributeValueList.check(ctx, t) + return n +} + +func (n *AttributeValueList) check(ctx *context, t *typeBase) { + for ; n != nil; n = n.AttributeValueList { + n.AttributeValue.check(ctx, t) + } +} + +func (n *AttributeValue) check(ctx *context, t *typeBase) { + if n == nil { + return + } + + switch n.Case { + case AttributeValueIdent: // IDENTIFIER + if n.Token.Value == idPacked && t != nil { + t.flags |= fPacked + } + case AttributeValueExpr: // IDENTIFIER '(' ExpressionList ')' + v := ctx.cfg.ignoreErrors + ctx.cfg.ignoreErrors = true + defer func() { ctx.cfg.ignoreErrors = v }() + n.ExpressionList.check(ctx, false) + if n.Token.Value == idAligned && n.ExpressionList != nil && t != nil { + switch x := n.ExpressionList.AssignmentExpression.Operand.Value().(type) { + case Int64Value: + t.setAligned(int(x)) + switch t.Kind() { + case Struct, Union: + ctx.structs[StructInfo{Size: t.Size(), Align: t.Align()}] = struct{}{} + } + } + } + default: + panic(todo("")) + } +} + +func (n *ExpressionList) check(ctx *context, isAsmArg bool) { + for ; n != nil; n = n.ExpressionList { + n.AssignmentExpression.check(ctx, isAsmArg) + } +} + +func (n *DeclarationSpecifiers) check(ctx *context, inUnion bool) (r Type, inline, noret bool) { + n0 := n + typ := &typeBase{} + for ; n != nil; n = n.DeclarationSpecifiers { + switch n.Case { + case DeclarationSpecifiersStorage: // StorageClassSpecifier DeclarationSpecifiers + n.StorageClassSpecifier.check(ctx, n) + case DeclarationSpecifiersTypeSpec: // TypeSpecifier DeclarationSpecifiers + n.TypeSpecifier.check(ctx, typ, inUnion) + case DeclarationSpecifiersTypeQual: // TypeQualifier DeclarationSpecifiers + n.TypeQualifier.check(ctx, typ) + case DeclarationSpecifiersFunc: // FunctionSpecifier DeclarationSpecifiers + if n.FunctionSpecifier == nil { + break + } + + switch n.FunctionSpecifier.Case { + case FunctionSpecifierInline: // "inline" + inline = true + case FunctionSpecifierNoreturn: // "_Noreturn" + noret = true + default: + panic(todo("")) + } + case DeclarationSpecifiersAlignSpec: // AlignmentSpecifier DeclarationSpecifiers + n.AlignmentSpecifier.check(ctx) + case DeclarationSpecifiersAttribute: // AttributeSpecifier DeclarationSpecifiers + n.AttributeSpecifier.check(ctx, typ) + default: + panic(todo("")) + } + } + r = typ.check(ctx, n0, true) + return r, inline, noret +} + +func (n *AlignmentSpecifier) check(ctx *context) { + if n == nil { + return + } + + switch n.Case { + case AlignmentSpecifierAlignasType: // "_Alignas" '(' TypeName ')' + n.TypeName.check(ctx, false, false, nil) + //TODO actually set the alignment + case AlignmentSpecifierAlignasExpr: // "_Alignas" '(' ConstantExpression ')' + n.ConstantExpression.check(ctx, ctx.mode|mIntConstExpr, false) + //TODO actually set the alignment + default: + panic(todo("")) + } +} + +func (n *StorageClassSpecifier) check(ctx *context, ds *DeclarationSpecifiers) { + if n == nil { + return + } + + switch n.Case { + case StorageClassSpecifierTypedef: // "typedef" + ds.class |= fTypedef + case StorageClassSpecifierExtern: // "extern" + ds.class |= fExtern + case StorageClassSpecifierStatic: // "static" + ds.class |= fStatic + case StorageClassSpecifierAuto: // "auto" + ds.class |= fAuto + case StorageClassSpecifierRegister: // "register" + ds.class |= fRegister + case StorageClassSpecifierThreadLocal: // "_Thread_local" + ds.class |= fThreadLocal + default: + panic(todo("")) + } + c := bits.OnesCount(uint(ds.class & (fTypedef | fExtern | fStatic | fAuto | fRegister | fThreadLocal))) + if c == 1 { + return + } + + // [2], 6.7.1, 2 + if c == 2 && ds.class&fThreadLocal != 0 { + if ds.class&(fStatic|fExtern) != 0 { + return + } + } + + ctx.errNode(n, "at most, one storage-class specifier may be given in the declaration specifiers in a declaration") +} + +// DeclarationSpecifiers Declarator DeclarationList CompoundStatement +func (n *FunctionDefinition) checkDeclarator(ctx *context) { + if n == nil { + return + } + + n.Declarator.fnDef = true + n.Declarator.funcDefinition = n + ctx.checkFn = n + typ, inline, noret := n.DeclarationSpecifiers.check(ctx, false) + typ = n.Declarator.check(ctx, n.DeclarationSpecifiers, typ, true) + typ.setFnSpecs(inline, noret) + ctx.checkFn = nil + n.DeclarationList.checkFn(ctx, typ, n.Declarator.ParamScope()) +} + +func (n *DeclarationList) checkFn(ctx *context, typ Type, s Scope) { + if n == nil { + return + } + + n.check(ctx) + ft, ok := typ.(*functionType) + if !ok { + return + } + + if ft.params != nil { + //TODO report error + return + } + + if len(ft.paramList) == 0 { + //TODO report error + return + } + + m := make(map[StringID]int, len(ft.paramList)) + for i, v := range ft.paramList { + if _, ok := m[v]; ok { + ctx.errNode(n, "duplicate parameter: %s", v) + continue + } + + m[v] = i + } + params := make([]*Parameter, len(m)) + i := 0 + for ; n != nil; n = n.DeclarationList { + for n := n.Declaration.InitDeclaratorList; n != nil; n = n.InitDeclaratorList { + n := n.InitDeclarator + switch n.Case { + case InitDeclaratorDecl: // Declarator AttributeSpecifierList + nm := n.Declarator.Name() + n.Declarator.IsParameter = true + switch x, ok := m[nm]; { + case ok: + params[x] = &Parameter{d: n.Declarator, typ: n.Declarator.Type()} + i++ + default: + //TODO report error + } + case InitDeclaratorInit: // Declarator AttributeSpecifierList '=' Initializer + //TODO report error + return + default: + panic(todo("")) + } + } + } + for i, v := range params { + if v != nil { + continue + } + + nm := ft.paramList[i] + d := &Declarator{ + DirectDeclarator: &DirectDeclarator{ + Case: DirectDeclaratorIdent, + Token: Token{Rune: IDENTIFIER, Value: nm}, + }, + IsParameter: true, + Linkage: None, + StorageClass: Automatic, + typ: ctx.cfg.ABI.Type(Int), + } + s.declare(nm, d) + params[i] = &Parameter{d, d.typ} + } + ft.params = params +} + +func (n *CompoundStatement) check(ctx *context) Operand { + n.Operand = n.BlockItemList.check(ctx) + return n.Operand +} + +func (n *BlockItemList) check(ctx *context) (r Operand) { + r = noOperand + var last *BlockItem + for ; n != nil; n = n.BlockItemList { + last = n.BlockItem + r = n.BlockItem.check(ctx) + } + if last != nil { + last.Last = true + } + return r +} + +func (n *BlockItem) check(ctx *context) Operand { + if n == nil { + return noOperand + } + + switch n.Case { + case BlockItemDecl: // Declaration + n.Declaration.check(ctx, false) + case BlockItemStmt: // Statement + return n.Statement.check(ctx) + case BlockItemLabel: // LabelDeclaration + n.LabelDeclaration.check(ctx) + case BlockItemFuncDef: // DeclarationSpecifiers Declarator CompoundStatement + ctxClosure := ctx.closure + ctx.closure = nil + ctxCheckFn := ctx.checkFn + fn := &FunctionDefinition{ + DeclarationSpecifiers: n.DeclarationSpecifiers, + Declarator: n.Declarator, + CompoundStatement: n.CompoundStatement, + } + n.fn = fn + ctx.checkFn = fn + n.CompoundStatement.scope.declare(idClosure, n) + fn.checkDeclarator(ctx) + ctxCapture := ctx.capture + ctx.capture = true + fn.checkBody(ctx) + n.closure = ctx.closure + ctx.capture = ctxCapture + delete(n.CompoundStatement.scope, idClosure) + ctx.checkFn = ctxCheckFn + ctx.closure = ctxClosure + case BlockItemPragma: // PragmaSTDC + n.PragmaSTDC.check(ctx) + default: + panic(todo("")) + } + return noOperand +} + +func (n *LabelDeclaration) check(ctx *context) { + if n == nil { + return + } + + n.IdentifierList.check(ctx) +} + +func (n *Statement) check(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.Operand = noOperand + switch n.Case { + case StatementLabeled: // LabeledStatement + n.LabeledStatement.check(ctx) + case StatementCompound: // CompoundStatement + n.Operand = n.CompoundStatement.check(ctx) + case StatementExpr: // ExpressionStatement + n.Operand = n.ExpressionStatement.check(ctx) + case StatementSelection: // SelectionStatement + n.SelectionStatement.check(ctx) + case StatementIteration: // IterationStatement + n.IterationStatement.check(ctx) + case StatementJump: // JumpStatement + n.JumpStatement.check(ctx) + case StatementAsm: // AsmStatement + n.AsmStatement.check(ctx) + default: + panic(todo("")) + } + return n.Operand +} + +func (n *JumpStatement) check(ctx *context) { + if n == nil { + return + } + + switch n.Case { + case JumpStatementGoto: // "goto" IDENTIFIER ';' + n.context = ctx.breakCtx + if ctx.checkFn.Gotos == nil { + ctx.checkFn.Gotos = map[StringID]*JumpStatement{} + } + ctx.checkFn.Gotos[n.Token2.Value] = n + case JumpStatementGotoExpr: // "goto" '*' Expression ';' + n.Expression.check(ctx, false) + //TODO + case JumpStatementContinue: // "continue" ';' + n.context = ctx.breakCtx + if ctx.continues <= 0 { + panic(n.Position().String()) + } + //TODO + case JumpStatementBreak: // "break" ';' + n.context = ctx.breakCtx + if ctx.breaks <= 0 { + panic(n.Position().String()) + } + //TODO + case JumpStatementReturn: // "return" Expression ';' + n.context = ctx.breakCtx + op := n.Expression.check(ctx, false) + if op.Type().IsComplexType() { + ctx.checkFn.ReturnComplexExpr = append(ctx.checkFn.ReturnComplexExpr, n.Expression) + } + default: + panic(todo("")) + } +} + +func (n *IterationStatement) check(ctx *context) { + if n == nil { + return + } + + sv := ctx.breakCtx + ctx.breakCtx = n + + defer func() { ctx.breakCtx = sv }() + + switch n.Case { + case IterationStatementWhile: // "while" '(' Expression ')' Statement + n.Expression.check(ctx, false) + ctx.breaks++ + ctx.continues++ + n.Statement.check(ctx) + ctx.breaks-- + ctx.continues-- + case IterationStatementDo: // "do" Statement "while" '(' Expression ')' ';' + ctx.breaks++ + ctx.continues++ + n.Statement.check(ctx) + ctx.breaks-- + ctx.continues-- + n.Expression.check(ctx, false) + case IterationStatementFor: // "for" '(' Expression ';' Expression ';' Expression ')' Statement + n.Expression.check(ctx, false) + n.Expression2.check(ctx, false) + n.Expression3.check(ctx, false) + ctx.breaks++ + ctx.continues++ + n.Statement.check(ctx) + ctx.breaks-- + ctx.continues-- + case IterationStatementForDecl: // "for" '(' Declaration Expression ';' Expression ')' Statement + n.Declaration.check(ctx, false) + n.Expression.check(ctx, false) + n.Expression2.check(ctx, false) + ctx.breaks++ + ctx.continues++ + n.Statement.check(ctx) + ctx.breaks-- + ctx.continues-- + default: + panic(todo("")) + } +} + +func (n *SelectionStatement) check(ctx *context) { + if n == nil { + return + } + + switch n.Case { + case SelectionStatementIf: // "if" '(' Expression ')' Statement + n.Expression.check(ctx, false) + n.Statement.check(ctx) + case SelectionStatementIfElse: // "if" '(' Expression ')' Statement "else" Statement + n.Expression.check(ctx, false) + n.Statement.check(ctx) + n.Statement2.check(ctx) + if !n.Expression.Operand.Type().IsScalarType() { + //TODO report err + break + } + case SelectionStatementSwitch: // "switch" '(' Expression ')' Statement + if n == nil { + return + } + + sv := ctx.breakCtx + ctx.breakCtx = n + + defer func() { ctx.breakCtx = sv }() + + op := n.Expression.check(ctx, false) + n.promote = op.integerPromotion(ctx, n).Type() + cp := ctx.casePromote + ctx.casePromote = n.promote + cs := ctx.cases + ctx.cases = nil + ctx.switches++ + ctx.breaks++ + n.Statement.check(ctx) + ctx.breaks-- + ctx.switches-- + n.cases = ctx.cases + ctx.cases = cs + ctx.casePromote = cp + default: + panic(todo("")) + } +} + +func (n *ExpressionStatement) check(ctx *context) Operand { + if n == nil { + return noOperand + } + + n.AttributeSpecifierList.check(ctx, nil) + return n.Expression.check(ctx, false) +} + +func (n *LabeledStatement) check(ctx *context) { + if n == nil { + return + } + + switch n.Case { + case LabeledStatementLabel: // IDENTIFIER ':' AttributeSpecifierList Statement + if ctx.checkFn.Labels == nil { + ctx.checkFn.Labels = map[StringID]*LabeledStatement{} + } + if _, ok := ctx.checkFn.Labels[n.Token.Value]; ok { + //TODO report redeclared + } + ctx.checkFn.Labels[n.Token.Value] = n + n.AttributeSpecifierList.check(ctx, nil) + n.Statement.check(ctx) + case LabeledStatementCaseLabel: // "case" ConstantExpression ':' Statement + if ctx.switches <= 0 { + //TODO report error + break + } + + switch op := n.ConstantExpression.check(ctx, ctx.mode|mIntConstExpr, false); op.Value().(type) { + case Int64Value, Uint64Value: + if t := ctx.casePromote; t.Kind() != Invalid { + n.ConstantExpression.Operand = op.convertTo(ctx, n, t) + break + } + + //TODO report error + default: + //TODO report error + } + ctx.cases = append(ctx.cases, n) + n.Statement.check(ctx) + case LabeledStatementRange: // "case" ConstantExpression "..." ConstantExpression ':' Statement + if ctx.switches <= 0 { + //TODO report error + break + } + + switch n.ConstantExpression.check(ctx, ctx.mode|mIntConstExpr, false).Value().(type) { + case Int64Value, Uint64Value: + // ok + default: + //TODO report error + } + switch n.ConstantExpression2.check(ctx, ctx.mode|mIntConstExpr, false).Value().(type) { + case Int64Value, Uint64Value: + // ok + default: + //TODO report error + } + ctx.cases = append(ctx.cases, n) + n.Statement.check(ctx) + case LabeledStatementDefault: // "default" ':' Statement + if ctx.switches <= 0 { + //TODO report error + break + } + + ctx.cases = append(ctx.cases, n) + n.Statement.check(ctx) + default: + panic(todo("")) + } +} + +func (n *DeclarationList) check(ctx *context) { + for ; n != nil; n = n.DeclarationList { + n.Declaration.check(ctx, false) + } +} + +func setAddressTaken(n Node, d *Declarator, s string) { + d.AddressTaken = true + // fmt.Printf("%v: %s, type %v (%v, %v), declared at %v, AddressTaken = true: %v\n", + // n.Position(), d.Name(), d.Type(), d.Type().Kind(), d.Type().Size(), d.Position(), s, + // ) //TODO- +} + +// Dump returns a debug form of n. +func (n *Initializer) Dump() string { + var b strings.Builder + f := strutil.IndentFormatter(&b, "\t") + n.dump(f) + return b.String() +} + +func pos(n Node) (r token.Position) { + if n == nil { + return r + } + + r = token.Position(n.Position()) + if r.IsValid() { + r.Filename = filepath.Base(r.Filename) + } + return r +} + +func (n *Initializer) dump(f strutil.Formatter) { + list := n.List() + if len(list) != 0 { + for i, v := range list { + f.Format("Initializer.List() #%d/%d: %v: off %v type %v", i, len(list), pos(v), v.Offset, v.Type()) + if fld := v.FirstDesignatorField(); fld != nil { + f.Format(" [FirstDesignatorField %q]", fld.Name()) + } + f.Format("\n") + } + } + if f0 := n.FirstDesignatorField(); f0 != nil { + f.Format("[FirstDesignatorField: %q, index %v, off %v, type %v] ", f0.Name(), f0.Index(), n.Offset, n.Type().Alias()) + } + switch n.Case { + case InitializerExpr: // AssignmentExpression + if op := n.AssignmentExpression.Operand; op != nil { + n.isConst = op.IsConst() + n.isZero = op.IsZero() + } + var t Type + if n.AssignmentExpression != nil && n.AssignmentExpression.Operand != nil { + t = n.AssignmentExpression.Operand.Type() + } + f.Format("%v: %T@%[2]p, .Case %v, off %v, type %v\n", pos(n), n, n.Case, n.Offset, t.Alias()) + case InitializerInitList: // '{' InitializerList ',' '}' + n.InitializerList.dump(f) + default: + panic(todo("%v:", n.Position())) + } +} + +// Dump returns a debug form of n. +func (n *InitializerList) Dump() string { + var b strings.Builder + f := strutil.IndentFormatter(&b, "\t") + n.dump(f) + return b.String() +} + +func (n *InitializerList) dump(f strutil.Formatter) { + if n == nil { + f.Format("<nil>") + return + } + + f.Format("%v: %T@%[2]p, len(.List()) %v {%i\n", pos(n), n, len(n.List())) + list := n.List() + for ; n != nil; n = n.InitializerList { + n.Designation.dump(f) + n.Initializer.dump(f) + } + for i, v := range list { + f.Format("InitializerList.List() #%d/%d:", i, len(list)) + v.dump(f) + } + f.Format("%u}\n") +} + +func (n *Designation) dump(f strutil.Formatter) { + if n == nil { + return + } + + cnt := 0 + designatorField2 := false + for n := n.DesignatorList; n != nil; n = n.DesignatorList { + n.Designator.dump(f) + if n.Designator.Case == DesignatorField2 { + designatorField2 = true + } + cnt++ + } + if cnt > 1 || !designatorField2 { + f.Format(" = ") + } +} + +func (n *Designator) dump(f strutil.Formatter) { + switch n.Case { + case DesignatorIndex: // '[' ConstantExpression ']' + f.Format("[%v]", n.ConstantExpression.Operand.Value()) + case DesignatorField: // '.' IDENTIFIER + f.Format(".%s", n.Token2.Value) + case DesignatorField2: // IDENTIFIER ':' + f.Format("%s:", n.Token.Value) + default: + panic(todo("")) + } +} |