Intel® C++ Compiler
Community support and assistance for creating C++ code that runs on platforms based on Intel® processors.
7944 Discussions

Failed assignment with std::optional

vindriolo
Beginner
1,766 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
1,739 Views

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

0 Kudos
PrasanthD_intel
Moderator
1,701 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

0 Kudos
PrasanthD_intel
Moderator
1,673 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

0 Kudos
Viet_H_Intel
Moderator
1,619 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,


0 Kudos
vindriolo
Beginner
1,614 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.  

0 Kudos
Viet_H_Intel
Moderator
1,589 Views

Hi,

Any update on this?

Thanks,


0 Kudos
Viet_H_Intel
Moderator
1,567 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,


0 Kudos
vindriolo
Beginner
1,547 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

 




0 Kudos
vindriolo
Beginner
1,542 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();"

0 Kudos
Viet_H_Intel
Moderator
1,540 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.


0 Kudos
Viet_H_Intel
Moderator
1,455 Views

Hi,

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

Thanks,


0 Kudos
Viet_H_Intel
Moderator
1,440 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,


0 Kudos
Reply