Intel® C++ Compiler
Support and discussions for creating C++ code that runs on platforms based on Intel® processors.
7650 Discussions

Failed assignment with std::optional

vindriolo
Beginner
719 Views

I am testing some code that developed some unexpected runtime behavior when built with ICC.  GCC builds work correctly.  Unfortunately, I have not yet been able to reproduce the failure in an isolated example, so I can't post the code directly.

There is an accessor method that checks an optional value and initializes it when necessary.  I see an exception thrown (std::bad_optional_access) in the last line, which should not be possible.   The value is always assigned if the std::optional's has_value returns false. 

 

// E is an enum class
// o is a reference to std::optional<E>
if (!o.has_value())
{
  E value = deriveEnum();
  o = value;
}
return o.value();

 

While the assignment fails, I found that using the 'emplace' method on the std::optional seems to work. Adding the block afterwards, I see the 'second setter' log and no exceptions are thrown.

 

// E is an enum class
// o is a reference to std::optional<E>
if (!o.has_value())
{
  E value = deriveEnum();
  o = value;

  // I added this to catch the failure to initialize
  // And prevent std::bad_optional_access from being thrown
  if (!o.has_value())
  {
    log("second setter");
    o.emplace(value)
  }
}
return o.value();

 

Is this related to any known issues with ICC?

The version of ICC I'm using is this:
icpc (ICC) 19.1.2.254 20200623

 

0 Kudos
12 Replies
vindriolo
Beginner
692 Views

This seems to have appeared when using icpc and devtoolset-9.  I don't see this when using icpc and devtoolset-8.

PrasanthD_intel
Moderator
654 Views

Hi Vince,

 

Thanks for reaching out to us.

I have constructed a sample code from your snippets and tried to reproduce your issue in our environment(Ubuntu). I haven't got any exceptions while running the code.

I am attaching my code and could you please take a look and suggest any changes to reproduce the error.

 

 

 

#include<iostream>

#include<string>

#include<optional>


enum class E : int{ var1 =1,

        var2 = 2,

        var3 = 3 };

enum E deriveEnum(void)

{

  return E::var3;

}

auto accessor(std::optional<E> o){

    try {

    if (!o.has_value())

    {

     E value = deriveEnum();

     o = value;

     std::cout<<"value of o is"<<static_cast<int>(*o)<<std::endl;

     /*if (!o.has_value())

     {

       std::cout<<"second setter"<<std::endl;

       o.emplace(value);

     }*/

    }

    return o.value();

    } catch(const std::bad_optional_access& e) {

        std::cout << e.what() << '\n';

    } catch (const std::exception& e) {

        std::cout << e.what() << '\n';

    } catch (const std::string& ex) {

        std::cout << ex << '\n';

    } catch (...) {

        std::cout<<"exception caught"<<'\n';

    }
}

int main() {

    std::optional<E> opt = {};

    auto value = accessor(opt);

    std::cout<<"value is"<<static_cast<int>(value)<<std::endl;

    return 0;

}

 

 

 

 

I have checked the release notes haven't found this kind of issue in Known Limitations.

Please provide your environment details (OS version, GCC version, Kernel Version etc..)

 

Regards

Prasanth

PrasanthD_intel
Moderator
626 Views

Hi Vince,

 

We haven't heard back from you.

Let us know if the given code is similar to yours or else please suggest any changes.

We are working on it and get back to you.

 

Regards

Prasanth

Viet_H_Intel
Moderator
572 Views

As Prasanth mentioned, we haven't been able to reproduce the issue at our end. Can you provide us a reproducer to investigate?

Thanks,


vindriolo
Beginner
567 Views

Sorry, for the lack of replies.  I was away from the office for a couple weeks.  The example you posted wasn't showing the behavior either.  I was able to work around the problem in my own code.  I'll see if I can recreate the problem in an isolated example.  

Viet_H_Intel
Moderator
542 Views

Hi,

Any update on this?

Thanks,


Viet_H_Intel
Moderator
520 Views

Please update us on this issue. If we don't hear from you in the next few days, we may go ahead and close this thread.


Thanks,


vindriolo
Beginner
500 Views

I had put a workaround in the code, but I just confirmed that the original problem is still there as stated.  I have not yet been able to create an isolated example that demonstrates the problem.  Due to -ipo and other flags, it takes 15 minutes or more to build and link a release binary.   I took the example and dropped it into my release binary using the same storage for optional as my code.  It exhibited the problem, logging "bad_optional_access: bad optional access".  I am beginning to think that this might be caused by the storage of the structure containing the std::optional values, which is periodically refreshed by calling "m_storage = {};" elsewhere.  The m_storage class is a templated container class which is selecting the appropriate structure via getter.

E MyClass::deriveEnum(int c) {
  auto& m_e = m_storage.get(1).e; // in my own code the getter uses a different enum
  if (!m_e.has_value()) {
    E e;
    switch (c)
    {
       case 1: e = E::var1;
       case 2: e = E::var2;
       case 3:
       default:
       e = E::var3;
    }
    m_e = e;
  }

  try {
    return m_e.value();
  } catch(const std::bad_optional_access& e) {
    LOG("bad_optional_access: %s\n", e.what());
  } catch (const std::exception& e) {
    LOG("exception: %s\n", e.what());
  } catch (const std::string& ex) {
    LOG("string: %s\n", ex);
  } catch (...) {
    LOG("other exception\n");
  }
  return E::var1;
}

 

 

 

 


@vindriolo wrote:

I am testing some code that developed some unexpected runtime behavior when built with ICC.  GCC builds work correctly.  Unfortunately, I have not yet been able to reproduce the failure in an isolated example, so I can't post the code directly.

There is an accessor method that checks an optional value and initializes it when necessary.  I see an exception thrown (std::bad_optional_access) in the last line, which should not be possible.   The value is always assigned if the std::optional's has_value returns false. 

 

// E is an enum class
// o is a reference to std::optional<E>
if (!o.has_value())
{
  E value = deriveEnum();
  o = value;
}
return o.value();

 

While the assignment fails, I found that using the 'emplace' method on the std::optional seems to work. Adding the block afterwards, I see the 'second setter' log and no exceptions are thrown.

 

// E is an enum class
// o is a reference to std::optional<E>
if (!o.has_value())
{
  E value = deriveEnum();
  o = value;

  // I added this to catch the failure to initialize
  // And prevent std::bad_optional_access from being thrown
  if (!o.has_value())
  {
    log("second setter");
    o.emplace(value)
  }
}
return o.value();

 

Is this related to any known issues with ICC?

The version of ICC I'm using is this:
icpc (ICC) 19.1.2.254 20200623

 




vindriolo
Beginner
495 Views

I pulled the m_e field out of the m_storage structure and it still shows the problem when performing reset via "m_e = {};".   However, it looks like the problem goes away when I change "m_e = {};" to "m_e.reset();"

Viet_H_Intel
Moderator
493 Views

I searched our bug database and couldn't find anything related to this one.

v19.1.2 is quite old. Can you try oneAPI 2021.2 HPC toolkit to see if the problem still occurs?

We need to have a reproducer in order to investigate this issue and I will keep this thread open in case you have a reproducer for us.

Thanks for your update.


Viet_H_Intel
Moderator
408 Views

Hi,

Were you able to construct a reproducer for us to investigate?

Thanks,


Viet_H_Intel
Moderator
393 Views

Hi Vince,


We will no longer respond to this thread. If you require additional assistance from Intel, please start a new thread. Any further interaction in this thread will be considered community only.


Thanks,


Reply