Richard Biener via Gcc [Friday, 22 May 2026, 14:52:05 CEST]:
> 
> 
> > Am 22.05.2026 um 13:49 schrieb Matthias Kretz <[email protected]>:
> 
> > Jason Merrill via Gcc [Thursday, 21 May 2026, 20:12:01 CEST]:
> 
> 
> 
> >> This seems too broad a category;
> 
> 
> 
> > I think it's a very interesting category. But, with that name, it's not
> > very discoverable for users.
> > 
> > My experience with these kinds of warnings (and I implemented library
> > precondition checking in a similar manner) is that they can be resolved by
> > precondition checking that is visible to the optimizer. I like to think of
> > this as "forcing precondition checks to bubble up".
> > 
> > Consider:
> >  char str[] = "Hello";
> >  
> >  // precondition: cond == false
> >  char
> >  f(bool cond)
> >  { return cond ? str[100] : str[0]; }
> > 
> > This warns about str[100] being out of bounds. But the real problem was
> > that f was called out-of-contract. If we add "pre (cond == false)" and
> > compile with -fno-contracts-conservative-ipa (and a terminating contracts
> > evaluation semantic) the warning goes away.
> > 
> > So you can think about these warnings as "your function is missing a
> > precondition check". Conceptually. Because C++ contracts currently default
> > to hiding from the optimizer.
> > 
> > Thus:
> > 1. middle-end warnings should hint at missing precondition checks
> > 2. middle-end warnings maybe should only be on by default if contracts
> > checking is visibly (to the optimizer) terminating. (I've been using
> > __builtin_trap for this.)
> > 
> > On the name: "-Wmiddle-end" is an explanation to compiler devs and nobody
> > else. How about something in the direction of "-Wstatic-precondition-
> > checking"?
> 
> I think this is a good observation.  Much like that diagnostics that do this
> should probably enable diagnostic-paths, at least for their emitted
> diagnostics.  Because the path is what shows the currently visible
> preconditions.  Remember that esp. with C++ the set of visible
> preconditions is heavily dependent on inlining decisions.

Right. The other "solution" to remove the warning for f(bool) above is to 
declare it inline. Then it's up to the caller to get precondition checks 
right. Like this (https://compiler-explorer.com/z/E5GKfaWYz):

char str[10] = {};

// precondition: !cond
inline char
f(bool cond)
{ return cond ? str[100] : str[0]; }

// precondition: x < 0
char
g(int x)
{
  if (x >= 0) __builtin_trap(); // remove => the warning is emitted
  return f(x > 0);
}

 --- * ---

For reference, this is the precondition check macro I use while developing 
std::simd:

#define _GLIBCXX_SIMD_LOC __FILE__ ":" _GLIBCXX_SIMD_TOSTRING(__LINE__) ": "

#define __glibcxx_simd_precondition(expr, msg, ...)                          \
  do {                                                                       \
    const bool __precondition_result = !bool(expr);                          \
    if (__builtin_constant_p(__precondition_result) && __precondition_result)\
      {                                                                      \
        struct _Precondition                                                 \
        {                                                                    \
          [[__gnu__::__noipa__, __gnu__::__warning__("precondition failure. "\
            "\n" _GLIBCXX_SIMD_LOC "note: " msg " (precondition '" #expr     \
            "' does not hold)")]]                                            \
          static inline void _S_fail() noexcept {}                           \
        };                                                                   \
        _Precondition::_S_fail();                                            \
      }                                                                      \
    if (__builtin_expect(__precondition_result, false))                      \
      std::simd::__invoke_ub(                                                \
        _GLIBCXX_SIMD_LOC "precondition failure in '%s':\n" msg " ('" #expr  \
        "' does not hold)", __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__);  \
  } while(false)


I recently wrote (B is an mdspan):

  simd::unchecked_load<V>(&B[k, 0], &B[k, M-1], simd::flag_aligned);

where I wanted to load a matrix row of M values. The end iterator is off-by-
one. And this is what I got:

/home/mkretz/src/simd/include/bits/simd_details.h|87 col 31 warning| call to 
'std::simd::unchecked_load<basic_vec<float, _Abi<8, 1, 0> >, std::span<const 
float, 18446744073709551615>, __aligned_flag>(std::span<const float, 
18446744073709551615>&&, flags<__aligned_flag>)::_Precondition::_S_fail' 
declared with attribute warning: precondition failure. 
/home/mkretz/src/simd/include/bits/simd_loadstore.h|65| note: Input range is 
too small. Did you mean to use 'partial_load'? (precondition 
'std::ranges::size(__r) >= _RV::size()' does not hold) [-Wattribute-warning]

Running the program traps by default. With -D_GLIBCXX_ASSERTIONS, running the 
program aborts after printing:

/home/mkretz/src/simd/include/bits/simd_loadstore.h|65| precondition failure 
in 'constexpr std::simd::__vec_load_return_t<_Vp, 
std::ranges::range_value_t<_View> > std::simd::unchecked_load(_Rg&&, 
flags<_Flags ...>) [with _Vp = basic_vec<float, _Abi<8, 1, 0> >; _Rg = 
std::span<const float, 18446744073709551615>; _Flags = {__aligned_flag}; 
__vec_load_return_t<_Vp, std::ranges::range_value_t<_View> > = 
basic_vec<float, _Abi<8, 1, 0> >; std::ranges::range_value_t<_View> = float]':
Input range is too small. Did you mean to use 'partial_load'? 
('std::ranges::size(__r) >= _RV::size()' does not hold)


Somewhat similar to -fhardened, I think this is a useful build-mode to have.

- Matthias

-- 
───────────────────────────────────────────────────────────────────
 Matthias Kretz                        https://www.kretzfamily.de/
───────────────────────────────────────────────────────────────────

Attachment: signature.asc
Description: This is a digitally signed message part.

Reply via email to