Weird C++ error cannot bind rvalue reference of type

Ok, you people who actally know C++:

I have this trivial program for the Adafruit Feather M4 CAN board (a SAME51):

Oscctrl *osc = OSCCTRL;
void setup() {
  OSCCTRL_EVCTRL_Type evc = osc->EVCTRL;
}

void loop() {}

OSCCTRL is defined off in the CMSIS-Atmel .h files (inside "extern "C""):

#define OSCCTRL           ((Oscctrl  *)0x40001000UL) /**< \brief (OSCCTRL) APB Base Address */

And also the Oscctrl type:

typedef struct {
  __IO OSCCTRL_EVCTRL_Type       EVCTRL;      /**< \brief Offset: 0x00 (R/W  8) Event Control */
       RoReg8                    Reserved1[0x3];
  __IO OSCCTRL_INTENCLR_Type     INTENCLR;    /**< \brief Offset: 0x04 (R/W 32) Interrupt Enable Clear */
  __IO OSCCTRL_INTENSET_Type     INTENSET;    /**< \brief Offset: 0x08 (R/W 32) Interrupt Enable Set */
  __IO OSCCTRL_INTFLAG_Type      INTFLAG;     /**< \brief Offset: 0x0C (R/W 32) Interrupt Flag Status and Clear */
  __I  OSCCTRL_STATUS_Type       STATUS;      /**< \brief Offset: 0x10 (R/  32) Status */
  __IO OSCCTRL_XOSCCTRL_Type     XOSCCTRL[2]; /**< \brief Offset: 0x14 (R/W 32) External Multipurpose Crystal Oscillator Control */
  __IO OSCCTRL_DFLLCTRLA_Type    DFLLCTRLA;   /**< \brief Offset: 0x1C (R/W  8) DFLL48M Control A */
       RoReg8                    Reserved2[0x3];
  __IO OSCCTRL_DFLLCTRLB_Type    DFLLCTRLB;   /**< \brief Offset: 0x20 (R/W  8) DFLL48M Control B */
       RoReg8                    Reserved3[0x3];
  __IO OSCCTRL_DFLLVAL_Type      DFLLVAL;     /**< \brief Offset: 0x24 (R/W 32) DFLL48M Value */
  __IO OSCCTRL_DFLLMUL_Type      DFLLMUL;     /**< \brief Offset: 0x28 (R/W 32) DFLL48M Multiplier */
  __IO OSCCTRL_DFLLSYNC_Type     DFLLSYNC;    /**< \brief Offset: 0x2C (R/W  8) DFLL48M Synchronization */
       RoReg8                    Reserved4[0x3];
       OscctrlDpll               Dpll[2];     /**< \brief Offset: 0x30 OscctrlDpll groups [DPLLS_NUM] */
} Oscctrl;

and OSCCTRL_EVCTRL_Type:

typedef union {
  struct {
    uint8_t  CFDEO0:1;         /*!< bit:      0  Clock 0 Failure Detector Event Output Enable */
    uint8_t  CFDEO1:1;         /*!< bit:      1  Clock 1 Failure Detector Event Output Enable */
    uint8_t  :6;               /*!< bit:  2.. 7  Reserved                           */
  } bit;                       /*!< Structure used for bit  access                  */
  struct {
    uint8_t  CFDEO:2;          /*!< bit:  0.. 1  Clock x Failure Detector Event Output Enable */
    uint8_t  :6;               /*!< bit:  2.. 7  Reserved                           */
  } vec;                       /*!< Structure used for vec  access                  */
  uint8_t reg;                 /*!< Type      used for register access              */
} OSCCTRL_EVCTRL_Type;

And I get:

sketch_may13a:4:33: error: no matching function for call to 'OSCCTRL_EVCTRL_Type(volatile OSCCTRL_EVCTRL_Type&)'
    4 |  OSCCTRL_EVCTRL_Type evc = osc->EVCTRL;
      |                                 ^~~~~~
In file included from /Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/tools/CMSIS-Atmel/1.2.2/CMSIS/Device/ATMEL/same51/include/same51j19a.h:576,
                 from /Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/tools/CMSIS-Atmel/1.2.2/CMSIS/Device/ATMEL/same.h:60,
                 from /Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/tools/CMSIS-Atmel/1.2.2/CMSIS/Device/ATMEL/sam.h:561,
                 from /Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/hardware/samd/1.7.16/cores/arduino/Arduino.h:48,
                 from /tmp/Arduino1.8.19Build/sketch/sketch_may13a.ino.cpp:1:
/Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/tools/CMSIS-Atmel/1.2.2/CMSIS/Device/ATMEL/same51/include/component/oscctrl.h:55:3: note: candidate: 'constexpr OSCCTRL_EVCTRL_Type::OSCCTRL_EVCTRL_Type(const OSCCTRL_EVCTRL_Type&)' <near match>
   55 | } OSCCTRL_EVCTRL_Type;
      |   ^~~~~~~~~~~~~~~~~~~
/Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/tools/CMSIS-Atmel/1.2.2/CMSIS/Device/ATMEL/same51/include/component/oscctrl.h:55:3: note:   conversion of argument 1 would be ill-formed:
sketch_may13a:4:33: error: binding reference of type 'const OSCCTRL_EVCTRL_Type&' to 'volatile OSCCTRL_EVCTRL_Type' discards qualifiers
    4 |  OSCCTRL_EVCTRL_Type evc = osc->EVCTRL;
      |                            ~~~~~^~~~~~
In file included from /Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/tools/CMSIS-Atmel/1.2.2/CMSIS/Device/ATMEL/same51/include/same51j19a.h:576,
                 from /Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/tools/CMSIS-Atmel/1.2.2/CMSIS/Device/ATMEL/same.h:60,
                 from /Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/tools/CMSIS-Atmel/1.2.2/CMSIS/Device/ATMEL/sam.h:561,
                 from /Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/hardware/samd/1.7.16/cores/arduino/Arduino.h:48,
                 from /tmp/Arduino1.8.19Build/sketch/sketch_may13a.ino.cpp:1:
/Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/tools/CMSIS-Atmel/1.2.2/CMSIS/Device/ATMEL/same51/include/component/oscctrl.h:55:3: note: candidate: 'constexpr OSCCTRL_EVCTRL_Type::OSCCTRL_EVCTRL_Type(OSCCTRL_EVCTRL_Type&&)' <near match>
   55 | } OSCCTRL_EVCTRL_Type;
      |   ^~~~~~~~~~~~~~~~~~~
/Applications/Arduino-1.8.19.app/Contents/Java/portable/packages/adafruit/tools/CMSIS-Atmel/1.2.2/CMSIS/Device/ATMEL/same51/include/component/oscctrl.h:55:3: note:   conversion of argument 1 would be ill-formed:
sketch_may13a:4:33: error: cannot bind rvalue reference of type 'OSCCTRL_EVCTRL_Type&&' to lvalue of type 'volatile OSCCTRL_EVCTRL_Type'
    4 |  OSCCTRL_EVCTRL_Type evc = osc->EVCTRL;
      |                            ~~~~~^~~~~~
exit status 1
no matching function for call to 'OSCCTRL_EVCTRL_Type(volatile OSCCTRL_EVCTRL_Type&)'

Should be a union assigned from a volatile union, right? I don't mind discarding the "volatile" aspect (I was the current value), but what's the rest of the error mean, and how do I fix it? Why does it think the rvalue and lvalues are C++ "references" (and why don't they match?)

(The code is accepted in a .c file, BTW.)

I think it's because the usual C++ compiler generated copy constructor doesn't include volatile, only const.

You could try making a reference to it rather than a copy.

__IO OSCCTRL_EVCTRL_Type &evc = osc->EVCTRL;

Dollars to donuts __IO is volatile.

Edit: and here's the whys and wherefores:

Then I'd put it in a function in a .c file and call it from your C++ using "extern "C"" to tell your C++ code that the function has 'C' linkage.

Try this:

typedef union {
   unsigned char a;
   signed char b;
} Chars;

typedef struct {
   char c;
   volatile Chars d;
} Struct;

Struct s;
Struct *p = &s;

void setup() {
   Chars e = p->d;
}

void loop() {}

You'll see the same errors you're getting. Take the volatile off the Chars member and it'll sail through. You'll also get the same errors with Chars e = s.d;.

Yes __IO is volatile
So why does it work OK for simple types, like uint8_t reg = (osc->EVCTRL.reg);

Does this mean that there's no way to have a local variable of a volatile bitfield struct in C++? (maybe that's why CMSIS doesn't like such unions?)

I want to do something like:

void print_evctrl(OSCCTRL_EVCTRL_Type evctrl) {
  printf("EVCTRL (Event Control) @ 0x00\n");
  printf("  ├─ CFDEO0 (Clock 0 Failure Detector Event Output):  %s\n",
         evctrl.bit.CFDEO0 ? "ENABLED" : "DISABLED");
  printf("  └─ CFDEO1 (Clock 1 Failure Detector Event Output):  %s\n",
         evctrl.bit.CFDEO1 ? "ENABLED" : "DISABLED");
  printf("  Raw: 0x%02X\n\n", evctrl.reg);
}

Modifying @van_der_decken's example code, the below compiles without error. But remember type punning via a Union is not allowed in C++. That's probably why this SAMD51 core stuff (and the SAMD21 core stuff before it) is written in C, not C++.

typedef union {
  unsigned char a;
  signed char b;
} Chars;

typedef struct {
  char c;
  volatile Chars d;
} Struct;

Struct s;
Struct *p = &s;

void setup() {
  Chars e = * (const_cast<Chars *> (&(p->d)));
}

void loop() {}

And here's the modified @westfw code that compiles (but is untested):

Oscctrl *osc = OSCCTRL;
void setup() {
  OSCCTRL_EVCTRL_Type evc = *(const_cast<OSCCTRL_EVCTRL_Type *>(&(osc->EVCTRL)));
}

void loop() {}

I imagine because the compiler doesn't have to generate a copy constructor for simple types.