On Wed, Sep 09, 2026 at 10:11:38AM -0400, Jason Merrill wrote:
> On 9/9/26 3:41 AM, Jakub Jelinek wrote:
> > On Thu, Aug 06, 2026 at 05:31:50PM +0200, Jakub Jelinek wrote:
> > > > > Turns out it doesn't prevent that, because match.pd has also
> > > > > /* Associate (p +p off1) +p off2 as (p +p (off1 + off2)). */
> > > > > (simplify
> > > > > (pointer_plus (pointer_plus:s @0 @1) @3)
> > > > > (pointer_plus @0 (plus @1 @3)))
> > > > > #if GENERIC
> > > > > (simplify
> > > > > (pointer_plus (convert:s (pointer_plus:s @0 @1)) @3)
> > > > > (pointer_plus (convert:type @0) (plus @1 @3)))
> > > > > #endif
> >
> > > Guess I'll try the cxx_fold_pointer_plus_expression change you're
> > > proposing.
> >
> > Actually cxx_fold_pointer_plus_expression can't help that much, the main
> > optimization is folding of (convert (pointer_plus @0 @1)) into
> > (pointer_plus (convert @0) @1) and similar optimizations, so upon
> > fold_convert, which isn't called just from cp_fold_convert, but many other
> > spots.
>
> Yes? I was thinking that when cxx_fold_pointer_plus_expression sees
> (pointer_plus (convert @0) @1) it can evaluate (pointer_plus @0 @1) instead
> and then convert the result.
Ah, I forgot the details.
I still think it is unsafe compared to the patch with the langhook I've
posted, e.g. it makes us reject
struct A { int a; };
struct B : A {};
struct C { B a; A b; A c; };
constexpr int
foo ()
{
C d {};
A *p = static_cast <A *> (&d.a);
B *q = static_cast <B *> (p) + 2;
A *r = static_cast <A *> (q);
return r->a;
}
static_assert (foo () == 0);
which we accepted before (though clang++ rejects it, but for a different
reason - that the pointer addition is not valid because d.a is not an array
element). So, now it is rejected for a wrong reason with this patch.
I don't know if it is possible to construct a valid testcase where this
problem occurs. Another option would be for the POINTER_PLUS_EXPR of
NOP_EXPR/CONVERT_EXPR first evaluate the operand of the cast, then silently
check if the cast is valid, if so, evaluate it normally, if not, evaluate
it as cast of POINTER_PLUS_EXPR with changed type like in the patch
below, except we would have the cast operand already pre-evaluated and so
we'd need to outline most of the NOP_EXPR/CONVERT_EXPR
cxx_eval_constant_expression handling into a separate function (I have
actually done that yesterday already during another failed attempt to handle
this), but that would be risky too, it would err on the side of accepting
possibly invalid program over rejecting invalid one, because it would be
using union of whether the cast is valid before or after the
POINTER_PLUS_EXPR.
Anyway, the following passed
GXX_TESTSUITE_STDS=98,11,14,17,20,23,26,29 make check-g++ -j32
so far.
2026-09-11 Jakub Jelinek <[email protected]>
* constexpr.cc (cxx_eval_binary_expression): For POINTER_PLUS_EXPR
with pointer cast in orig_lhs operand evaluate it as if it was
cast of POINTER_PLUS_EXPR with cast operand.
(cxx_eval_constant_expression) <case CONVERT_EXPR>: Set
*non_constant_p and optionally diagnose if a static_cast downcast
results in undefined behavior.
* g++.dg/cpp0x/constexpr-static-cast1.C: New test.
* g++.dg/cpp0x/constexpr-static-cast2.C: New test.
* g++.dg/cpp1y/constexpr-static-cast1.C: New test.
* g++.dg/cpp1y/constexpr-static-cast2.C: New test.
* g++.dg/cpp1y/constexpr-static-cast3.C: New test.
* g++.dg/cpp1y/constexpr-static-cast4.C: New test.
--- a/gcc/cp/constexpr.cc 2026-09-10 17:54:18.991321017 +0200
+++ b/gcc/cp/constexpr.cc 2026-09-11 14:20:48.286379639 +0200
@@ -5671,6 +5671,38 @@ cxx_eval_binary_expression (const conste
tree orig_lhs = TREE_OPERAND (t, 0);
tree orig_rhs = TREE_OPERAND (t, 1);
tree lhs, rhs;
+
+ if (TREE_CODE (t) == POINTER_PLUS_EXPR
+ && CONVERT_EXPR_P (orig_lhs)
+ && (lhs = TREE_OPERAND (orig_lhs, 0))
+ && INDIRECT_TYPE_P (TREE_TYPE (lhs))
+ && (TREE_CODE (orig_lhs) != NOP_EXPR || !REINTERPRET_CAST_P (orig_lhs))
+ && is_properly_derived_from (TREE_TYPE (TREE_TYPE (orig_lhs)),
+ TREE_TYPE (TREE_TYPE (lhs))))
+ /* fold_unary_loc and match.pd can optimize
+ (type *) (ptr p+ off) into ((type *) ptr) p+ off, which can break
+ static_cast constexpr diagnostics, because ptr before the p+
+ cast to type * can be invalid while the original valid.
+ Evaluate it as (type *) (ptr p+ off) instead. */
+ {
+ tree cast = copy_node (orig_lhs);
+ tree pplus = copy_node (t);
+ TREE_OPERAND (cast, 0) = pplus;
+ TREE_OPERAND (pplus, 0) = lhs;
+ TREE_TYPE (pplus) = TREE_TYPE (lhs);
+ lhs = cxx_eval_constant_expression (ctx, cast, vc_prvalue,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (lhs != cast)
+ {
+ ggc_free (cast);
+ ggc_free (pplus);
+ }
+ if (*non_constant_p)
+ return t;
+ return lhs;
+ }
+
lhs = cxx_eval_constant_expression (ctx, orig_lhs, vc_prvalue,
non_constant_p, overflow_p,
jump_target);
@@ -10766,6 +10798,53 @@ cxx_eval_constant_expression (const cons
*non_constant_p = true;
return t;
}
+ }
+
+ /* [expr.static.cast]/10: A prvalue of type "pointer to cv1 B", where
+ B is a class type, can be converted to a prvalue of type
+ "pointer to cv2 D", where D is a complete class derived from B, ...
+ If the prvalue of type "pointer to cv1 B" points to a B that is
+ actually a base class subobject of an object of type D, the
+ resulting pointer points to the enclosing object of type D.
+ Otherwise, the behavior is undefined.
+ Similarly [expr.static.cast]/2 for references. */
+ if (INDIRECT_TYPE_P (type)
+ && INDIRECT_TYPE_P (TREE_TYPE (op))
+ && COMPLETE_TYPE_P (TREE_TYPE (type))
+ && !integer_zerop (op)
+ && is_properly_derived_from (TREE_TYPE (type),
+ TREE_TYPE (TREE_TYPE (op))))
+ {
+ tree sop = tree_strip_nop_conversions (op);
+ if (cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop,
+ NULL, jump_target) == NULL_TREE)
+ {
+ tree dyntype = NULL_TREE;
+ if (!ctx->quiet && TREE_CODE (sop) == ADDR_EXPR)
+ {
+ sop = TREE_OPERAND (sop, 0);
+ while (TREE_CODE (sop) == COMPONENT_REF
+ && DECL_FIELD_IS_BASE (TREE_OPERAND (sop, 1)))
+ sop = TREE_OPERAND (sop, 0);
+ dyntype = strip_array_types (TREE_TYPE (sop));
+ if (same_type_p (dyntype, TREE_TYPE (TREE_TYPE (op))))
+ dyntype = NULL_TREE;
+ }
+ if (!ctx->quiet)
+ {
+ if (dyntype)
+ error_at (loc, "%qT operand (of dynamic type %qT) is "
+ "not a base class subobject of a %qT object",
+ TREE_TYPE (TREE_TYPE (op)), dyntype,
+ TREE_TYPE (type));
+ else
+ error_at (loc, "%qT operand is not a base class "
+ "subobject of a %qT object",
+ TREE_TYPE (TREE_TYPE (op)), TREE_TYPE (type));
+ }
+ *non_constant_p = true;
+ return t;
+ }
}
if (TREE_CODE (op) == PTRMEM_CST && !TYPE_PTRMEM_P (type))
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-static-cast1.C 2026-09-11
14:12:11.820171208 +0200
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-static-cast1.C 2026-09-11
14:12:11.820171208 +0200
@@ -0,0 +1,76 @@
+// { dg-do compile { target c++11 } }
+
+struct A { int a; };
+struct B : A { int b; };
+struct C : A { int c; };
+struct D { int d; };
+struct E { int e; };
+struct F : A, D, E { int f; };
+struct G {};
+struct H {};
+struct I {};
+struct J : G, H, I {};
+struct K { F k; J l[2]; };
+struct L : G {};
+struct M : G {};
+struct N : L, M {};
+constexpr A a = {};
+constexpr B b = {};
+constexpr C c = {};
+constexpr auto d = static_cast <const B *> (&a); // { dg-error "'const
A' operand is not a base class subobject of a 'const B' object" }
+constexpr auto e = static_cast <const A *> (&a);
+constexpr auto f = static_cast <const A *> (&b);
+constexpr auto g = static_cast <const B *> (&b);
+constexpr auto h = static_cast <const B *> (f);
+constexpr auto i = static_cast <const A *> (nullptr);
+constexpr auto j = static_cast <const B *> (nullptr);
+constexpr auto k = static_cast <const B *> (i);
+constexpr auto l = static_cast <const A *> (j);
+constexpr auto m = static_cast <const A *> (&c);
+constexpr auto n = static_cast <const B *> (m); // { dg-error
"'const A' operand \\\(of dynamic type 'const C'\\\) is not a base class
subobject of a 'const B' object" }
+constexpr auto o = static_cast <const C *> (m);
+constexpr F p = {};
+constexpr auto q = static_cast <const A *> (&p);
+constexpr auto r = static_cast <const F *> (q);
+constexpr auto s = static_cast <const D *> (r);
+constexpr auto t = static_cast <const F *> (s);
+constexpr auto u = static_cast <const E *> (t);
+constexpr auto v = static_cast <const F *> (u);
+constexpr J y = {};
+constexpr auto z = static_cast <const G *> (&y);
+constexpr auto aa = static_cast <const J *> (z);
+constexpr auto ab = static_cast <const H *> (aa);
+constexpr auto ac = static_cast <const J *> (ab);
+constexpr auto ad = static_cast <const I *> (ac);
+constexpr auto ae = static_cast <const J *> (ad);
+constexpr H af = {};
+constexpr auto ag = static_cast <const J *> (&af); // { dg-error "'const
H' operand is not a base class subobject of a 'const J' object" }
+constexpr F ah[1] = {};
+constexpr auto ai = static_cast <const A *> (&ah[0]);
+constexpr auto aj = static_cast <const F *> (ai);
+constexpr auto ak = static_cast <const D *> (aj);
+constexpr auto al = static_cast <const F *> (ak);
+constexpr auto am = static_cast <const E *> (al);
+constexpr auto an = static_cast <const F *> (am);
+constexpr K ao = {};
+constexpr auto ap = static_cast <const A *> (&ao.k);
+constexpr auto aq = static_cast <const F *> (ap);
+constexpr auto ar = static_cast <const D *> (aq);
+constexpr auto as = static_cast <const F *> (ar);
+constexpr auto at = static_cast <const E *> (as);
+constexpr auto au = static_cast <const F *> (at);
+constexpr auto av = static_cast <const G *> (&ao.l[0]);
+constexpr auto aw = static_cast <const J *> (av);
+constexpr auto ax = static_cast <const H *> (aw);
+constexpr auto ay = static_cast <const J *> (ax);
+constexpr auto az = static_cast <const I *> (ay);
+constexpr auto ba = static_cast <const J *> (az);
+constexpr auto bb = static_cast <const G *> (&ao.l[1]);
+constexpr auto bc = static_cast <const J *> (bb);
+constexpr auto bd = static_cast <const H *> (bc);
+constexpr auto be = static_cast <const J *> (bd);
+constexpr auto bf = static_cast <const I *> (be);
+constexpr auto bg = static_cast <const J *> (bf);
+constexpr N bh = {};
+constexpr const G *bi = static_cast <const M *> (&bh);
+constexpr auto bj = static_cast <const L *> (bi); // { dg-error "'const
G' operand is not a base class subobject of a 'const L' object" }
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-static-cast2.C 2026-09-11
14:12:11.820269858 +0200
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-static-cast2.C 2026-09-11
14:12:11.820269858 +0200
@@ -0,0 +1,72 @@
+// { dg-do compile { target c++11 } }
+
+struct A { int a; };
+struct B : A { int b; };
+struct C : A { int c; };
+struct D { int d; };
+struct E { int e; };
+struct F : A, D, E { int f; };
+struct G {};
+struct H {};
+struct I {};
+struct J : G, H, I {};
+struct K { F k; J l[2]; };
+struct L : G {};
+struct M : G {};
+struct N : L, M {};
+constexpr A a = {};
+constexpr B b = {};
+constexpr C c = {};
+constexpr auto &d = static_cast <const B &> (a); // { dg-error "'const
A' operand is not a base class subobject of a 'const B' object" }
+constexpr auto &e = static_cast <const A &> (a);
+constexpr auto &f = static_cast <const A &> (b);
+constexpr auto &g = static_cast <const B &> (b);
+constexpr auto &h = static_cast <const B &> (f);
+constexpr auto &m = static_cast <const A &> (c);
+constexpr auto &n = static_cast <const B &> (m); // { dg-error "'const
A' operand \\\(of dynamic type 'const C'\\\) is not a base class subobject of a
'const B' object" }
+constexpr auto &o = static_cast <const C &> (m);
+constexpr F p = {};
+constexpr auto &q = static_cast <const A &> (p);
+constexpr auto &r = static_cast <const F &> (q);
+constexpr auto &s = static_cast <const D &> (r);
+constexpr auto &t = static_cast <const F &> (s);
+constexpr auto &u = static_cast <const E &> (t);
+constexpr auto &v = static_cast <const F &> (u);
+constexpr J y = {};
+constexpr auto &z = static_cast <const G &> (y);
+constexpr auto &aa = static_cast <const J &> (z);
+constexpr auto &ab = static_cast <const H &> (aa);
+constexpr auto &ac = static_cast <const J &> (ab);
+constexpr auto &ad = static_cast <const I &> (ac);
+constexpr auto &ae = static_cast <const J &> (ad);
+constexpr H af = {};
+constexpr auto &ag = static_cast <const J &> (af); // { dg-error "'const
H' operand is not a base class subobject of a 'const J' object" }
+constexpr F ah[1] = {};
+constexpr auto &ai = static_cast <const A &> (ah[0]);
+constexpr auto &aj = static_cast <const F &> (ai);
+constexpr auto &ak = static_cast <const D &> (aj);
+constexpr auto &al = static_cast <const F &> (ak);
+constexpr auto &am = static_cast <const E &> (al);
+constexpr auto &an = static_cast <const F &> (am);
+constexpr K ao = {};
+constexpr auto &ap = static_cast <const A &> (ao.k);
+constexpr auto &aq = static_cast <const F &> (ap);
+constexpr auto &ar = static_cast <const D &> (aq);
+constexpr auto &as = static_cast <const F &> (ar);
+constexpr auto &at = static_cast <const E &> (as);
+constexpr auto &au = static_cast <const F &> (at);
+constexpr auto &av = static_cast <const G &> (ao.l[0]);
+constexpr auto &aw = static_cast <const J &> (av);
+constexpr auto &ax = static_cast <const H &> (aw);
+constexpr auto &ay = static_cast <const J &> (ax);
+constexpr auto &az = static_cast <const I &> (ay);
+constexpr auto &ba = static_cast <const J &> (az);
+constexpr auto &bb = static_cast <const G &> (ao.l[1]);
+constexpr auto &bc = static_cast <const J &> (bb);
+constexpr auto &bd = static_cast <const H &> (bc);
+constexpr auto &be = static_cast <const J &> (bd);
+constexpr auto &bf = static_cast <const I &> (be);
+constexpr auto &bg = static_cast <const J &> (bf);
+constexpr N bh = {};
+constexpr const G &bi = static_cast <const M &> (bh);
+constexpr auto &bj = static_cast <const L &> (bi); // { dg-error "'const
G' operand is not a base class subobject of a 'const L' object" }
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast1.C 2026-09-11
14:12:11.820523894 +0200
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast1.C 2026-09-11
14:12:11.820523894 +0200
@@ -0,0 +1,91 @@
+// { dg-do compile { target c++14 } }
+
+struct A { int a; };
+struct B : A { int b; };
+struct C : A { int c; };
+struct D { int d = 42; };
+struct E { int e = 0; };
+struct F : E, D { };
+struct G : F { };
+struct H { G b; };
+
+constexpr bool
+foo (bool x)
+{
+ A a = {};
+ if (x)
+ {
+ auto c = static_cast <B *> (&a); // { dg-error "'A' operand is not a
base class subobject of a 'B' object" }
+ }
+ return true;
+}
+
+constexpr bool
+bar ()
+{
+ A a = {};
+ B b = {};
+ auto c = static_cast <A *> (&a);
+ auto d = static_cast <A *> (&b);
+ auto e = static_cast <B *> (&b);
+ auto f = static_cast <B *> (d);
+ auto g = static_cast <A *> (nullptr);
+ auto h = static_cast <B *> (nullptr);
+ auto i = static_cast <B *> (g);
+ auto j = static_cast <A *> (h);
+ return true;
+}
+
+constexpr bool
+baz (bool x)
+{
+ C a = {};
+ auto b = static_cast <A *> (&a);
+ if (x)
+ {
+ auto c = static_cast <B *> (b); // { dg-error "'A' operand \\\(of
dynamic type 'C'\\\) is not a base class subobject of a 'B' object" }
+ }
+ return true;
+}
+
+#if __cpp_constexpr_dynamic_alloc >= 201907
+constexpr bool
+qux ()
+{
+ A *a = new B {};
+ auto b = static_cast <B *> (a);
+ auto c = static_cast <C *> (a); // { dg-error "operand \\\(of dynamic
type 'B'\\\) is not a base class subobject of a 'C' object" "" { target c++20 }
}
+ delete a;
+ return true;
+}
+
+constexpr bool
+corge ()
+{
+ A *a = new B[2] {};
+ auto b = static_cast <B *> (a);
+ auto c = static_cast <C *> (a); // { dg-error "operand \\\(of dynamic
type 'B'\\\) is not a base class subobject of a 'C' object" "" { target c++20 }
}
+ delete[] a;
+ return true;
+}
+#endif
+
+constexpr int
+fred ()
+{
+ H h;
+ auto a = static_cast <D *> (&h.b);
+ auto b = static_cast <G *> (a);
+ return b->d;
+}
+
+static_assert (foo (false), "");
+constexpr bool a = foo (true);
+static_assert (bar (), "");
+static_assert (baz (false), "");
+constexpr bool b = baz (true);
+#if __cpp_constexpr_dynamic_alloc >= 201907
+constexpr bool c = qux ();
+constexpr bool d = corge ();
+#endif
+static_assert(fred () == 42, "");
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast2.C 2026-09-11
14:12:11.820639029 +0200
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast2.C 2026-09-11
14:12:11.820639029 +0200
@@ -0,0 +1,87 @@
+// { dg-do compile { target c++14 } }
+
+struct A { int a; };
+struct B : A { int b; };
+struct C : A { int c; };
+struct D { int d = 42; };
+struct E { int e = 0; };
+struct F : E, D { };
+struct G : F { };
+struct H { G b; };
+
+constexpr bool
+foo (bool x)
+{
+ A a = {};
+ if (x)
+ {
+ auto &c = static_cast <B &> (a); // { dg-error "'A' operand is not a
base class subobject of a 'B' object" }
+ }
+ return true;
+}
+
+constexpr bool
+bar ()
+{
+ A a = {};
+ B b = {};
+ auto &c = static_cast <A &> (a);
+ auto &d = static_cast <A &> (b);
+ auto &e = static_cast <B &> (b);
+ auto &f = static_cast <B &> (d);
+ return true;
+}
+
+constexpr bool
+baz (bool x)
+{
+ C a = {};
+ auto &b = static_cast <A &> (a);
+ if (x)
+ {
+ auto &c = static_cast <B &> (b); // { dg-error "'A' operand \\\(of
dynamic type 'C'\\\) is not a base class subobject of a 'B' object" }
+ }
+ return true;
+}
+
+#if __cpp_constexpr_dynamic_alloc >= 201907
+constexpr bool
+qux ()
+{
+ A *a = new B {};
+ auto &b = static_cast <B &> (*a);
+ auto &c = static_cast <C &> (*a); // { dg-error "'A' operand \\\(of
dynamic type 'B'\\\) is not a base class subobject of a 'C' object" "" { target
c++20 } }
+ delete a;
+ return true;
+}
+
+constexpr bool
+corge ()
+{
+ A *a = new B[2] {};
+ auto &b = static_cast <B &> (*a);
+ auto &c = static_cast <C &> (*a); // { dg-error "'A' operand \\\(of
dynamic type 'B'\\\) is not a base class subobject of a 'C' object" "" { target
c++20 } }
+ delete[] a;
+ return true;
+}
+#endif
+
+constexpr int
+fred ()
+{
+ H h;
+ auto &a = static_cast <D &> (h.b);
+ auto &b = static_cast <G &> (a);
+ return b.d;
+}
+
+static_assert (foo (false), "");
+constexpr bool a = foo (true);
+static_assert (bar (), "");
+static_assert (baz (false), "");
+constexpr bool b = baz (true);
+#if __cpp_constexpr_dynamic_alloc >= 201907
+constexpr bool c = qux ();
+constexpr bool d = corge ();
+#endif
+static_assert(fred () == 42, "");
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast3.C 2026-09-11
14:12:11.820762941 +0200
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast3.C 2026-09-11
14:12:11.820762941 +0200
@@ -0,0 +1,31 @@
+// { dg-do compile { target c++14 } }
+
+template <typename T>
+struct A
+{
+ unsigned char a = 0;
+ constexpr T &
+ foo (const unsigned char &x)
+ {
+ a = x;
+ return *static_cast <T *> (this);
+ }
+};
+
+template <typename T>
+struct B
+{
+ unsigned char b = 0;
+ constexpr T &
+ bar (const unsigned char &x)
+ {
+ b = x;
+ return *static_cast <T *> (this); // { dg-error "'B<D>' operand \\\(of
dynamic type 'C<D>'\\\) is not a base class subobject of a 'D' object" }
+ }
+};
+
+template <typename T>
+struct C : A <C <T>>, B <T> { };
+struct D : B <D> { };
+
+constexpr auto c = C <D> ().foo (100).bar (10);
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast4.C 2026-09-11
14:12:11.820868676 +0200
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast4.C 2026-09-11
14:12:11.820868676 +0200
@@ -0,0 +1,37 @@
+// { dg-do compile { target c++14 } }
+
+struct A {};
+struct B : A {};
+struct C { int c; };
+struct D { int d; };
+struct E : C, D { int e; };
+
+constexpr bool
+foo (bool x)
+{
+ struct A a = {};
+ A *b = &a;
+ A *c = nullptr;
+ if (x)
+ {
+ c = static_cast <A *> (static_cast <B *> (static_cast <A *> (static_cast
<B *> (b)))); // { dg-error "'A' operand is not a base class subobject of a
'B' object" }
+ }
+ return true;
+}
+
+constexpr bool a = foo (true);
+
+constexpr bool
+bar (bool x)
+{
+ struct D a = { 42 };
+ D *b = &a;
+ D *c = nullptr;
+ if (x)
+ {
+ c = static_cast <D *> (static_cast <E *> (static_cast <D *> (static_cast
<E *> (b)))); // { dg-error "'D' operand is not a base class subobject of a
'E' object" }
+ }
+ return true;
+}
+
+constexpr bool b = bar (true);
Jakub