# BST-TIMEOUT: 180 . "$BST_LIB" # function2 4.2.4+git (SRCREV a354bd09) -- an improved drop-in replacement for # std::function: move-only, multi-signature and small-buffer callable wrappers. # Header-only: the port ships ONE header at /usr/include/function2/function2.hpp, # three generated cmake files under /usr/lib/cmake/function2, and the Readme + # licence upstream misinstalls to a bare ${prefix} and the recipe relocates to # /usr/share/function2. No archive, no shared object, no CLI, and no .pc # (upstream ships no pkg-config template -- asserted as an absence below rather # than skipped). # # WHY THERE IS A PROBE. A header-only port ships bytes NO COMPILER ON THIS ARM # NEED EVER HAVE LOOKED AT, so "the files are present" is compatible with a # header set that does not compile. For function2 it is compatible with # something worse than a crash: a wrapper that heap-allocates where it # advertised a small buffer, one that copies a move-only callable instead of # moving it, one whose multi-signature dispatch collapsed to a single arm, one # whose function_view owns what it was meant to view. Each of those returns a # PLAUSIBLE answer. The recipe builds function2-probe from ${S}/include during # the port, making do_compile the port's first consumer, and the probe RUNS # here. Sections 1-3 assert the PAYLOAD; sections 4-9 assert the LIBRARY. # # THE FIRST HEADLINE FINDING IS THAT fu2::bad_function_call DOES NOT EXIST ON # THIS ARM, and section 2 establishes it from the SHIPPED HEADER while section 5 # establishes it from the compiler -- two witnesses, neither derived from the # other. Upstream's __clang__ branch defines FU2_HAS_DISABLED_EXCEPTIONS from # the compiler's own state (nothing is passed to make it), that macro then # suppresses `#include `, and BOTH definitions of bad_function_call # sit inside `#if !defined(FU2_HAS_DISABLED_EXCEPTIONS)`. So an empty call # cannot throw, and it does not merely fail to throw: throw_or_abort forwards to # the non-throwing overload and calls std::abort(). Section 9 drives that in # three separate processes and MEASURES it. # # THE SECOND IS THAT THE WRAPPER IS 48 BYTES HERE, NOT THE 32 UPSTREAM BUILDS # FOR. `using object_size = std::integral_constant` and # capacity_default = capacity_fixed exist # precisely to fix the wrapper at 32 bytes on every arm. On wasm32 the # arithmetic is defeated by alignment: the requested capacity is 32 - 2*4 = 24, # the alignment is alignof(std::max_align_t) = 16, and aligned_storage_t<24,16> # rounds UP to 32 -- so the inline buffer is 32 bytes rather than the 24 asked # for, and fu2::function lands at 48 bytes, 1.5x its documented size. On x86-64 # nothing rounds (16 is already a multiple of 16) and the invariant holds, which # is why this is invisible on a developer's own box. Sections 6 and 8 pin both # halves -- the size and the resulting allocation threshold -- because the # threshold is what a consumer feels: a capture set of 25..32 bytes is stored # inline HERE and heap-boxed on x86-64. # # Neither finding is a brintOS defect and neither is a workaround: both are # upstream's own arithmetic and upstream's own #ifdefs meeting this target's # pointer width. Nothing is filed for them; the function2 entry in # docs/brintos-test-plan.md carries them as measured facts. That entry is cited # by SUBJECT and not by number here and in every shipped file, because entry # numbers are assigned at merge time and ports land in parallel. inc=/usr/include/function2/function2.hpp cmk=/usr/lib/cmake/function2 doc=/usr/share/function2 payload=/usr/libexec/function2-tests/function2-probe # --------------------------------------------------------------------------- # 0. The decoy that absorbs the lost line. # # A suite's FIRST BST result line does not reach the host: the kernel prints its # benign `[k] wasm32: the region holds pending signal(s) the kernel does not ...` # audit line (BRINTOS-783) immediately after ==BST-BEGIN==, and that displaces # the result beside it -- BRINTOS-1149 for the host-side parser, # brintos/linux#96 and linux-in-the-browser#138 for the console loss itself. # # The harness computes its pass count as total-minus-failures, so a dropped line # is counted as a PASS. That is why the eaten position must never carry an # assertion anyone depends on: it would be reported green without ever having # been read. Measured on this port before the decoy existed -- the eaten line was # function2/header-present, a real assertion -- and confirmed against an # unrelated suite in the same session: 76-span-lite.test, which predates this # convention, loses its own first case span-lite/inc-dir the same way. # # The guest's own tally is unaffected and is what settles a run # (`==BST-SUITE function2 ... cases=N==` plus ==BST-END==). Remove this case when # BRINTOS-1149 is fixed. bst_ok function2/suite-canary # --------------------------------------------------------------------------- # 1. The payload. What the port actually put on the image. check function2/header-present test -f "$inc" check function2/cmake-config test -f "$cmk/function2Config.cmake" check function2/cmake-configversion test -f "$cmk/function2ConfigVersion.cmake" check function2/cmake-targets test -f "$cmk/function2Targets.cmake" # ${PN} IS NOT EMPTY HERE, which is what separates this port from every sibling # in its round. libeigen, glm, span-lite and microsoft-gsl install nothing but # headers and cmake files -- both routed to -dev -- so each had to reason about # an empty ${PN}. function2's own do_install:append relocates the Readme and # licence into ${datadir}/function2, which FILES:${PN} claims, so `function2` is # a real package with two files in it. check function2/doc-readme test -f "$doc/Readme.md" check function2/doc-licence test -f "$doc/LICENSE.txt" # Upstream installs these to a BARE ${prefix}; the recipe moves them. If that # move ever stops happening they land in /usr and this goes red. check function2/no-stray-prefix-readme test ! -e /usr/Readme.md check function2/no-stray-prefix-licence test ! -e /usr/LICENSE.txt # The single header is the whole library -- there is no second one to find. n_hdr=$(find /usr/include/function2 -type f 2>/dev/null | wc -l) check_eq function2/header-count 1 "$(printf '%s' "$n_hdr" | tr -d ' ')" # Absences asserted rather than skipped: upstream ships no pkg-config template # and nothing to link against, so a consumer must use cmake or a bare -I. check function2/no-pkgconfig test ! -e /usr/lib/pkgconfig/function2.pc check function2/no-archive test ! -e /usr/lib/libfunction2.a check function2/no-solib sh -c 'ls /usr/lib/libfunction2.so* >/dev/null 2>&1 && exit 1; exit 0' # The probe SHIPPED. `test -f`, never `test -x`: do_install stages data payloads # 0644 and the rootfs is read-only, so an -x assertion would pass only against a # hand-placed copy and fail against every image that ever built the package. check function2/probe-present test -f "$payload" # --------------------------------------------------------------------------- # 2. The exceptions chain, read out of the SHIPPED HEADER. # # This is the independent half of the headline finding: section 5 asks the # compiler what it did, this asks the bytes what they say. The awk below is a # small preprocessor-guard tracker -- it counts how many DECLARATIONS of # bad_function_call (the struct, the `using std::` alias, the public # `using detail::...` re-export) sit inside a # `#if !defined(FU2_HAS_DISABLED_EXCEPTIONS)` region. All three must, because # that is exactly what makes the name absent when the macro is set. # # It is not a vacuous check: run against a copy of this header with the # outermost guard removed it reports 2/3, and with both removed 0/3. guarded=$(awk ' /^[ \t]*#[ \t]*if[ \t]+!defined\(FU2_HAS_DISABLED_EXCEPTIONS\)/ { d++; g[d]=1; next } /^[ \t]*#[ \t]*(if|ifdef|ifndef)/ { d++; g[d]=0; next } /^[ \t]*#[ \t]*endif/ { g[d]=0; if (d>0) d--; next } /^[ \t]*(struct|using)[ \t].*bad_function_call/ { t++; ing=0; for (i=1;i<=d;i++) if (g[i]) ing=1; if (ing) n++ } END { printf "%d/%d\n", n+0, t+0 }' "$inc" 2>/dev/null) check_eq function2/bad-function-call-all-guarded "3/3" "$guarded" # The auto-detection itself: the __clang__ branch that fires with no help from # the recipe. Its presence is what makes P-FU2-WITH-DISABLED-EXCEPTIONS 0 # meaningful in section 5. check_out function2/hdr-clang-autodetect \ '__EXCEPTIONS && __has_feature(cxx_exceptions)' cat "$inc" # ...and the step that drops on the floor. check_out function2/hdr-no-functional-header \ '#define FU2_HAS_NO_FUNCTIONAL_HEADER' cat "$inc" # ...and the abort forward that section 9 measures. check_out function2/hdr-throw-or-abort-forwards \ 'throw_or_abort(std::false_type{});' cat "$inc" # The size arithmetic section 6 pins, quoted from the shipped source so the two # cannot drift apart. check_out function2/hdr-object-size-32 \ 'using object_size = std::integral_constant;' cat "$inc" # The diagnostic that makes the function<-unique conversion UNASKABLE: fu2 # rejects it with a static_assert inside the constructor's own SFINAE chain # rather than by removing the overload, so std::is_constructible<> on that pair # does not evaluate to false -- instantiating the trait fails the translation # unit. A consumer guarding the conversion with `if constexpr` on that trait # therefore does not compile, on this arm or any other. check_out function2/hdr-noncopyable-static-assert \ "Can't wrap a non copyable object into a unique function!" cat "$inc" # --------------------------------------------------------------------------- # 3. The version a consumer sees, which is a THIRD number. # # The package is 4.2.4+git at SRCREV a354bd09 (`git describe --tags` is exactly # 4.2.4), the porting ticket named 4.2.5, and CMakeLists.txt:2 still reads # `project(function2 VERSION 4.2.0 ...)` because upstream has never bumped it. # write_basic_package_version_file emits that third number, so # `find_package(function2 4.2.4 REQUIRED)` FAILS against a correctly installed # 4.2.4. COMPATIBILITY is SameMajorVersion, so any 4.x request up to 4.2.0 # succeeds. Upstream's, not ours; pinned so it is a fact a consumer can plan # around rather than a surprise at their configure step. pkgver=$(awk -F'"' '/^set\(PACKAGE_VERSION /{print $2; exit}' "$cmk/function2ConfigVersion.cmake" 2>/dev/null) check_eq function2/cmake-version-is-4.2.0 "4.2.0" "$pkgver" # The compatibility MODE, which is what decides how bad the gap above is. The # generated file never spells "SameMajorVersion", so it is identified by its # template's own machinery: SameMajorVersion and ExactVersion both branch on # CVF_VERSION_MAJOR, and only SameMinorVersion adds a CVF_VERSION_MINOR. Its # absence with MAJOR present is the SameMajorVersion template -- so a 4.x # request up to 4.2.0 succeeds and anything above it, 4.2.4 included, does not. check_out function2/cmake-compat-major-template \ 'CVF_VERSION_MAJOR' cat "$cmk/function2ConfigVersion.cmake" n_minor=$(grep -c CVF_VERSION_MINOR "$cmk/function2ConfigVersion.cmake" 2>/dev/null) check_eq function2/cmake-compat-not-same-minor 0 "$(printf '%s' "${n_minor:-0}" | tr -d ' ')" cfgver=$(awk -F'[ )]' '/^set\(function2_VERSION /{print $2; exit}' "$cmk/function2Config.cmake" 2>/dev/null) check_eq function2/config-version-is-4.2.0 "4.2.0" "$cfgver" # --------------------------------------------------------------------------- # 4. Stage and run the probe. # # Copied to /tmp and chmod +x there: the rootfs is read-only at runtime and # do_install stages data payloads 0644, so it cannot be executed where it sits. # /tmp is a mode=1777 tmpfs with no noexec. tmp=${TMPDIR:-/tmp} probe=$tmp/bst-function2-probe.$$ if cp "$payload" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then bst_ok function2/probe-stage else bst_notok function2/probe-stage "cannot stage $payload" return 2>/dev/null || exit 0 fi # A ceiling on the staged size. The guest stages an exec image as a segment list # capped at WASM32_IMG_MAX_SEGS=1024, so on a fragmented heap a large image is # refused -ENOMEM before it runs (nopoll measured exactly that at 41 MB). # Measured at ~4.5 MB on 2026-08-22; 12 MB leaves real headroom while still # catching a growth that would put the probe near the cap. psz=$(wc -c < "$payload" 2>/dev/null | tr -d ' ') check function2/probe-size-under-12m test "$psz" -lt 12582912 out=$("$probe" 2>&1); rc=$? # Every value below is read from this one run. `p ` pulls the value; a tag # the probe never printed yields the empty string, which fails its check_eq # rather than silently passing. p() { printf '%s\n' "$out" | awk -v k="P-$1" '$1==k{print $2; exit}'; } check_eq function2/probe-rc 0 "$rc" check_eq function2/probe-result OK "$(p RESULT)" check_eq function2/probe-failures 0 "$(p FAILURES)" # Bounded from BELOW, not pinned: the exact count moves the day a case is added # to the probe, and a summary that goes false on an improvement is worse than no # summary. 45 as of 2026-08-22. nchk=$(p CHECKS) check function2/probe-checks-not-less-than-45 test "${nchk:-0}" -ge 45 # --------------------------------------------------------------------------- # 5. Compile-time state. The tripwire: everything measured below was measured # under exactly this, so these come before any value is read. check_eq function2/cxxstd 201703 "$(p CXXSTD)" check_eq function2/cxx-exceptions-off 0 "$(p CXX-EXCEPTIONS)" # The finding, from the compiler's side. The second line is what makes it # upstream's behaviour rather than this recipe's configuration: the switch that # would have FORCED it was not passed. check_eq function2/fu2-disabled-exceptions 1 "$(p FU2-DISABLED-EXCEPTIONS)" check_eq function2/fu2-not-told-to-disable 0 "$(p FU2-WITH-DISABLED-EXCEPTIONS)" check_eq function2/fu2-no-functional-header 1 "$(p FU2-NO-FUNCTIONAL-HEADER)" check_eq function2/fu2-no-bad-function-call 0 "$(p FU2-HAS-BAD-FUNCTION-CALL)" # C++17 makes `int(int) noexcept` a distinct signature fu2 expands its own # qualifier arm for; at C++14 the noexcept case in section 7 would not compile. check_eq function2/fu2-cxx17-noexcept-fn-type 1 "$(p FU2-CXX17-NOEXCEPT-FN-TYPE)" check_eq function2/ptr-bits 32 "$(p PTR-BITS)" check_eq function2/abi-namespace-is-400 1 "$(p FU2-ABI-NS)" check_eq function2/include-guard 1 "$(p FU2-INCLUDE-GUARD)" # --------------------------------------------------------------------------- # 6. The ABI facts -- the second headline finding, as numbers. # # declared cap align sizeof(function) sizeof(view) capacity_none # wasm32 24 16 48 8 12 # x86-64 16 16 32 16 24 # # 48 is the one to read: upstream's object_size = 32 exists to make this exactly # 32 on every arm, and here it is not, because aligned_storage_t<24,16> rounds # the 24 up to 32. A consumer budgeting an array of these on this target gets # half again as much as the documentation promises. check_eq function2/capacity-declared 24 "$(p CAPACITY-DEFAULT)" check_eq function2/capacity-alignment 16 "$(p CAPACITY-ALIGN)" check_eq function2/sizeof-function 48 "$(p SIZEOF-FUNCTION)" check_eq function2/sizeof-unique 48 "$(p SIZEOF-UNIQUE)" # A view is two words and owns nothing -- the one wrapper that is SMALLER here # than on x86-64, because it is pure pointers and nothing rounds. check_eq function2/sizeof-view 8 "$(p SIZEOF-VIEW)" # capacity_none: 3 * sizeof(void*), exactly as upstream documents. check_eq function2/sizeof-capacity-none 12 "$(p SIZEOF-CAPNONE)" check_eq function2/capacity-can-hold-3d 24 "$(p CAPACITY-CAN-HOLD)" # The type-level half of move-only. A unique_function that was quietly copyable # would pass every runtime case in section 7. check_eq function2/function-is-copyable 1 "$(p FUNCTION-COPYABLE)" check_eq function2/unique-is-not-copyable 0 "$(p UNIQUE-COPYABLE)" check_eq function2/unique-is-movable 1 "$(p UNIQUE-MOVABLE)" check_eq function2/view-is-copyable 1 "$(p VIEW-COPYABLE)" check_eq function2/function-converts-to-unique 1 "$(p FUNCTION-TO-UNIQUE)" check_eq function2/reverse-is-a-static-assert 1 "$(p UNIQUE-TO-FUNCTION-IS-STATIC-ASSERT)" # --------------------------------------------------------------------------- # 7. Behaviour. The ticket's three assignment cases, the move-only path, and the # signature shapes std::function has no equivalent of. check_eq function2/assign-lambda 42 "$(p ASSIGN-LAMBDA)" check_eq function2/assign-lambda-bool 1 "$(p ASSIGN-LAMBDA-BOOL)" check_eq function2/assign-fnptr 1007 "$(p ASSIGN-FNPTR)" # fu2 carries its own invoke handling Type T::* by reference AND by pointer, so # a pointer-to-member is assignable directly; the object arrives as the first # argument. A pointer to a scalar member is a third overload needing no call. check_eq function2/assign-memfn 8 "$(p ASSIGN-MEMFN)" check_eq function2/assign-memfn-ptr 8 "$(p ASSIGN-MEMFN-PTR)" check_eq function2/assign-memvar 5 "$(p ASSIGN-MEMVAR)" check_eq function2/assign-functor 11 "$(p ASSIGN-FUNCTOR)" check_eq function2/reassign-replaces 0 "$(p REASSIGN)" check_eq function2/empty-is-false 0 "$(p EMPTY-BOOL)" check_eq function2/nullptr-clears 0 "$(p CLEARED-BOOL)" # A copy must be INDEPENDENT of its source: clearing one must not clear the # other. A wrapper that shared the erased object would pass every read. check_eq function2/copy-independent 2 "$(p COPY-INDEPENDENT)" # A moved-from wrapper must be empty. Leaving it callable is the classic # move-semantics defect and it is silent. check_eq function2/moved-from-empty 0 "$(p MOVED-FROM-EMPTY)" # The move-only path -- the whole reason this package exists beside # std::function, which cannot hold a non-copyable callable at all. check_eq function2/callable-is-move-only 0 "$(p MOVEONLY-COPYABLE)" check_eq function2/unique-call 42 "$(p UNIQUE-CALL)" check_eq function2/unique-move-empties-src 0 "$(p UNIQUE-MOVED-SRC)" check_eq function2/unique-move-carries 42 "$(p UNIQUE-MOVED-DST)" check_eq function2/unique-move-assign 42 "$(p UNIQUE-MOVE-ASSIGN)" check_eq function2/unique-takes-plain 12 "$(p UNIQUE-PLAIN)" check_eq function2/function-into-unique 10 "$(p FUNCTION-INTO-UNIQUE)" # The owned unique_ptr must actually be released when the wrapper dies; a type # erasure that forgot the destructor leaks silently and forever. ufree=$(p UNIQUE-DTOR-FREES) check function2/unique-dtor-frees test "${ufree:-0}" -ge 1 check_eq function2/void-return 5 "$(p VOID-RETURN)" # A by-reference argument must arrive as a reference; if the wrapper copied it # the mutation would be invisible and the call would still "work". check_eq function2/byref-argument 11 "$(p BYREF)" check_eq function2/mutable-state 123 "$(p MUTABLE)" # Multi-signature: one wrapper, two call operators, chosen by argument type. # A dispatcher that collapsed to one arm answers plausibly for whichever it kept. check_eq function2/multisig-int 42 "$(p MULTISIG-INT)" check_eq function2/multisig-string 5 "$(p MULTISIG-STR)" check_eq function2/overload-int 2 "$(p OVERLOAD-INT)" check_eq function2/overload-str -1 "$(p OVERLOAD-STR)" check_eq function2/const-qualified 4 "$(p CONST-QUALIFIED)" check_eq function2/noexcept-signature 3 "$(p NOEXCEPT-SIG)" check_eq function2/noexcept-call-is-noexcept 1 "$(p NOEXCEPT-CALL-IS-NOEXCEPT)" # --------------------------------------------------------------------------- # 8. THE NON-ALLOCATING CLAIM, MEASURED. The probe replaces global operator # new/delete and sweeps for the smallest callable that gets boxed. # # 33 here against 17 on x86-64, and that gap is the whole practical consequence # of section 6's rounding: a capture set of 25..32 bytes -- four to eight # pointers' worth on this arm -- is stored INLINE here and heap-boxed on the # developer's host. The declared capacity is 24, so the effective buffer is # eight bytes larger than the number upstream's own constant asks for. check_eq function2/alloc-tiny-none 0 "$(p ALLOC-TINY)" check_eq function2/alloc-threshold-33 33 "$(p ALLOC-THRESHOLD)" check_eq function2/alloc-capacity-declared 24 "$(p CAPACITY-DECLARED)" check_eq function2/alloc-below-threshold 0 "$(p ALLOC-BELOW-THRESHOLD)" abig=$(p ALLOC-BIG) check function2/alloc-big-boxes test "${abig:-0}" -ge 1 # capacity_none: upstream's comment says the buffer is "removed" and that the # wrapper "will then allocate memory for every object it applies a type erasure # on". THAT IS NOT WHAT IT DOES. capacity_fixed<0> selects the specialization # for `Capacity::capacity < sizeof(void*)`, whose storage is a bare # data_accessor -- so the effective buffer is ONE POINTER, and a callable that # fits in one is still stored inline. 5 here (4-byte pointers), 9 on x86-64. # Measured on both arms; upstream's, not ours, and not a brintOS defect. check_eq function2/alloc-capnone-threshold-5 5 "$(p ALLOC-CAPNONE-THRESHOLD)" check_eq function2/alloc-capnone-smaller 1 "$(p ALLOC-CAPNONE-SMALLER)" # A function pointer is one word and must never box. check_eq function2/alloc-fnptr-none 0 "$(p ALLOC-FNPTR)" # A COPY of a boxed wrapper must allocate its own box -- otherwise the two share # and clearing one clears the other, which section 7 asserts they do not. acp=$(p ALLOC-COPY-BOXED) check function2/alloc-copy-boxed test "${acp:-0}" -ge 1 # ...while a MOVE must transfer the box, not re-box it. check_eq function2/alloc-move-boxed-none 0 "$(p ALLOC-MOVE-BOXED)" # What a boxed lifetime allocated, it frees. check_eq function2/alloc-balanced "1/1" "$(p ALLOC-BALANCED)" # function_view's defect is the opposite of everything above: it must NOT take a # copy. A view that owned would work in every case where the referent outlives # it and fail only where it does not. check_eq function2/view-call 12 "$(p VIEW-CALL)" check_eq function2/view-aliases-referent 2 "$(p VIEW-ALIASES)" check_eq function2/view-never-allocates 0 "$(p VIEW-ALLOC-HEAVY)" check_eq function2/view-empty-is-false 0 "$(p VIEW-EMPTY-BOOL)" # --------------------------------------------------------------------------- # 9. The empty-call arms. Each is its own process because each one DIES. # # An empty fu2::function call reaches throw_or_abort(std::true_type), which # under FU2_HAS_DISABLED_EXCEPTIONS forwards to the non-throwing overload and # calls std::abort(). There is no throwing path to select on this arm and no # exception type to catch it with, so a violation cannot be a case inside a run # that has cases after it. # # THE CONTROL RUNS FIRST AND IS WHAT MAKES THE THREE DEATH ARMS NON-VACUOUS. It # drives the identical call path with a NON-empty wrapper and must print # P-EMPTY-SURVIVED: a "did not survive" assertion against a binary that cannot # reach the survivor line for some unrelated reason would pass while testing # nothing. Measured 2026-08-22: the control exits 0 printing # `P-EMPTY-SURVIVED 1001`, and all three death arms exit non-zero on SIGABRT # having printed their PRE line and nothing after it. cout=$("$probe" empty-control 2>&1); crc=$? check_eq function2/empty-control-rc 0 "$crc" case "$cout" in *"P-EMPTY-SURVIVED 1001"*) bst_ok function2/empty-control-survives ;; *) bst_notok function2/empty-control-survives "$(_bst_1line "$cout")" ;; esac # The three arms. `nothrow` is function_base<...,IsThrowing=false,...>: on an arm # WITH exceptions it aborts while fu2::function throws, so running it here shows # the two have CONVERGED rather than leaving that inferred from the source. # # The assertions are the arch-independent properties -- died, and did not reach # the survivor line -- plus the fact that all three reach the SAME end. The # status number itself is recorded in the design doc and the test-plan entry # rather than pinned here: it is a property of how this guest reports a signal, # not of the port, and pinning it would make this suite fail on a runtime change # that has nothing to do with function2. s_function=""; s_unique=""; s_nothrow="" for mode in function unique nothrow; do aout=$("$probe" "empty-$mode" 2>&1); arc=$? case "$mode" in function) s_function=$arc ;; unique) s_unique=$arc ;; nothrow) s_nothrow=$arc ;; esac if [ "$arc" -ne 0 ]; then bst_ok "function2/empty-$mode-dies" else bst_notok "function2/empty-$mode-dies" "rc=$arc $(_bst_1line "$aout")" fi case "$aout" in *P-EMPTY-SURVIVED*) bst_notok "function2/empty-$mode-no-survivor" \ "reached the survivor line: $(_bst_1line "$aout")" ;; *) bst_ok "function2/empty-$mode-no-survivor" ;; esac # It got as far as the call. Without this, a binary that died at start-up # would satisfy both assertions above for entirely the wrong reason. case "$aout" in *"P-EMPTY-PRE bool=0"*) bst_ok "function2/empty-$mode-reached-call" ;; *) bst_notok "function2/empty-$mode-reached-call" "$(_bst_1line "$aout")" ;; esac done # All three ends agree. This is the assertion that says the throwing and # non-throwing configurations have collapsed onto one channel on this arm. if [ "$s_function" = "$s_unique" ] && [ "$s_function" = "$s_nothrow" ]; then bst_ok function2/empty-arms-agree else bst_notok function2/empty-arms-agree "function=$s_function unique=$s_unique nothrow=$s_nothrow" fi rm -f "$probe"