<commit_before>#
#
#           The Nim Compiler
#        (c) Copyright 2015 <NAME>
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# this module folds constants; used by semantic checking phase
# and evaluation phase

import
  strutils, options, ast, astalgo, trees, treetab, nimsets,
  nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
  commands, magicsys, modulegraphs, strtabs, lineinfos

import system/helpers2

proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  case skipTypes(n.typ, abstractVarRange).kind
  of tyInt:
    result = newIntNode(nkIntLit, intVal)
    # See bug #6989. 'pred' et al only produce an int literal type if the
    # original type was 'int', not a distinct int etc.
    if n.typ.kind == tyInt:
      result.typ = getIntLitType(g, result)
    else:
      result.typ = n.typ
    # hrm, this is not correct: 1 + high(int) shouldn't produce tyInt64 ...
    #setIntLitType(result)
  of tyChar:
    result = newIntNode(nkCharLit, intVal)
    result.typ = n.typ
  else:
    result = newIntNode(nkIntLit, intVal)
    result.typ = n.typ
  result.info = n.info

proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
  result = newFloatNode(nkFloatLit, floatVal)
  result.typ = n.typ
  result.info = n.info

proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
  result = newStrNode(nkStrLit, strVal)
  result.typ = n.typ
  result.info = n.info

proc getConstExpr*(m: PSym, n: PNode; g: ModuleGraph): PNode
  # evaluates the constant expression or returns nil if it is no constant
  # expression
proc evalOp*(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode

proc checkInRange(conf: ConfigRef; n: PNode, res: BiggestInt): bool =
  if res in firstOrd(conf, n.typ)..lastOrd(conf, n.typ):
    result = true

proc foldAdd(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  let res = a +% b
  if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and
      checkInRange(g.config, n, res):
    result = newIntNodeT(res, n, g)

proc foldSub*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  let res = a -% b
  if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and
      checkInRange(g.config, n, res):
    result = newIntNodeT(res, n, g)

proc foldUnarySub(a: BiggestInt, n: PNode, g: ModuleGraph): PNode =
  if a != firstOrd(g.config, n.typ):
    result = newIntNodeT(-a, n, g)

proc foldAbs*(a: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  if a != firstOrd(g.config, n.typ):
    result = newIntNodeT(abs(a), n, g)

proc foldMod*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  if b != 0'i64:
    result = newIntNodeT(a mod b, n, g)

proc foldModU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  if b != 0'i64:
    result = newIntNodeT(a %% b, n, g)

proc foldDiv*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  if b != 0'i64 and (a != firstOrd(g.config, n.typ) or b != -1'i64):
    result = newIntNodeT(a div b, n, g)

proc foldDivU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  if b != 0'i64:
    result = newIntNodeT(a /% b, n, g)

proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  let res = a *% b
  let floatProd = toBiggestFloat(a) * toBiggestFloat(b)
  let resAsFloat = toBiggestFloat(res)

  # Fast path for normal case: small multiplicands, and no info
  # is lost in either method.
  if resAsFloat == floatProd and checkInRange(g.config, n, res):
    return newIntNodeT(res, n, g)

  # Somebody somewhere lost info. Close enough, or way off? Note
  # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
  # The difference either is or isn't significant compared to the
  # true value (of which floatProd is a good approximation).

  # abs(diff)/abs(prod) <= 1/32 iff
  #   32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
  if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and
      checkInRange(g.config, n, res):
    return newIntNodeT(res, n, g)

proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
  # because $ has the param ordinal[T], `a` is not necessarily an enum, but an
  # ordinal
  var x = getInt(a)

  var t = skipTypes(a.typ, abstractRange)
  case t.kind
  of tyChar:
    result = $chr(int(x) and 0xff)
  of tyEnum:
    var n = t.n
    for i in countup(0, sonsLen(n) - 1):
      if n.sons[i].kind != nkSym: internalError(g.config, a.info, "ordinalValToString")
      var field = n.sons[i].sym
      if field.position == x:
        if field.ast == nil:
          return field.name.s
        else:
          return field.ast.strVal
    internalError(g.config, a.info, "no symbol for ordinal value: " & $x)
  else:
    result = $x

proc isFloatRange(t: PType): bool {.inline.} =
  result = t.kind == tyRange and t.sons[0].kind in {tyFloat..tyFloat128}

proc isIntRange(t: PType): bool {.inline.} =
  result = t.kind == tyRange and t.sons[0].kind in {
      tyInt..tyInt64, tyUInt8..tyUInt32}

proc pickIntRange(a, b: PType): PType =
  if isIntRange(a): result = a
  elif isIntRange(b): result = b
  else: result = a

proc isIntRangeOrLit(t: PType): bool =
  result = isIntRange(t) or isIntLit(t)

proc makeRange(typ: PType, first, last: BiggestInt; g: ModuleGraph): PType =
  let minA = min(first, last)
  let maxA = max(first, last)<commit_msg>Fix internal error when casting to invalid enum value<commit_after>#
#
#           The Nim Compiler
#        (c) Copyright 2015 <NAME>
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# this module folds constants; used by semantic checking phase
# and evaluation phase

import
  strutils, options, ast, astalgo, trees, treetab, nimsets,
  nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
  commands, magicsys, modulegraphs, strtabs, lineinfos

import system/helpers2

proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  case skipTypes(n.typ, abstractVarRange).kind
  of tyInt:
    result = newIntNode(nkIntLit, intVal)
    # See bug #6989. 'pred' et al only produce an int literal type if the
    # original type was 'int', not a distinct int etc.
    if n.typ.kind == tyInt:
      result.typ = getIntLitType(g, result)
    else:
      result.typ = n.typ
    # hrm, this is not correct: 1 + high(int) shouldn't produce tyInt64 ...
    #setIntLitType(result)
  of tyChar:
    result = newIntNode(nkCharLit, intVal)
    result.typ = n.typ
  else:
    result = newIntNode(nkIntLit, intVal)
    result.typ = n.typ
  result.info = n.info

proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
  result = newFloatNode(nkFloatLit, floatVal)
  result.typ = n.typ
  result.info = n.info

proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
  result = newStrNode(nkStrLit, strVal)
  result.typ = n.typ
  result.info = n.info

proc getConstExpr*(m: PSym, n: PNode; g: ModuleGraph): PNode
  # evaluates the constant expression or returns nil if it is no constant
  # expression
proc evalOp*(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode

proc checkInRange(conf: ConfigRef; n: PNode, res: BiggestInt): bool =
  if res in firstOrd(conf, n.typ)..lastOrd(conf, n.typ):
    result = true

proc foldAdd(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  let res = a +% b
  if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and
      checkInRange(g.config, n, res):
    result = newIntNodeT(res, n, g)

proc foldSub*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  let res = a -% b
  if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and
      checkInRange(g.config, n, res):
    result = newIntNodeT(res, n, g)

proc foldUnarySub(a: BiggestInt, n: PNode, g: ModuleGraph): PNode =
  if a != firstOrd(g.config, n.typ):
    result = newIntNodeT(-a, n, g)

proc foldAbs*(a: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  if a != firstOrd(g.config, n.typ):
    result = newIntNodeT(abs(a), n, g)

proc foldMod*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  if b != 0'i64:
    result = newIntNodeT(a mod b, n, g)

proc foldModU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  if b != 0'i64:
    result = newIntNodeT(a %% b, n, g)

proc foldDiv*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  if b != 0'i64 and (a != firstOrd(g.config, n.typ) or b != -1'i64):
    result = newIntNodeT(a div b, n, g)

proc foldDivU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  if b != 0'i64:
    result = newIntNodeT(a /% b, n, g)

proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
  let res = a *% b
  let floatProd = toBiggestFloat(a) * toBiggestFloat(b)
  let resAsFloat = toBiggestFloat(res)

  # Fast path for normal case: small multiplicands, and no info
  # is lost in either method.
  if resAsFloat == floatProd and checkInRange(g.config, n, res):
    return newIntNodeT(res, n, g)

  # Somebody somewhere lost info. Close enough, or way off? Note
  # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
  # The difference either is or isn't significant compared to the
  # true value (of which floatProd is a good approximation).

  # abs(diff)/abs(prod) <= 1/32 iff
  #   32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
  if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and
      checkInRange(g.config, n, res):
    return newIntNodeT(res, n, g)

proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
  # because $ has the param ordinal[T], `a` is not necessarily an enum, but an
  # ordinal
  var x = getInt(a)

  var t = skipTypes(a.typ, abstractRange)
  case t.kind
  of tyChar:
    result = $chr(int(x) and 0xff)
  of tyEnum:
    var n = t.n
    for i in countup(0, sonsLen(n) - 1):
      if n.sons[i].kind != nkSym: internalError(g.config, a.info, "ordinalValToString")
      var field = n.sons[i].sym
      if field.position == x:
        if field.ast == nil:
          return field.name.s
        else:
          return field.ast.strVal
    localError(g.config, a.info,
      "Cannot convert int literal to $1. The value is invalid." %
        [typeToString(t)])
  else:
    result = $x

proc isFloatRange(t: PType): bool {.inline.} =
  result = t.kind == tyRange and t.sons[0].kind in {tyFloat..tyFloat128}

proc isIntRange(t: PType): bool {.inline.} =
  result = t.kind == tyRange and t.sons[0].kind in {
      tyInt..tyInt64, tyUInt8..tyUInt32}

proc pickIntRange(a, b: PType): PType =
  if isIntRange(a): result = a
  elif isIntRange(b): result = b
  else: result = a

proc isIntRangeOrLit(t: PType): bool =
  result = isIntRange(t) or isIntLit(t)

proc makeRange(typ: PType, first, last: BiggestInt; g: ModuleGraph): PType =
  let minA = min(first, last)
  let maxA = max(first, last)
{'fraction_non_alphanumeric': 0.13139297005765296, 'fraction_numerical': 0.011995536544541566, 'mean_word_length': 3.3772893772893773, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2930055', 'n_tokens_mistral': 4102, 'n_tokens_neox': 3957, 'n_words': 1281}