Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++ Atomics: operator= is implicitly deleted because the default definition would be ill-formed #13093

Open
mratsim opened this issue Jan 9, 2020 · 2 comments · Fixed by #21169

Comments

@mratsim
Copy link
Collaborator

mratsim commented Jan 9, 2020

Seems like C++ atomics don't like me at all.

When trying to workaround #13062

import atomics

type
  Pledge* = object
    p: PledgePtr

  PledgeKind = enum
    Single
    Iteration

  PledgePtr = ptr object
    refCount: Atomic[int32]
    impl: PledgeImpl
    # The case object is not important for the bug
    # but the non-binary 0 case requires the object constructor-based
    # initialization as we can't change the case object once constructed
    case kind: PledgeKind
    of Single:
      discard
    of Iteration:
      discard

  PledgeImpl = object
    fulfilled: Atomic[bool]

template deref*(T: typedesc): typedesc =
  ## Return the base object type behind a ptr type
  typeof(default(T)[])

proc main() =
  var pledge: Pledge
  pledge.p = createShared(deref(PledgePtr))
  zeroMem(pledge.p, sizeof(deref(PledgePtr)))

  # Bad codegen
  pledge.p[] = deref(PledgePtr)(kind: Iteration)

main()

Error


..../.cache/nim/atombug_d/@matombug.nim.cpp: In function ‘void main__b3r7EXSeAzsO9aIDCMEJ8CA()’:
..../.cache/nim/atombug_d/@matombug.nim.cpp:157:16: error: use of deleted function ‘tyObject_PledgePtrcolonObjectType___FkbYobdvoyzypSol0uBgbw& tyObject_PledgePtrcolonObjectType___FkbYobdvoyzypSol0uBgbw::operator=(const tyObject_PledgePtrcolonObjectType___FkbYobdvoyzypSol0uBgbw&)’
  157 |  (*pledge.p) = T1_;
      |                ^~~
..../.cache/nim/atombug_d/@matombug.nim.cpp:47:8: note: ‘tyObject_PledgePtrcolonObjectType___FkbYobdvoyzypSol0uBgbw& tyObject_PledgePtrcolonObjectType___FkbYobdvoyzypSol0uBgbw::operator=(const tyObject_PledgePtrcolonObjectType___FkbYobdvoyzypSol0uBgbw&)’ is implicitly deleted because the default definition would be ill-formed:
   47 | struct tyObject_PledgePtrcolonObjectType___FkbYobdvoyzypSol0uBgbw {
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
..../.cache/nim/atombug_d/@matombug.nim.cpp:47:8: error: use of deleted function ‘std::atomic<int>& std::atomic<int>::operator=(const std::atomic<int>&)’
In file included from ..../.cache/nim/atombug_d/@matombug.nim.cpp:11:
/usr/include/c++/9.2.0/atomic:756:15: note: declared here
  756 |       atomic& operator=(const atomic&) = delete;
      |               ^~~~~~~~
..../.cache/nim/atombug_d/@matombug.nim.cpp:47:8: error: use of deleted function ‘tyObject_PledgeImpl__PC08ohoFN4aei4c6kCPnAw& tyObject_PledgeImpl__PC08ohoFN4aei4c6kCPnAw::operator=(const tyObject_PledgeImpl__PC08ohoFN4aei4c6kCPnAw&)’
   47 | struct tyObject_PledgePtrcolonObjectType___FkbYobdvoyzypSol0uBgbw {
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
..../.cache/nim/atombug_d/@matombug.nim.cpp:43:8: note: ‘tyObject_PledgeImpl__PC08ohoFN4aei4c6kCPnAw& tyObject_PledgeImpl__PC08ohoFN4aei4c6kCPnAw::operator=(const tyObject_PledgeImpl__PC08ohoFN4aei4c6kCPnAw&)’ is implicitly deleted because the default definition would be ill-formed:
   43 | struct tyObject_PledgeImpl__PC08ohoFN4aei4c6kCPnAw {
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
..../.cache/nim/atombug_d/@matombug.nim.cpp:43:8: error: use of deleted function ‘std::atomic<bool>& std::atomic<bool>::operator=(const std::atomic<bool>&)’
In file included from ..../.cache/nim/atombug_d/@matombug.nim.cpp:11:
/usr/include/c++/9.2.0/atomic:74:13: note: declared here
   74 |     atomic& operator=(const atomic&) = delete;
      |             ^~~~~~~~
@mratsim mratsim changed the title C++ Atomics: is implicitly deleted because the default definition would be ill-formed C++ Atomics: operator= is implicitly deleted because the default definition would be ill-formed Jan 9, 2020
@mratsim
Copy link
Collaborator Author

mratsim commented Jan 9, 2020

Even this very minimal example doesn't compile for some reason

import atomics

type
  Pledge* = object
    p: PledgePtr

  PledgePtr = ptr PledgeObj
  PledgeObj = object
    refCount: Atomic[int32]

proc main() =
  var pledge: Pledge
  pledge.p = createShared(PledgeObj)
  let tmp = PledgeObj()

  pledge.p[] = tmp

main()

Note that I have plenty of code with atomics that does compile in C++ mode

@mratsim
Copy link
Collaborator Author

mratsim commented Apr 25, 2020

To answer the small example, this is actually not allowed

import atomics

type
  Pledge* = object
    p: PledgePtr

  PledgePtr = ptr PledgeObj
  PledgeObj = object
    refCount: Atomic[int32]

proc main() =
  var pledge: Pledge
  pledge.p = createShared(PledgeObj)
  let tmp = PledgeObj() # <---- not allowed: atomics are not copyable

  pledge.p[] = tmp

main()

The code generated is

N_LIB_PRIVATE N_NIMCALL(void, main__BTA1O9bZ9cw3phtcEzYReOQw)(void) {
	tyObject_Pledge__liVKkHLjQBejXUzfhcSWAw pledge;
	tyObject_PledgeObj__JS8Q9a9bu0tSuejYr9a1kzS1A T1_;
	nimZeroMem((void*)(&pledge), sizeof(tyObject_Pledge__liVKkHLjQBejXUzfhcSWAw));
	pledge.p = createShared__w7DYWNtNZpqDzb9bgOkT5tAsystem(((NI) 1));
	nimZeroMem((void*)(&T1_), sizeof(tyObject_PledgeObj__JS8Q9a9bu0tSuejYr9a1kzS1A));
	tyObject_PledgeObj__JS8Q9a9bu0tSuejYr9a1kzS1A tmp = T1_;
	(*pledge.p) = tmp;
}

and tyObject_PledgeObj__JS8Q9a9bu0tSuejYr9a1kzS1A tmp = T1_; is not allowed due to the presence of atomics.

In the past, due to the use of case objects and the fact that we can't change case this is required for
pledge.p[] = deref(PledgePtr)(kind: Iteration)

but the case-object transition rules seem to have been relaxed so now this seems to be a valid workaround?

import atomics

type
  Pledge* = object
    p: PledgePtr

  PledgeKind = enum
    Single
    Iteration

  PledgePtr = ptr object
    refCount: Atomic[int32]
    impl: PledgeImpl
    # The case object is not important for the bug
    # but the non-binary 0 case requires the object constructor-based
    # initialization as we can't change the case object once constructed
    case kind: PledgeKind
    of Single:
      discard
    of Iteration:
      discard

  PledgeImpl = object
    fulfilled: Atomic[bool]

template deref*(T: typedesc): typedesc =
  ## Return the base object type behind a ptr type
  typeof(default(T)[])

proc main() =
  var pledge: Pledge
  pledge.p = createShared(deref(PledgePtr))
  zeroMem(pledge.p, sizeof(deref(PledgePtr)))

  # Case object transition avoids invalid copy/sink
  pledge.p.kind = Iteration

main()

bung87 added a commit to bung87/Nim that referenced this issue Dec 24, 2022
bung87 added a commit to bung87/Nim that referenced this issue Dec 28, 2022
Araq pushed a commit that referenced this issue Jan 27, 2023
…efault definition would be ill-formed (#21169)

* add test

* fix #17982 Invalid C++ code generation when returning discardable var T

* fix #13093

* cpp atomic good example

* clearify the condition
ringabout added a commit that referenced this issue Jan 27, 2023
…se the default definition would be ill-formed (#21169)"

This reverts commit a7bae91.
Araq pushed a commit that referenced this issue Jan 27, 2023
…se the default definition would be ill-formed " (#21307)

Revert "Fix #13093 C++ Atomics: operator= is implicitly deleted because the default definition would be ill-formed  (#21169)"

This reverts commit a7bae91.
@ringabout ringabout reopened this Jan 31, 2023
@nim-lang nim-lang deleted a comment from hkl615 Feb 1, 2023
@nim-lang nim-lang deleted a comment from hkl615 Feb 1, 2023
survivorm pushed a commit to survivorm/Nim that referenced this issue Feb 28, 2023
…se the default definition would be ill-formed (nim-lang#21169)

* add test

* fix nim-lang#17982 Invalid C++ code generation when returning discardable var T

* fix nim-lang#13093

* cpp atomic good example

* clearify the condition
survivorm pushed a commit to survivorm/Nim that referenced this issue Feb 28, 2023
…ed because the default definition would be ill-formed " (nim-lang#21307)

Revert "Fix nim-lang#13093 C++ Atomics: operator= is implicitly deleted because the default definition would be ill-formed  (nim-lang#21169)"

This reverts commit a7bae91.
capocasa pushed a commit to capocasa/Nim that referenced this issue Mar 31, 2023
…se the default definition would be ill-formed (nim-lang#21169)

* add test

* fix nim-lang#17982 Invalid C++ code generation when returning discardable var T

* fix nim-lang#13093

* cpp atomic good example

* clearify the condition
capocasa pushed a commit to capocasa/Nim that referenced this issue Mar 31, 2023
…ed because the default definition would be ill-formed " (nim-lang#21307)

Revert "Fix nim-lang#13093 C++ Atomics: operator= is implicitly deleted because the default definition would be ill-formed  (nim-lang#21169)"

This reverts commit a7bae91.
bung87 added a commit to bung87/Nim that referenced this issue Apr 23, 2023
bung87 added a commit to bung87/Nim that referenced this issue Apr 23, 2023
…se the default definition would be ill-formed
bung87 added a commit to bung87/Nim that referenced this issue Jul 29, 2023
…se the default definition would be ill-formed (nim-lang#21169)

* add test

* fix nim-lang#17982 Invalid C++ code generation when returning discardable var T

* fix nim-lang#13093

* cpp atomic good example

* clearify the condition
bung87 pushed a commit to bung87/Nim that referenced this issue Jul 29, 2023
…ed because the default definition would be ill-formed " (nim-lang#21307)

Revert "Fix nim-lang#13093 C++ Atomics: operator= is implicitly deleted because the default definition would be ill-formed  (nim-lang#21169)"

This reverts commit a7bae91.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment