# User-defined non-copy record initializer on other type breaks resolution of compiler's default copy-initializer

**URL:** <https://chapel.discourse.group/t/user-defined-non-copy-record-initializer-on-other-type-breaks-resolution-of-compilers-default-copy-initializer/19327>\
**Category:** Users\
**Created:** [February 10, 2023, 8:37pm UTC](https://chapel.discourse.group/t/user-defined-non-copy-record-initializer-on-other-type-breaks-resolution-of-compilers-default-copy-initializer/19327 "2023-02-10T20:37:16Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![psath](https://yyz2.discourse-cdn.com/free1/user_avatar/chapel.discourse.group/psath/32/389_2.png) [@psath](https://chapel.discourse.group/u/psath)\
**Post date:** [February 10, 2023, 8:37pm UTC](https://chapel.discourse.group/t/user-defined-non-copy-record-initializer-on-other-type-breaks-resolution-of-compilers-default-copy-initializer/19327/1 "2023-02-10T20:37:16Z")

</div>

Using Chapel 1.29

```Chapel
  record CSR_descriptor {
    var isWeighted : bool;
    var isVertexT64 : bool;
    var isEdgeT64 : bool;
    var isWeightT64 : bool;
    proc init=(rhs : 4*bool) {
      this.isWeighted = rhs(0);
      this.isVertexT64 = rhs(1);
      this.isEdgeT64 = rhs(2);
      this.isWeightT64 = rhs(3);
    }
  }
  //Opaque handle
  record CSR_handle { 
    var desc : CSR_descriptor; //Error points here
    var data : c_void_ptr;
  }
  //test
  proc copyDesc() {
  var desc1 : CSR_descriptor;
  var desc2 : CSR_descriptor;
  desc1.isWeighted = true;
  desc1.isVertexT64 = false;
  desc1.isEdgeT64 = false;
  desc1.isWeightT64 = true;
  writeln(desc1);
  writeln(desc2);
  desc2 = desc1;
  writeln(desc2);
  }

```

[https://chapel-lang.org/docs/technotes/initequals.html?highlight=operator#the-init-method-for-non-generic-types](https://chapel-lang.org/docs/technotes/initequals.html?highlight=operator#the-init-method-for-non-generic-types)  
Says that

> In order to override this compiler-generated implementation, the user must implement an `init=` method that can accept an argument of the same type. **Other user-defined `init=` methods will not prevent the compiler from generating a default implementation for `init=` .**

(emphasis mine)

Since my `init=` is on a `4*bool` rather than `CSR_descriptor`, it shouldn't get in the way of the default copy-initializer.

Without the `init=` method, it compiles and I get the expected output from `copyDesc()`

```Chapel
(isWeighted = true, isVertexT64 = false, isEdgeT64 = false, isWeightT64 = true)
(isWeighted = false, isVertexT64 = false, isEdgeT64 = false, isWeightT64 = false)
(isWeighted = true, isVertexT64 = false, isEdgeT64 = false, isWeightT64 = true)

```

But with the `init=` function, it suddenly can't resolve the default copy initializer

```Chapel
CSR.chpl:52: error: could not find a copy initializer ('init=') for type 'CSR_descriptor' from type 'CSR_descriptor'
CSR.chpl:43: note: this candidate did not match: CSR_descriptor.init=(rhs: 4*bool) [184935]
CSR.chpl:52: note: because actual argument #1 with type 'CSR_descriptor'
CSR.chpl:43: note: is passed to formal 'rhs: 4*bool [184945]'
CSR.chpl:52: note: unresolved call had id 901713

```

I can write the trivial copy initializer below (which in turn requires the explicit assignment operator, else I get `error: Type 'CSR_descriptor' uses compiler-generated default '=' but has a custom 'init=' method. Please add a '=' function.`) and then it works again, but given the documentation this feels like a regression that I shouldn't have to do?

```Chapel
   proc init=(rhs : CSR_descriptor) {
      this.isWeighted = rhs.isWeighted;
      this.isVertexT64 = rhs.isVertexT64;
      this.isEdgeT64 = rhs.isEdgeT64;
      this.isWeightT64 = rhs.isWeightT64;
    }
    operator =(ref lhs : CSR_descriptor, rhs : CSR_descriptor) :void {
      lhs.isWeighted = rhs.isWeighted;
      lhs.isVertexT64 = rhs.isVertexT64;
      lhs.isEdgeT64 = rhs.isEdgeT64;
      lhs.isWeightT64 = rhs.isWeightT64;
    }

```

---

<div class="post-metadata">

**Author:** ![bradcray](https://yyz2.discourse-cdn.com/free1/user_avatar/chapel.discourse.group/bradcray/32/72_2.png) [@bradcray](https://chapel.discourse.group/u/bradcray)\
**Post date:** [February 10, 2023, 8:42pm UTC](https://chapel.discourse.group/t/user-defined-non-copy-record-initializer-on-other-type-breaks-resolution-of-compilers-default-copy-initializer/19327/2 "2023-02-10T20:42:34Z")

</div>

Welcome to Chapel's Discourse, Paul! I agree that this looks like either a bug in the documentation or a regression/mistake in the implementation. Let's see whether we can get someone on the team more knowledgeable than me about `init=` to comment.

Thanks for raising this,  
-Brad

---

<div class="post-metadata">

**Author:** ![benharsh](https://avatars.discourse-cdn.com/v4/letter/b/7feea3/32.png) [@benharsh](https://chapel.discourse.group/u/benharsh)\
**Post date:** [February 10, 2023, 11:13pm UTC](https://chapel.discourse.group/t/user-defined-non-copy-record-initializer-on-other-type-breaks-resolution-of-compilers-default-copy-initializer/19327/3 "2023-02-10T23:13:50Z")

</div>

Hi Paul. Unfortunately this is an error in our documentation. At one point it was true that a compiler-generated copy initializer would always be generated, but that behavior has since changed and the documentation was not updated to reflect the new behavior.

Part of the motivation behind this change was to allow users to indicate that a type cannot be copy-initialized. It was also found in practice that always compiler-generating the copy initializer could lead to some compilation or execution errors that became difficult to diagnose due to the invisible nature of the compiler-generated copy initializer.

I will work on updating the documentation to reflect the current behavior.

---

<div class="post-metadata">

**Author:** ![bradcray](https://yyz2.discourse-cdn.com/free1/user_avatar/chapel.discourse.group/bradcray/32/72_2.png) [@bradcray](https://chapel.discourse.group/u/bradcray)\
**Post date:** [February 10, 2023, 11:46pm UTC](https://chapel.discourse.group/t/user-defined-non-copy-record-initializer-on-other-type-breaks-resolution-of-compilers-default-copy-initializer/19327/4 "2023-02-10T23:46:04Z")

</div>

As Ben reminded me offline, it's worth mentioning that for cases like this where the presence of a user method disables the compiler-provided version of the method, we've wanted to provide a way to have the type author opt into preserving the compiler-generated initializer without having to re-implement it manually themselves. For example, the compiler-generated initializer for a large record can be very handy, but sometimes you need a second initializer with a different signature as well. Today, adding that initializer would cause the compiler-generated initializer not to be created, but there ought to be a way to say "I want both" without reimplementing the compiler's version manually.

This is an idea that hasn't had detractors that I'm aware of, and is just looking for a good proposal for how it should be expressed.

-Brad

---

<div class="post-metadata">

**Author:** ![benharsh](https://avatars.discourse-cdn.com/v4/letter/b/7feea3/32.png) [@benharsh](https://chapel.discourse.group/u/benharsh)\
**Post date:** [February 27, 2023, 6:15pm UTC](https://chapel.discourse.group/t/user-defined-non-copy-record-initializer-on-other-type-breaks-resolution-of-compilers-default-copy-initializer/19327/5 "2023-02-27T18:15:10Z")

</div>

Following up on this topic, I've [merged a PR](https://github.com/chapel-lang/chapel/pull/21682) that will update the relevant documentation for the 1.30 release.
