Skip to content

Destructors + concepts semcheck issue #12620

Open
@mratsim

Description

@mratsim

I get a cannot instantiate =destroy in the following complex case. The workaround is to have a dummy global variable of the type that has the destructor or that contains it.

While it seems like a similar cross-module visibility issue that also involves mixin like the generic sandwich #11225, just exporting the =destroy that had the mixin doesn't solve the issue so it's probably something else.

Tagging low priority as there are 4 workarounds:

  • not using destructors
  • not using concepts
  • instantiating a dummy variable in the inner exports
  • exporting the inner type that implements the concept (ensuring visibility of the container with destructors is not enough)

File 1: runtime.nim

Compile this to reproduce the problem

# runtime.nim
import ./context_thread_local

var localCtx* : TLContext

File 2: context_thread_local.nim

2 fixes are possible in this file, exporting "tasks" or instantiating a magic variable

# context_thread_local
import ./tasks, ./listdeques

export listdeques            # Exporting the type with destructor doesn't help
# export tasks               # solution 1. Exporting the inner type

type MagicCompile = object
  dq: ListDeque[Task]

# var x: MagicCompile        # solution 2. Instantiating the type with destructors
echo "Success"

type
  TLContext* = object
    deque*: ListDeque[Task]

File 3: listdeques.nim

Remove the concept and everything works as well

# listdeques

type
  StealableTask* = concept task, var mutTask, type T
    task is ptr
    task.prev is T
    task.next is T
    task.parent is T
    task.fn is proc (param: pointer) {.nimcall.}
    allocate(mutTask)
    delete(task)

  ListDeque*[T: StealableTask] = object
    head, tail: T

func isEmpty*(dq: ListDeque): bool {.inline.} =
  discard

func popFirst*[T](dq: var ListDeque[T]): T =
  discard

proc `=destroy`*[T: StealableTask](dq: var ListDeque[T]) =
  mixin delete
  if dq.isEmpty():
    return

  while (let task = dq.popFirst(); not task.isNil):
    delete(task)

  delete(dq.head)

File 4: tasks.nim

The base type, it just needs to implement the concept

# tasks.nim
type
  Task* = ptr object
    parent*: Task
    prev*: Task
    next*: Task
    fn*: proc (param: pointer) {.nimcall.}

# StealableTask API
proc allocate*(task: var Task) =
  discard

proc delete*(task: Task) =
  discard

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions