Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang] Function call without comparisons reported as non-constant expression after CWG2765 implementation #117409

Open
ormris opened this issue Nov 23, 2024 · 4 comments
Labels
c++20 clang:analysis clang:diagnostics New/improved warning or error message in Clang, but not in clang-tidy or static analyzer

Comments

@ormris
Copy link
Collaborator

ormris commented Nov 23, 2024

We're seeing a new diagnostic trigger in an internal test. The diagnostic is implemented in...

commit d8a281590311010955c323806fb24fa484376f4d
Author: Richard Smith <[email protected]>
Date:   Thu Sep 26 15:56:33 2024 -0700

    [clang] implement current direction of CWG2765 for string literal comparisons in constant evaluation (#109208)

    Track the identity of each string literal object produced by evaluation
    with a global version number. Accept comparisons between literals of the
    same version, and between literals of different versions that cannot
    possibly be placed in overlapping storage. Treat the remaining
    comparisons as non-constant.

    ---------

    Co-authored-by: Timm Baeder <[email protected]>
    Co-authored-by: Aaron Ballman <[email protected]>

Reduced test case below. The test case and CWG document seem to indicate that comparison is the expected use case, but I don't see any comparison operations in the code below. The values returned by the function indicated in the diagnostic are constant as well.

template <class> struct g;
template <class b> struct g<const b> { using e = b; };
template <class> struct l;
template <class b> struct l<b &> { using e = b; };
template <typename aa> struct ab {
  const aa *ac;
  ab();
  constexpr ab(const aa *k, long) : ac(k) {}
  constexpr auto af() { return ac; }
};
constexpr void al(const char *am, ab<char> aj) {
  am - aj.af();
}
struct L {
  ab<char> bp;
  template <typename o> consteval L(o k) : bp(k) {
    al(bp.af(), k);
  }
};
template <typename br> void q(br, L);
template <typename aa, int s> constexpr auto t(const aa (&k)[s]) -> ab<aa> {
  return {k, s};
}
void e() {
  q(0, [] {
    struct bx {
      constexpr operator ab<g<l<decltype(""[0])>::e>::e>() { return t(""); }
    };
    return bx();
  }());
} 

To reproduce:

$ clang -std=c++20 test.cpp
test.cpp:12:6: warning: expression result unused [-Wunused-value]
   12 |   am - aj.af();
      |   ~~ ^ ~~~~~~~
test.cpp:25:8: error: call to consteval function 'L::L<bx>' is not a constant expression
   25 |   q(0, [] {
      |        ^
test.cpp:12:6: note: subexpression not valid in a constant expression
   12 |   am - aj.af();
      |   ~~~^~~~~~~~~
test.cpp:17:5: note: in call to 'al(&""[0], {&""[0]})'
   17 |     al(bp.af(), k);
      |     ^~~~~~~~~~~~~~
test.cpp:25:8: note: in call to 'L<bx>({})'
   25 |   q(0, [] {
      |        ^~~~
   26 |     struct bx {
      |     ~~~~~~~~~~~
   27 |       constexpr operator ab<g<l<decltype(""[0])>::e>::e>() { return t(""); }
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   28 |     };
      |     ~~
   29 |     return bx();
      |     ~~~~~~~~~~~~
   30 |   }());
      |   ~~~
1 warning and 1 error generated.
$
@ormris ormris added c++20 clang:diagnostics New/improved warning or error message in Clang, but not in clang-tidy or static analyzer clang:analysis labels Nov 23, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 23, 2024

@llvm/issue-subscribers-c-20

Author: Matthew Voss (ormris)

We're seeing a new diagnostic trigger in an internal test. The diagnostic is implemented in...
commit d8a281590311010955c323806fb24fa484376f4d
Author: Richard Smith &lt;richard@<!-- -->metafoo.co.uk&gt;
Date:   Thu Sep 26 15:56:33 2024 -0700

    [clang] implement current direction of CWG2765 for string literal comparisons in constant evaluation (#<!-- -->109208)

    Track the identity of each string literal object produced by evaluation
    with a global version number. Accept comparisons between literals of the
    same version, and between literals of different versions that cannot
    possibly be placed in overlapping storage. Treat the remaining
    comparisons as non-constant.

    ---------

    Co-authored-by: Timm Baeder &lt;tbaeder@<!-- -->redhat.com&gt;
    Co-authored-by: Aaron Ballman &lt;aaron@<!-- -->aaronballman.com&gt;

Reduced test case below. The test case and CWG document seem to indicate that comparison is the expected use case, but I don't see any comparison operations in the code below. The values returned by the function indicated in the diagnostic are constant as well.

template &lt;class&gt; struct g;
template &lt;class b&gt; struct g&lt;const b&gt; { using e = b; };
template &lt;class&gt; struct l;
template &lt;class b&gt; struct l&lt;b &amp;&gt; { using e = b; };
template &lt;typename aa&gt; struct ab {
  const aa *ac;
  ab();
  constexpr ab(const aa *k, long) : ac(k) {}
  constexpr auto af() { return ac; }
};
constexpr void al(const char *am, ab&lt;char&gt; aj) {
  am - aj.af();
}
struct L {
  ab&lt;char&gt; bp;
  template &lt;typename o&gt; consteval L(o k) : bp(k) {
    al(bp.af(), k);
  }
};
template &lt;typename br&gt; void q(br, L);
template &lt;typename aa, int s&gt; constexpr auto t(const aa (&amp;k)[s]) -&gt; ab&lt;aa&gt; {
  return {k, s};
}
void e() {
  q(0, [] {
    struct bx {
      constexpr operator ab&lt;g&lt;l&lt;decltype(""[0])&gt;::e&gt;::e&gt;() { return t(""); }
    };
    return bx();
  }());
} 

To reproduce:

$ clang -std=c++20 test.cpp
test.cpp:12:6: warning: expression result unused [-Wunused-value]
   12 |   am - aj.af();
      |   ~~ ^ ~~~~~~~
test.cpp:25:8: error: call to consteval function 'L::L&lt;bx&gt;' is not a constant expression
   25 |   q(0, [] {
      |        ^
test.cpp:12:6: note: subexpression not valid in a constant expression
   12 |   am - aj.af();
      |   ~~~^~~~~~~~~
test.cpp:17:5: note: in call to 'al(&amp;""[0], {&amp;""[0]})'
   17 |     al(bp.af(), k);
      |     ^~~~~~~~~~~~~~
test.cpp:25:8: note: in call to 'L&lt;bx&gt;({})'
   25 |   q(0, [] {
      |        ^~~~
   26 |     struct bx {
      |     ~~~~~~~~~~~
   27 |       constexpr operator ab&lt;g&lt;l&lt;decltype(""[0])&gt;::e&gt;::e&gt;() { return t(""); }
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   28 |     };
      |     ~~
   29 |     return bx();
      |     ~~~~~~~~~~~~
   30 |   }());
      |   ~~~
1 warning and 1 error generated.
$

@tbaederr
Copy link
Contributor

CC @zygoloid

@zygoloid
Copy link
Collaborator

The problem is that the conversion function to const char* is called twice and evaluates a string literal each time, and is allowed to return a different pointer each time. That means the subsequent pointer subtraction has undefined behavior (if the pointers are in fact different).

Was this reduced from fmtlib by any chance? This looks like the exact same issue as in #113517. If so, the bug is fixed in fmtlib already.

@tbaederr
Copy link
Contributor

The relevant diagnostic in the output is just

test.cpp:12:6: note: subexpression not valid in a constant expression
   12 |   am - aj.af();
      |   ~~~^~~~~~~~~

isn't it? Can we improve that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++20 clang:analysis clang:diagnostics New/improved warning or error message in Clang, but not in clang-tidy or static analyzer
Projects
None yet
Development

No branches or pull requests

4 participants