#
#
#           The Nim Compiler
#        (c) Copyright 2020 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

proc checkForSink*(config: ConfigRef; idgen: IdGenerator; owner: PSym; arg: PNode) =
  #[ Patterns we seek to detect:

    someLocation = p # ---> p: sink T
    passToSink(p)    # p: sink
    ObjConstr(fieldName: p)
    [p, q] # array construction

    # Open question:
    var local = p # sink parameter?
    passToSink(local)
  ]#
  if optSinkInference notin config.options: return
  case arg.kind
  of nkSym:
    if arg.sym.kind == skParam and
        arg.sym.owner == owner and
        owner.typ != nil and owner.typ.kind == tyProc and
        arg.sym.typ.hasDestructor and
        arg.sym.typ.kind notin {tyVar, tySink, tyOwned}:
      # Watch out: cannot do this inference for procs with forward
      # declarations.
      if sfWasForwarded notin owner.flags:
        let argType = arg.sym.typ

        let sinkType = newType(tySink, nextTypeId(idgen), owner)
        sinkType.size = argType.size
        sinkType.align = argType.align
        sinkType.paddingAtEnd = argType.paddingAtEnd
        sinkType.add argType

        arg.sym.typ = sinkType
        owner.typ[arg.sym.position+1] = sinkType

        #message(config, arg.info, warnUser,
        #  ("turned '$1' to a sink parameter") % [$arg])
        #echo config $ arg.info, " turned into a sink parameter ", arg.sym.name.s
      elif sfWasForwarded notin arg.sym.flags:
        # we only report every potential 'sink' parameter only once:
        incl arg.sym.flags, sfWasForwarded
        message(config, arg.info, hintPerformance,
          "could not turn '$1' to a sink parameter" % [arg.sym.name.s])
      #echo config $ arg.info, " candidate for a sink parameter here"
  of nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr:
    if not isEmptyType(arg.typ):
      checkForSink(config, idgen, owner, arg.lastSon)
  of nkIfStmt, nkIfExpr, nkWhen:
    for branch in arg:
      let value = branch.lastSon
      if not isEmptyType(value.typ):
        checkForSink(config, idgen, owner, value)
  of nkCaseStmt:
    for i in 1..<arg.len:
      let value = arg[i].lastSon
      if not isEmptyType(value.typ):
        checkForSink(config, idgen, owner, value)
  of nkTryStmt:
    checkForSink(config, idgen, owner, arg[0])
  else:
    discard "nothing to do"
