summary refs log tree commit diff stats
path: root/compiler/ccgmerge.nim
blob: 2d68a198e3281f903b543c437fc8c3846816766a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
discard """
  errormsg: "type mismatch"
  line: 18
"""

# bug #3998

type Vec3[T] = array[3, T]

var vg: Vec3[float32] = Vec3([1.0f, 2.0f, 3.0f])

echo "vg[0]: " & $vg[0]  # prints 1.0    OK
echo "vg[1]: " & $vg[1]  # prints 2.0    OK
echo "vg[2]: " & $vg[2]  # prints 3.0    OK
echo ""

var ve: Vec3[float64]
ve = vg     # compiles, this MUST NOT be allowed!
id='n286' href='#n286'>286 287 288 289 290 291 292 293 294 295 296 297
#
#
#           The Nim Compiler
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module implements the merge operation of 2 different C files. This
## is needed for incremental compilation.

import
  ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils,
  intsets, platform, llstream, tables, sighashes, pathutils

# Careful! Section marks need to contain a tabulator so that they cannot
# be part of C string literals.

const
  CFileSectionNames: array[TCFileSection, string] = [
    cfsMergeInfo: "",
    cfsHeaders: "NIM_merge_HEADERS",
    cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
    cfsTypes: "NIM_merge_TYPES",
    cfsSeqTypes: "NIM_merge_SEQ_TYPES",
    cfsFieldInfo: "NIM_merge_FIELD_INFO",
    cfsTypeInfo: "NIM_merge_TYPE_INFO",
    cfsProcHeaders: "NIM_merge_PROC_HEADERS",
    cfsVars: "NIM_merge_VARS",
    cfsData: "NIM_merge_DATA",
    cfsProcs: "NIM_merge_PROCS",
    cfsInitProc: "NIM_merge_INIT_PROC",
    cfsDatInitProc: "NIM_merge_DATINIT_PROC",    
    cfsTypeInit1: "NIM_merge_TYPE_INIT1",
    cfsTypeInit2: "NIM_merge_TYPE_INIT2",
    cfsTypeInit3: "NIM_merge_TYPE_INIT3",
    cfsDebugInit: "NIM_merge_DEBUG_INIT",
    cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
    cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
  ]
  CProcSectionNames: array[TCProcSection, string] = [
    cpsLocals: "NIM_merge_PROC_LOCALS",
    cpsInit: "NIM_merge_PROC_INIT",
    cpsStmts: "NIM_merge_PROC_BODY"
  ]
  NimMergeEndMark = "/*\tNIM_merge_END:*/"

proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
  if compilationCachePresent(conf):
    result = nil
    add(result, "\n/*\t")
    add(result, CFileSectionNames[fs])
    add(result, ":*/\n")

proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
  if compilationCachePresent(conf):
    result = rope(NimMergeEndMark & "\n")

proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
  if compilationCachePresent(conf):
    result = rope("")
    add(result, "\n/*\t")
    add(result, CProcSectionNames[ps])
    add(result, ":*/\n")

proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
  if compilationCachePresent(conf):
    result = rope(NimMergeEndMark & "\n")

proc writeTypeCache(a: TypeCache, s: var string) =
  var i = 0
  for id, value in pairs(a):
    if i == 10:
      i = 0
      s.add('\L')
    else:
      s.add(' ')
    encodeStr($id, s)
    s.add(':')
    encodeStr($value, s)
    inc i
  s.add('}')

proc writeIntSet(a: IntSet, s: var string) =
  var i = 0
  for x in items(a):
    if i == 10:
      i = 0
      s.add('\L')
    else:
      s.add(' ')
    encodeVInt(x, s)
    inc i
  s.add('}')

proc genMergeInfo*(m: BModule): Rope =
  if not compilationCachePresent(m.config): return nil
  var s = "/*\tNIM_merge_INFO:\n"
  s.add("typeCache:{")
  writeTypeCache(m.typeCache, s)
  s.add("declared:{")
  writeIntSet(m.declaredThings, s)
  when false:
    s.add("typeInfo:{")
    writeIntSet(m.typeInfoMarker, s)
  s.add("labels:")
  encodeVInt(m.labels, s)
  s.add(" flags:")
  encodeVInt(cast[int](m.flags), s)
  s.add("\n*/")
  result = s.rope

template `^`(pos: int): untyped = L.buf[pos]

proc skipWhite(L: var TBaseLexer) =
  var pos = L.bufpos
  while true:
    case ^pos
    of CR: pos = nimlexbase.handleCR(L, pos)
    of LF: pos = nimlexbase.handleLF(L, pos)
    of ' ': inc pos
    else: break
  L.bufpos = pos

proc skipUntilCmd(L: var TBaseLexer) =
  var pos = L.bufpos
  while true:
    case ^pos
    of CR: pos = nimlexbase.handleCR(L, pos)
    of LF: pos = nimlexbase.handleLF(L, pos)
    of '\0': break
    of '/':
      if ^(pos+1) == '*' and ^(pos+2) == '\t':
        inc pos, 3
        break
      inc pos
    else: inc pos
  L.bufpos = pos

proc atEndMark(buf: cstring, pos: int): bool =
  var s = 0
  while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
  result = s == NimMergeEndMark.len

proc readVerbatimSection(L: var TBaseLexer): Rope =
  var pos = L.bufpos
  var buf = L.buf
  var r = newStringOfCap(30_000)
  while true:
    case buf[pos]
    of CR:
      pos = nimlexbase.handleCR(L, pos)
      buf = L.buf
      r.add('\L')
    of LF:
      pos = nimlexbase.handleLF(L, pos)
      buf = L.buf
      r.add('\L')
    of '\0':
      doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
      break
    else:
      if atEndMark(buf, pos):
        inc pos, NimMergeEndMark.len
        break
      r.add(buf[pos])
      inc pos
  L.bufpos = pos
  result = r.rope

proc readKey(L: var TBaseLexer, result: var string) =
  var pos = L.bufpos
  var buf = L.buf
  setLen(result, 0)
  while buf[pos] in IdentChars:
    result.add(buf[pos])
    inc pos
  if buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected")
  L.bufpos = pos + 1 # skip ':'

proc newFakeType(id: int): PType =
  new(result)
  result.id = id

proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
  if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
  inc L.bufpos
  while ^L.bufpos != '}':
    skipWhite(L)
    var key = decodeStr(L.buf, L.bufpos)
    if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
    inc L.bufpos
    var value = decodeStr(L.buf, L.bufpos)
    # XXX implement me
    when false:
      idTablePut(result, newFakeType(key), value.rope)
  inc L.bufpos

proc readIntSet(L: var TBaseLexer, result: var IntSet) =
  if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
  inc L.bufpos
  while ^L.bufpos != '}':
    skipWhite(L)
    var key = decodeVInt(L.buf, L.bufpos)
    result.incl(key)
  inc L.bufpos

proc processMergeInfo(L: var TBaseLexer, m: BModule) =
  var k = newStringOfCap("typeCache".len)
  while true:
    skipWhite(L)
    if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
      inc(L.bufpos, 2)
      break
    readKey(L, k)
    case k
    of "typeCache": readTypeCache(L, m.typeCache)
    of "declared":  readIntSet(L, m.declaredThings)
    of "typeInfo":
      when false: readIntSet(L, m.typeInfoMarker)
    of "labels":    m.labels = decodeVInt(L.buf, L.bufpos)
    of "flags":
      m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
    else: doAssert(false, "ccgmerge: unknown key: " & k)

when not defined(nimhygiene):
  {.pragma: inject.}

template withCFile(cfilename: AbsoluteFile, body: untyped) =
  var s = llStreamOpen(cfilename, fmRead)
  if s == nil: return
  var L {.inject.}: TBaseLexer
  openBaseLexer(L, s)
  var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
  while true:
    skipUntilCmd(L)
    if ^L.bufpos == '\0': break
    body
  closeBaseLexer(L)

proc readMergeInfo*(cfilename: AbsoluteFile, m: BModule) =
  ## reads the merge meta information into `m`.
  withCFile(cfilename):
    readKey(L, k)
    if k == "NIM_merge_INFO":
      processMergeInfo(L, m)
      break

type
  TMergeSections = object
    f: TCFileSections
    p: TCProcSections

proc readMergeSections(cfilename: AbsoluteFile, m: var TMergeSections) =
  ## reads the merge sections into `m`.
  withCFile(cfilename):
    readKey(L, k)
    if k == "NIM_merge_INFO":
      discard
    elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
      inc(L.bufpos, 2)
      # read back into section
      skipWhite(L)
      var verbatim = readVerbatimSection(L)
      skipWhite(L)
      var sectionA = CFileSectionNames.find(k)
      if sectionA > 0 and sectionA <= high(TCFileSection).int:
        m.f[TCFileSection(sectionA)] = verbatim
      else:
        var sectionB = CProcSectionNames.find(k)
        if sectionB >= 0 and sectionB <= high(TCProcSection).int:
          m.p[TCProcSection(sectionB)] = verbatim
        else:
          doAssert(false, "ccgmerge: unknown section: " & k)
    else:
      doAssert(false, "ccgmerge: '*/' expected")

proc mergeRequired*(m: BModule): bool =
  for i in cfsHeaders..cfsProcs:
    if m.s[i] != nil:
      #echo "not empty: ", i, " ", m.s[i]
      return true
  for i in low(TCProcSection)..high(TCProcSection):
    if m.initProc.s(i) != nil:
      #echo "not empty: ", i, " ", m.initProc.s[i]
      return true

proc mergeFiles*(cfilename: AbsoluteFile, m: BModule) =
  ## merges the C file with the old version on hard disc.
  var old: TMergeSections
  readMergeSections(cfilename, old)
  # do the merge; old section before new section:
  for i in low(TCFileSection)..high(TCFileSection):
    m.s[i] = old.f[i] & m.s[i]
  for i in low(TCProcSection)..high(TCProcSection):
    m.initProc.s(i) = old.p[i] & m.initProc.s(i)