Skip to content

Miscompilation with libstdc++'s std::optional<int> and -O1 #98753

Closed
@cadubentzen

Description

@cadubentzen

https://godbolt.org/z/KjEreWf57

With Clang 18.1 and libstdc++, we get different behavior when compared to Clang 17 in the code below.
EDIT: requires at least -O1.

#include <optional>
#include <iostream>

// defined in a separate compilation unit
int takeIntRefAndReturn0(int&);

std::optional<int> shouldReturnEmptyOptional() {
  int v = 5;
  if (takeIntRefAndReturn0(v))
    return v;
  return std::nullopt;
}

int main() {
  auto opt = shouldReturnEmptyOptional();
  if (opt && *opt > 0) {
    std::cout << "SHOULD NOT BE PRINTED: *opt = " << *opt << std::endl;
    return 1;
  }
  std::cout << "SHOULD BE PRINTED" << std::endl;
}

With Clang 17, we get SHOULD BE PRINTED, while with with Clang 18.1 we get SHOULD NOT BE PRINTED: *opt = 5.

With git-bisect, I found that this is caused by 060de41.

An isomorphic example to reproduce this (https://godbolt.org/z/9PsjY17sT):

int takeIntRefReturn0(int &);
void assertNotReached(int);

static bool logicalAnd(bool a, bool b) { return a && b; }

int main() {
  int v4;
  bool v3;
  {
    int v1 = 5;
    int v2 = takeIntRefReturn0(v1);
    v3 = v2 != 0;
    if (v3)
      v4 = v1;
  }
  // Move  to a function so that && is not short cutted.
  // v4 will be undefined if v2 == 0, but v3 is false, so the branch shouldn't be entered.
  if (logicalAnd(v3, v4 > 0))
    assertNotReached(v4);

  return 0;
}

Note in the generated LLVM IR that

%6 = icmp sgt i32 %5, 0
%7 = and i1 %3, %6
br i1 %7, label %8, label %9

was reduced to only

%6 = icmp sgt i32 %5, 0
br i1 %6, label %7, label %8

Metadata

Metadata

Assignees

Type

No type

Projects

Status

Needs Triage

Relationships

None yet

Development

No branches or pull requests

Issue actions