summary refs log tree commit diff stats
path: root/compiler/seminst.nim
blob: 42a39d0df4e8020183d3838b9add7833c8356db6 (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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#
#
#           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 instantiation of generic procs.
# included from sem.nim

iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
  internalAssert n.kind == nkGenericParams
  for i, a in n.pairs:
    internalAssert a.kind == nkSym
    var q = a.sym
    if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
      continue
    let symKind = if q.typ.kind == tyStatic: skConst else: skType
    var s = newSym(symKind, q.name, getCurrOwner(), q.info)
    s.flags = s.flags + {sfUsed, sfFromGeneric}
    var t = PType(idTableGet(pt, q.typ))
    if t == nil:
      if tfRetType in q.typ.flags:
        # keep the generic type and allow the return type to be bound
        # later by semAsgn in return type inference scenario
        t = q.typ
      else:
        localError(a.info, errCannotInstantiateX, s.name.s)
        t = errorType(c)
    elif t.kind == tyGenericParam:
      localError(a.info, errCannotInstantiateX, q.name.s)
      t = errorType(c)
    elif t.kind == tyGenericInvocation:
      #t = instGenericContainer(c, a, t)
      t = generateTypeInstance(c, pt, a, t)
      #t = ReplaceTypeVarsT(cl, t)
    s.typ = t
    if t.kind == tyStatic: s.ast = t.n
    yield s

proc sameInstantiation(a, b: TInstantiation): bool =
  if a.concreteTypes.len == b.concreteTypes.len:
    for i in 0..a.concreteTypes.high:
      if not compareTypes(a.concreteTypes[i], b.concreteTypes[i],
                          flags = {ExactTypeDescValues}): return
    result = true

proc genericCacheGet(genericSym: PSym, entry: TInstantiation): PSym =
  if genericSym.procInstCache != nil:
    for inst in genericSym.procInstCache:
      if sameInstantiation(entry, inst[]):
        return inst.sym

proc removeDefaultParamValues(n: PNode) =
  # we remove default params, because they cannot be instantiated properly
  # and they are not needed anyway for instantiation (each param is already
  # provided).
  when false:
    for i in countup(1, sonsLen(n)-1):
      var a = n.sons[i]
      if a.kind != nkIdentDefs: IllFormedAst(a)
      var L = a.len
      if a.sons[L-1].kind != nkEmpty and a.sons[L-2].kind != nkEmpty:
        # ``param: typ = defaultVal``.
        # We don't need defaultVal for semantic checking and it's wrong for
        # ``cmp: proc (a, b: T): int = cmp``. Hm, for ``cmp = cmp`` that is
        # not possible... XXX We don't solve this issue here.
        a.sons[L-1] = ast.emptyNode

proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
  # we need to create a fresh set of gensym'ed symbols:
  if n.kind == nkSym and sfGenSym in n.sym.flags:
    let s = n.sym
    var x = PSym(idTableGet(symMap, s))
    if x == nil:
      x = copySym(s, false)
      x.owner = owner
      idTablePut(symMap, s, x)
    n.sym = x
  else:
    for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap)

proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)

proc addProcDecls(c: PContext, fn: PSym) =
  # get the proc itself in scope (e.g. for recursion)
  addDecl(c, fn)

  for i in 1 .. <fn.typ.n.len:
    var param = fn.typ.n.sons[i].sym
    param.owner = fn
    addParamOrResult(c, param, fn.kind)

  maybeAddResult(c, fn, fn.ast)

proc instantiateBody(c: PContext, n, params: PNode, result: PSym) =
  if n.sons[bodyPos].kind != nkEmpty:
    inc c.inGenericInst
    # add it here, so that recursive generic procs are possible:
    var b = n.sons[bodyPos]
    var symMap: TIdTable
    initIdTable symMap
    if params != nil:
      for i in 1 .. <params.len:
        let param = params[i].sym
        if sfGenSym in param.flags:
          idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym)
    freshGenSyms(b, result, symMap)
    b = semProcBody(c, b)
    b = hloBody(c, b)
    n.sons[bodyPos] = transformBody(c.module, b, result)
    #echo "code instantiated ", result.name.s
    excl(result.flags, sfForward)
    dec c.inGenericInst

proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
  for i in countup(0, c.generics.len - 1):
    if c.generics[i].genericSym.id == s.id:
      var oldPrc = c.generics[i].inst.sym
      pushInfoContext(oldPrc.info)
      openScope(c)
      var n = oldPrc.ast
      n.sons[bodyPos] = copyTree(s.getBody)
      instantiateBody(c, n, nil, oldPrc)
      closeScope(c)
      popInfoContext()

proc sideEffectsCheck(c: PContext, s: PSym) =
  if {sfNoSideEffect, sfSideEffect} * s.flags ==
      {sfNoSideEffect, sfSideEffect}:
    localError(s.info, errXhasSideEffects, s.name.s)

proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
                          allowMetaTypes = false): PType =
  var cl: TReplTypeVars
  initIdTable(cl.symMap)
  initIdTable(cl.typeMap)
  initIdTable(cl.localCache)
  cl.info = info
  cl.c = c
  cl.allowMetaTypes = allowMetaTypes
  result = replaceTypeVarsT(cl, header)

proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
  result = instGenericContainer(c, n.info, header)

proc instantiateProcType(c: PContext, pt: TIdTable,
                          prc: PSym, info: TLineInfo) =
  # XXX: Instantiates a generic proc signature, while at the same
  # time adding the instantiated proc params into the current scope.
  # This is necessary, because the instantiation process may refer to
  # these params in situations like this:
  # proc foo[Container](a: Container, b: a.type.Item): type(b.x)
  #
  # Alas, doing this here is probably not enough, because another
  # proc signature could appear in the params:
  # proc foo[T](a: proc (x: T, b: type(x.y))
  #
  # The solution would be to move this logic into semtypinst, but
  # at this point semtypinst have to become part of sem, because it
  # will need to use openScope, addDecl, etc.
  addDecl(c, prc)

  pushInfoContext(info)
  var cl = initTypeVars(c, pt, info)
  var result = instCopyType(cl, prc.typ)
  let originalParams = result.n
  result.n = originalParams.shallowCopy

  for i in 1 .. <result.len:
    # twrong_field_caching requires these 'resetIdTable' calls:
    if i > 1:
      resetIdTable(cl.symMap)
      resetIdTable(cl.localCache)
    result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
    propagateToOwner(result, result.sons[i])
    internalAssert originalParams[i].kind == nkSym
    when true:
      let oldParam = originalParams[i].sym
      let param = copySym(oldParam)
      param.owner = prc
      param.typ = result.sons[i]
      if oldParam.ast != nil:
        param.ast = fitNode(c, param.typ, oldParam.ast)

      # don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
      result.n.sons[i] = newSymNode(param)
      addDecl(c, param)
    else:
      let param = replaceTypeVarsN(cl, originalParams[i])
      result.n.sons[i] = param
      param.sym.owner = prc
      addDecl(c, result.n.sons[i].sym)

  resetIdTable(cl.symMap)
  resetIdTable(cl.localCache)
  result.sons[0] = replaceTypeVarsT(cl, result.sons[0])
  result.n.sons[0] = originalParams[0].copyTree

  eraseVoidParams(result)
  skipIntLiteralParams(result)

  prc.typ = result
  maybeAddResult(c, prc, prc.ast)
  popInfoContext()

proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
                      info: TLineInfo): PSym =
  ## Generates a new instance of a generic procedure.
  ## The `pt` parameter is a type-unsafe mapping table used to link generic
  ## parameters to their concrete types within the generic instance.
  # no need to instantiate generic templates/macros:
  internalAssert fn.kind notin {skMacro, skTemplate}
  # generates an instantiated proc
  if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
  inc(c.instCounter)
  # careful! we copy the whole AST including the possibly nil body!
  var n = copyTree(fn.ast)
  # NOTE: for access of private fields within generics from a different module
  # we set the friend module:
  c.friendModules.add(getModule(fn))
  let oldInTypeClass = c.inTypeClass
  c.inTypeClass = 0
  let oldScope = c.currentScope
  while not isTopLevel(c): c.currentScope = c.currentScope.parent
  result = copySym(fn, false)
  incl(result.flags, sfFromGeneric)
  result.owner = fn
  result.ast = n
  pushOwner(result)

  openScope(c)
  let gp = n.sons[genericParamsPos]
  internalAssert gp.kind != nkEmpty
  n.sons[namePos] = newSymNode(result)
  pushInfoContext(info)
  var entry = TInstantiation.new
  entry.sym = result
  newSeq(entry.concreteTypes, gp.len)
  var i = 0
  for s in instantiateGenericParamList(c, gp, pt):
    addDecl(c, s)
    entry.concreteTypes[i] = s.typ
    inc i
  pushProcCon(c, result)
  instantiateProcType(c, pt, result, info)
  n.sons[genericParamsPos] = ast.emptyNode
  var oldPrc = genericCacheGet(fn, entry[])
  if oldPrc == nil:
    # we MUST not add potentially wrong instantiations to the caching mechanism.
    # This means recursive instantiations behave differently when in
    # a ``compiles`` context but this is the lesser evil. See
    # bug #1055 (tevilcompiles).
    if c.inCompilesContext == 0: fn.procInstCache.safeAdd(entry)
    c.generics.add(makeInstPair(fn, entry))
    if n.sons[pragmasPos].kind != nkEmpty:
      pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
    if isNil(n.sons[bodyPos]):
      n.sons[bodyPos] = copyTree(fn.getBody)
    instantiateBody(c, n, fn.typ.n, result)
    sideEffectsCheck(c, result)
    paramsTypeCheck(c, result.typ)
  else:
    result = oldPrc
  popProcCon(c)
  popInfoContext()
  closeScope(c)           # close scope for parameters
  popOwner()
  c.currentScope = oldScope
  discard c.friendModules.pop()
  dec(c.instCounter)
  c.inTypeClass = oldInTypeClass
  if result.kind == skMethod: finishMethod(c, result)
lass="o">->calls.size() == 1) return false; const call& caller = *++Current_routine->calls.begin(); const instruction& caller_inst = to_instruction(caller); if (product_index >= SIZE(caller_inst.products)) return false; return !is_dummy(caller_inst.products.at(product_index)); } void rewrite_default_space_instruction(instruction& curr) { if (!curr.ingredients.empty()) raise << to_original_string(curr) << " can't take any ingredients\n" << end(); curr.name = "new"; curr.ingredients.push_back(reagent("location:type")); curr.ingredients.push_back(reagent("number-of-locals:literal")); if (!curr.products.empty()) raise << "new-default-space can't take any results\n" << end(); curr.products.push_back(reagent("default-space:address:array:location")); } :(scenario local_scope_frees_up_addresses_inside_containers) container foo [ x:number y:address:number ] def main [ local-scope x:address:number <- new number:type y:foo <- merge 34, x:address:number # x and y are both cleared when main returns ] +mem: clearing x:address:number +mem: decrementing refcount of 1006: 2 -> 1 +mem: clearing y:foo +mem: decrementing refcount of 1006: 1 -> 0 +mem: automatically abandoning 1006 :(scenario local_scope_returns_addresses_inside_containers) container foo [ x:number y:address:number ] def f [ local-scope x:address:number <- new number:type *x:address:number <- copy 12 y:foo <- merge 34, x:address:number # since y is 'escaping' f, it should not be cleared return y:foo ] def main [ 1:foo <- f 3:number <- get 1:foo, x:offset 4:address:number <- get 1:foo, y:offset 5:number <- copy *4:address:number 1:foo <- put 1:foo, y:offset, 0 4:address:number <- copy 0 ] +mem: storing 34 in location 1 +mem: storing 1006 in location 2 +mem: storing 34 in location 3 # refcount of 1:foo shouldn't include any stray ones from f +run: {4: ("address" "number")} <- get {1: "foo"}, {y: "offset"} +mem: incrementing refcount of 1006: 1 -> 2 # 1:foo wasn't abandoned/cleared +run: {5: "number"} <- copy {4: ("address" "number"), "lookup": ()} +mem: storing 12 in location 5 +run: {1: "foo"} <- put {1: "foo"}, {y: "offset"}, {0: "literal"} +mem: decrementing refcount of 1006: 2 -> 1 +run: {4: ("address" "number")} <- copy {0: "literal"} +mem: decrementing refcount of 1006: 1 -> 0 +mem: automatically abandoning 1006 :(scenario local_scope_claims_return_values_when_not_saved) def f [ local-scope x:address:number <- new number:type reply x:address:number ] def main [ f # doesn't save result ] # x reclaimed +mem: automatically abandoning 1004 # f's local scope reclaimed +mem: automatically abandoning 1000 //:: all recipes must set default-space one way or another :(before "End Globals") bool Hide_missing_default_space_errors = true; :(before "End Checks") Transform.push_back(check_default_space); // idempotent :(code) void check_default_space(const recipe_ordinal r) { if (Hide_missing_default_space_errors) return; // skip previous core tests; this is only for mu code const recipe& caller = get(Recipe, r); // skip scenarios (later layer) // user code should never create recipes with underscores in their names if (caller.name.find("scenario_") == 0) return; // skip mu scenarios which will use raw memory locations if (caller.name.find("run_") == 0) return; // skip calls to 'run', which should be in scenarios and will also use raw memory locations // assume recipes with only numeric addresses know what they're doing (usually tests) if (!contains_non_special_name(r)) return; trace(9991, "transform") << "--- check that recipe " << caller.name << " sets default-space" << end(); if (caller.steps.empty()) return; if (caller.steps.at(0).products.empty() || caller.steps.at(0).products.at(0).name != "default-space") { raise << caller.name << " does not seem to start with default-space or local-scope\n" << end(); } } :(after "Load .mu Core") Hide_missing_default_space_errors = false; :(after "Test Runs") Hide_missing_default_space_errors = true; :(after "Running Main") Hide_missing_default_space_errors = false; :(code) bool contains_non_special_name(const recipe_ordinal r) { for (map<string, int>::iterator p = Name[r].begin(); p != Name[r].end(); ++p) { if (p->first.empty()) continue; if (p->first.find("stash_") == 0) continue; // generated by rewrite_stashes_to_text (cross-layer) if (!is_special_name(p->first)) return true; } return false; } // reagent comparison -- only between reagents in a single recipe bool operator==(const reagent& a, const reagent& b) { if (a.name != b.name) return false; if (property(a, "space") != property(b, "space")) return false; return true; } bool operator<(const reagent& a, const reagent& b) { int aspace = 0, bspace = 0; if (has_property(a, "space")) aspace = to_integer(property(a, "space")->value); if (has_property(b, "space")) bspace = to_integer(property(b, "space")->value); if (aspace != bspace) return aspace < bspace; return a.name < b.name; }