From 243d547552824dec779edff5a3ce9b11f994d90b Mon Sep 17 00:00:00 2001 From: cds-amal Date: Thu, 26 Feb 2026 07:28:07 -0500 Subject: [PATCH 1/6] Remove incorrect builtin_deref assertions from VTable/Static alloc collection The `collect_alloc` function asserted that VTable and Static allocations must have a builtin-deref pointer type. This is wrong: vtable pointers carry a raw `*const ()` type that doesn't pass `builtin_deref(true)`, and static allocations can have non-pointer types depending on the static's definition. Removing these assertions fixes 6 UI tests that were previously panicking during alloc collection (4 thin-box tests, const-trait-to-trait, issue-11205). --- src/printer.rs | 10 ---------- tests/ui/failing.tsv | 6 ------ tests/ui/passing.tsv | 6 ++++++ 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/printer.rs b/src/printer.rs index 9f74261..01a2bcb 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -972,21 +972,11 @@ fn collect_alloc( } } GlobalAlloc::Static(_) => { - assert!( - kind.clone().builtin_deref(true).is_some(), - "Allocated pointer is not a built-in pointer type: {:?}", - kind - ); val_collector .visited_allocs .insert(val, (ty, global_alloc.clone())); } GlobalAlloc::VTable(_, _) => { - assert!( - kind.clone().builtin_deref(true).is_some(), - "Allocated pointer is not a built-in pointer type: {:?}", - kind - ); val_collector .visited_allocs .insert(val, (ty, global_alloc.clone())); diff --git a/tests/ui/failing.tsv b/tests/ui/failing.tsv index 28da13f..e09990b 100644 --- a/tests/ui/failing.tsv +++ b/tests/ui/failing.tsv @@ -1,18 +1,12 @@ tests/ui/async-await/issue-60709.rs 101 tests/ui/async-await/issues/issue-59972.rs 101 -tests/ui/box/thin_align.rs 101 -tests/ui/box/thin_drop.rs 101 -tests/ui/box/thin_new.rs 101 -tests/ui/box/thin_zst.rs 101 tests/ui/cfg/cfg_attr.rs 101 tests/ui/cfg/cfgs-on-items.rs 101 tests/ui/consts/const-eval/const_fn_ptr.rs 101 tests/ui/consts/const-int-arithmetic-overflow.rs 101 -tests/ui/consts/const-trait-to-trait.rs 101 tests/ui/env-macro/env-env-overload.rs 101 tests/ui/env-macro/env-env.rs 101 tests/ui/fmt/fmt_debug/none.rs 101 -tests/ui/issues/issue-11205.rs 101 tests/ui/issues/issue-26641.rs 101 tests/ui/label/label_break_value_desugared_break.rs 101 tests/ui/macros/macro-meta-items.rs 101 diff --git a/tests/ui/passing.tsv b/tests/ui/passing.tsv index 8def20a..53cc969 100644 --- a/tests/ui/passing.tsv +++ b/tests/ui/passing.tsv @@ -350,6 +350,10 @@ tests/ui/box/into-boxed-slice.rs tests/ui/box/new-box-syntax.rs tests/ui/box/new-box.rs tests/ui/box/new.rs +tests/ui/box/thin_align.rs +tests/ui/box/thin_drop.rs +tests/ui/box/thin_new.rs +tests/ui/box/thin_zst.rs tests/ui/box/unit/expr-block-generic-unique1.rs tests/ui/box/unit/expr-block-generic-unique2.rs tests/ui/box/unit/expr-if-unique.rs @@ -678,6 +682,7 @@ tests/ui/consts/const-repeated-values.rs tests/ui/consts/const-size_of-align_of.rs tests/ui/consts/const-size_of_val-align_of_val.rs tests/ui/consts/const-struct.rs +tests/ui/consts/const-trait-to-trait.rs tests/ui/consts/const-tuple-struct.rs tests/ui/consts/const-typeid-of-rpass.rs tests/ui/consts/const-unit-struct.rs @@ -1249,6 +1254,7 @@ tests/ui/issues/issue-10802.rs tests/ui/issues/issue-10806.rs tests/ui/issues/issue-11047.rs tests/ui/issues/issue-11085.rs +tests/ui/issues/issue-11205.rs tests/ui/issues/issue-11267.rs tests/ui/issues/issue-11382.rs tests/ui/issues/issue-11552.rs From 72c0edcd29e6ced119e72b8359bde9705623b1d4 Mon Sep 17 00:00:00 2001 From: cds-amal Date: Thu, 26 Feb 2026 07:32:57 -0500 Subject: [PATCH 2/6] Rewrite UI test runner with flag extraction and arch filtering Rewrite run_ui_tests.sh: flag-based CLI (--verbose, --save-generated-output, --save-debug-output), build once then invoke binary directly (eliminates cargo freshness overhead per test), arch-aware skip logic for cross-platform test lists. Add extract_test_flags() to both runners to parse //@ compile-flags:, //@ edition:, and //@ rustc-env: directives from test files and pass them through to the compiler. Previously these directives were silently ignored, causing tests to fail for the wrong reasons or pass despite being misconfigured. Update Makefile test-ui target for the new CLI. --- .gitignore | 3 +- Makefile | 7 +- tests/ui/remake_ui_tests.sh | 40 ++++- tests/ui/run_ui_tests.sh | 287 ++++++++++++++++++++++++++++++------ 4 files changed, 282 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index 25e003d..8e0c979 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ tests/ui/failing/* -tests/ui/passing/* \ No newline at end of file +tests/ui/passing/* +tests/ui/debug/* \ No newline at end of file diff --git a/Makefile b/Makefile index 3d41d14..f649218 100644 --- a/Makefile +++ b/Makefile @@ -63,12 +63,7 @@ remake-ui-tests: test-ui: VERBOSE?=0 test-ui: - # Check if RUST_DIR_ROOT is set - if [ -z "$$RUST_DIR_ROOT" ]; then \ - echo "Error: RUST_DIR_ROOT is not set. Please set it to the absolute path to rust compiler checkout."; \ - exit 1; \ - fi - bash tests/ui/run_ui_tests.sh "$$RUST_DIR_ROOT" "${VERBOSE}" + bash tests/ui/run_ui_tests.sh $(if $(filter 1,$(VERBOSE)),--verbose) "$$RUST_DIR_ROOT" .PHONY: dot svg png d2 clean-graphs check-graphviz diff --git a/tests/ui/remake_ui_tests.sh b/tests/ui/remake_ui_tests.sh index 2943669..0ca685a 100755 --- a/tests/ui/remake_ui_tests.sh +++ b/tests/ui/remake_ui_tests.sh @@ -35,6 +35,42 @@ if [ -n "${KEEP_OUTPUT}" ]; then mkdir -p "$FAILING_DIR" "$PASSING_DIR" fi +# Extract //@ compile-flags: and //@ edition: directives from a test file. +# Prints space-separated flags to stdout. +extract_test_flags() { + local file="$1" + local flags="" + + # Extract //@ compile-flags: directives (everything after "compile-flags:") + local compile_flags + compile_flags=$(grep -s '^[[:space:]]*//@[[:space:]]*compile-flags:' "$file" \ + | sed 's/^.*compile-flags:[[:space:]]*//' || true) + if [ -n "$compile_flags" ]; then + flags="$compile_flags" + fi + + # Extract //@ edition: directive (e.g., "//@ edition: 2021") + local edition + edition=$(grep -s '^[[:space:]]*//@[[:space:]]*edition:' "$file" \ + | sed 's/^.*edition:[[:space:]]*//' | head -1 || true) + if [ -n "$edition" ]; then + flags="$flags --edition $edition" + fi + + # Extract //@ rustc-env: directives (e.g., "//@ rustc-env:MY_VAR=value") + # These set environment variables for the rustc process via --env-set. + local rustc_envs + rustc_envs=$(grep -s '^[[:space:]]*//@[[:space:]]*rustc-env:' "$file" \ + | sed 's/^.*rustc-env:[[:space:]]*//' || true) + if [ -n "$rustc_envs" ]; then + while IFS= read -r env_pair; do + flags="$flags --env-set $env_pair -Zunstable-options" + done <<< "$rustc_envs" + fi + + echo "$flags" +} + echo "Running UI tests..." while read -r test; do full_path="$RUST_DIR/$test" @@ -45,7 +81,9 @@ while read -r test; do fi echo "Running test: $test" - cargo run -- -Zno-codegen "$full_path" > tmp.stdout 2> tmp.stderr + test_flags=$(extract_test_flags "$full_path") + # shellcheck disable=SC2086 # intentional word-splitting of test_flags + cargo run -- -Zno-codegen ${test_flags} "$full_path" > tmp.stdout 2> tmp.stderr status=$? base_test="$(basename "$test")" json_file="${PWD}/$(basename "$test" .rs).smir.json" diff --git a/tests/ui/run_ui_tests.sh b/tests/ui/run_ui_tests.sh index 99f8f51..fbaf818 100755 --- a/tests/ui/run_ui_tests.sh +++ b/tests/ui/run_ui_tests.sh @@ -1,72 +1,265 @@ #!/usr/bin/env bash +set -euo pipefail -set -u +usage() { + cat <<'EOF' +Usage: run_ui_tests.sh [--verbose] [--save-generated-output] [--save-debug-output] RUST_DIR_ROOT -USAGE="Usage: $0 RUST_DIR_ROOT [VERBOSE]\n -'RUST_DIR_ROOT' is the Rust directory to take ui tests from. Optional 'VERBOSE' can be set to '1' for verbose output." +Options: + --verbose Print passing and skipped tests. + --save-generated-output Do not delete generated *.smir.json files. + --save-debug-output On failure, print stderr snippet inline and save + full stderr to /debug/.stderr. + --help, -h Show this help. -if [ $# -lt 1 ]; then - echo -e "$USAGE" - exit 1 -else - VERBOSE="$2" -fi +Environment: + RUN_SMIR Optional override for the runner command. + Example: RUN_SMIR=./target/debug/stable_mir_json + (If you include flags, they will be split on spaces.) +EOF +} + +die() { + printf 'Error: %s\n' "$*" >&2 + exit 1 +} + +log() { + printf '%s\n' "$*" +} + +# ------------------------- +# Extract test directives +# ------------------------- + +# Extract //@ compile-flags:, //@ edition:, and //@ rustc-env: directives +# from a test file. Prints space-separated flags to stdout. +extract_test_flags() { + local file="$1" + local flags="" + + # Extract //@ compile-flags: directives (everything after "compile-flags:") + local compile_flags + compile_flags=$(grep -s '^[[:space:]]*//@[[:space:]]*compile-flags:' "$file" \ + | sed 's/^.*compile-flags:[[:space:]]*//' || true) + if [ -n "$compile_flags" ]; then + flags="$compile_flags" + fi + + # Extract //@ edition: directive (e.g., "//@ edition: 2021") + local edition + edition=$(grep -s '^[[:space:]]*//@[[:space:]]*edition:' "$file" \ + | sed 's/^.*edition:[[:space:]]*//' | head -1 || true) + if [ -n "$edition" ]; then + flags="$flags --edition $edition" + fi + + # Extract //@ rustc-env: directives (e.g., "//@ rustc-env:MY_VAR=value") + # These set environment variables for the rustc process via --env-set. + local rustc_envs + rustc_envs=$(grep -s '^[[:space:]]*//@[[:space:]]*rustc-env:' "$file" \ + | sed 's/^.*rustc-env:[[:space:]]*//' || true) + if [ -n "$rustc_envs" ]; then + while IFS= read -r env_pair; do + flags="$flags --env-set $env_pair -Zunstable-options" + done <<< "$rustc_envs" + fi + + echo "$flags" +} + +# ------------------------- +# Arg parsing +# ------------------------- +VERBOSE=0 +SAVE_GENERATED_OUTPUT=0 +SAVE_DEBUG_OUTPUT=0 +RUST_DIR_ROOT="" + +while (( $# > 0 )); do + case "$1" in + --verbose) + VERBOSE=1 + shift + ;; + --save-generated-output) + SAVE_GENERATED_OUTPUT=1 + shift + ;; + --save-debug-output) + SAVE_DEBUG_OUTPUT=1 + shift + ;; + --help|-h) + usage + exit 0 + ;; + --*) + die "unknown option: $1" + ;; + *) + if [[ -z "$RUST_DIR_ROOT" ]]; then + RUST_DIR_ROOT=$1 + else + die "unexpected argument: $1" + fi + shift + ;; + esac +done + +[[ -n "$RUST_DIR_ROOT" ]] || { usage; exit 1; } +[[ -d "$RUST_DIR_ROOT" ]] || die "RUST_DIR_ROOT is not a directory: $RUST_DIR_ROOT" -RUST_DIR_ROOT="$1" -UI_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +# ------------------------- +# Paths +# ------------------------- +UI_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" PASSING_TSV="${UI_DIR}/passing.tsv" +[[ -f "$PASSING_TSV" ]] || die "Missing TSV file: $PASSING_TSV" -KEEP_FILES=${KEEP_FILES:-""} +DEBUG_DIR="${UI_DIR}/debug" +if (( SAVE_DEBUG_OUTPUT )); then + rm -rf "$DEBUG_DIR" + mkdir -p "$DEBUG_DIR" +fi + +# ------------------------- +# Runner setup +# ------------------------- +declare -a RUN_SMIR_CMD + +if [[ -z "${RUN_SMIR:-}" ]]; then + # Build once upfront, then invoke the binary directly to avoid cargo's + # per-invocation freshness check (~2-3s overhead * thousands of tests). + log "RUN_SMIR unset; building and using binary directly" + cargo build + + SMIR_BIN="./target/debug/stable_mir_json" + [[ -x "$SMIR_BIN" ]] || die "failed to build: $SMIR_BIN" -if [ -z "${RUN_SMIR:-""}" ]; then - echo "RUN_SMIR unset, using cargo to run the tests" - RUN_SMIR="cargo run -- -Zno-codegen" + SYSROOT_LIB="$(rustc --print sysroot)/lib" + DYLD_LIBRARY_PATH="${SYSROOT_LIB}${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}" + LD_LIBRARY_PATH="${SYSROOT_LIB}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" + export DYLD_LIBRARY_PATH LD_LIBRARY_PATH + + RUN_SMIR_CMD=( "$SMIR_BIN" -Zno-codegen ) +else + # Allow RUN_SMIR to be either a path or a simple "path flags..." string. + # Note: this splits on spaces (no shell quoting). + read -r -a RUN_SMIR_CMD <<<"$RUN_SMIR" + [[ -n "${RUN_SMIR_CMD[0]:-}" ]] || die "RUN_SMIR is set but empty" + [[ -x "${RUN_SMIR_CMD[0]}" ]] || die "RUN_SMIR binary is not executable: ${RUN_SMIR_CMD[0]}" + RUN_SMIR_CMD+=( -Zno-codegen ) fi -echo "Running regression tests for passing UI cases..." +# ------------------------- +# Arch filtering +# ------------------------- +HOST_ARCH="$(uname -m)" +case "$HOST_ARCH" in + arm64|aarch64) + SKIP_ARCH_RE='/(x86_64|x86|i686|i386|s390x|powerpc|mips|riscv|loongarch|sparc|hexagon|bpf|avr|msp430|nvptx)/' + HOST_ONLY_RE='^//@ (\[.*\])?only-(aarch64|arm)' + ;; + x86_64) + SKIP_ARCH_RE='/(aarch64|arm|s390x|powerpc|mips|riscv|loongarch|sparc|hexagon|bpf|avr|msp430|nvptx)/' + HOST_ONLY_RE='^//@ (\[.*\])?only-(x86_64|x86)' + ;; + *) + SKIP_ARCH_RE='^$' # no filtering on unknown arch + HOST_ONLY_RE='' # no directive filtering either + ;; +esac + +ANY_ARCH_ONLY_RE='^//@ (\[.*\])?only-(x86_64|x86|i686|aarch64|arm|s390x|riscv|mips|powerpc|loongarch|sparc)' + +log "Running regression tests for passing UI cases (host: $HOST_ARCH)..." + +start_time=$SECONDS failed=0 passed=0 +skipped=0 total=0 -while read -r test; do - test_path="${RUST_DIR_ROOT}/${test}" - test_name="$(basename "$test" .rs)" - json_file="${PWD}/${test_name}.smir.json" +while IFS= read -r test; do + [[ -n "$test" ]] || continue + + test_path="${RUST_DIR_ROOT}/${test}" + test_name="$(basename "$test" .rs)" + json_file="${PWD}/${test_name}.smir.json" - ${RUN_SMIR} "$test_path" > /dev/null 2>&1 - status=$? + (( ++total )) - total=$((total + 1)) + # Skip tests gated on a different architecture. + # Check 1: path contains a foreign-arch directory name. + # Check 2: file contains a rustc `//@ only-` directive for a different arch. + skip_test=0 + if grep -qE "$SKIP_ARCH_RE" <<<"$test"; then + skip_test=1 + elif [[ -f "$test_path" ]] && grep -qE "$ANY_ARCH_ONLY_RE" "$test_path"; then + if [[ -n "${HOST_ONLY_RE:-}" ]] && ! grep -qE "$HOST_ONLY_RE" "$test_path"; then + skip_test=1 + fi + fi - if [ $status -ne 0 ]; then - echo "❌ FAILED: $test_path (exit $status)" - failed=$((failed + 1)) - else - if [ "$VERBOSE" -eq "1" ]; then - echo "✅ PASSING: $test_path" - fi - passed=$((passed + 1)) + if (( skip_test )); then + (( ++skipped )) + if (( VERBOSE )); then + log "⏭️ SKIPPED (arch): $test_path" fi + continue + fi + + # Extract //@ compile-flags:, //@ edition:, and //@ rustc-env: directives. + test_flags=$(extract_test_flags "$test_path") - # Clean up generated JSON - [ -z "$KEEP_FILES" ] && [ -f "$json_file" ] && rm -f "$json_file" -done < "$PASSING_TSV" + # shellcheck disable=SC2086 # intentional word-splitting of $test_flags + if (( SAVE_DEBUG_OUTPUT )); then + test_stderr=$(mktemp) + "${RUN_SMIR_CMD[@]}" ${test_flags} "$test_path" >/dev/null 2>"$test_stderr" && rc=0 || rc=$? + else + "${RUN_SMIR_CMD[@]}" ${test_flags} "$test_path" >/dev/null 2>&1 && rc=0 || rc=$? + fi -echo "—— Summary ——" -echo "Total tests : $total" -echo "Passed : $passed" -echo "Failed : $failed" + if (( rc == 0 )); then + (( ++passed )) + if (( VERBOSE )); then + log "✅ PASSING: $test_path" + fi + else + log "❌ FAILED: $test_path (exit $rc)" + if (( SAVE_DEBUG_OUTPUT )) && [[ -s "$test_stderr" ]]; then + tail -4 "$test_stderr" | sed 's/^/ /' + cp -- "$test_stderr" "${DEBUG_DIR}/${test_name}.stderr" + fi + (( ++failed )) + fi + (( SAVE_DEBUG_OUTPUT )) && rm -f -- "$test_stderr" -if [ $total -gt 0 ]; then - # Calculate ratios as decimal fractions (e.g. 0.75) - ratio_passed=$(awk "BEGIN { printf \"%.2f\", $passed/$total }") - ratio_failed=$(awk "BEGIN { printf \"%.2f\", $failed/$total }") + # Clean up generated JSON + if (( ! SAVE_GENERATED_OUTPUT )) && [[ -f "$json_file" ]]; then + rm -f -- "$json_file" + fi +done <"$PASSING_TSV" - echo - echo "Passing ratio : $ratio_passed" - echo "Failing ratio : $ratio_failed" +elapsed=$(( SECONDS - start_time )) +printf '—— Summary ——\n' +printf 'Total tests : %d\n' "$total" +printf 'Skipped : %d\n' "$skipped" +printf 'Passed : %d\n' "$passed" +printf 'Failed : %d\n' "$failed" +printf 'Elapsed : %dm%02ds\n' $(( elapsed / 60 )) $(( elapsed % 60 )) + +run=$(( total - skipped )) +if (( run > 0 )); then + ratio_passed="$(awk "BEGIN { printf \"%.2f\", $passed/$run }")" + ratio_failed="$(awk "BEGIN { printf \"%.2f\", $failed/$run }")" + printf '\nPassing ratio : %s\nFailing ratio : %s\n' "$ratio_passed" "$ratio_failed" fi -if [ $failed -gt 0 ]; then - exit 1 +if (( SAVE_DEBUG_OUTPUT && failed > 0 )); then + printf '\nDebug output saved to: %s\n' "$DEBUG_DIR" fi + +(( failed == 0 )) || exit 1 From d0244f78702a4da9f362b5f0c33fd9d6ce982bc2 Mon Sep 17 00:00:00 2001 From: cds-amal Date: Thu, 26 Feb 2026 07:33:14 -0500 Subject: [PATCH 3/6] Recursively walk nested struct/tuple fields to resolve provenance types get_prov_ty previously used field_for_offset (exact byte-offset match) to find the field at a given provenance offset, then returned that field's type directly. This broke on nested structs: a pointer at byte offset 56 inside TestDescAndFn sits inside a nested TestDesc field that starts at offset 0, not at offset 56 itself. Add field_containing_offset (range-based lookup: largest field start <= target) and make the struct and tuple branches recurse into the containing field with a relative offset, walking down through the field hierarchy until reaching the actual pointer type. Replace panicking unwraps/asserts with graceful eprintln + return None for layout failures, non-rigid types, and unexpected non-zero offsets on builtin_deref types. Add debug_log_println! instrumentation at key resolution points (zero cost without --features debug_log). Moves 18 UI tests from failing to passing (compile-flags tests that now work with the runner fix, plus tests that previously panicked in get_prov_ty). --- src/printer.rs | 99 ++++++++++++++++++++++++++++++++------------ tests/ui/failing.tsv | 18 -------- tests/ui/passing.tsv | 18 ++++++++ 3 files changed, 91 insertions(+), 44 deletions(-) diff --git a/src/printer.rs b/src/printer.rs index 01a2bcb..060a6ba 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -844,40 +844,72 @@ fn update_link_map<'tcx>( /// Returns the field index (source order) for a given offset and layout if /// the layout contains fields (shared between all variants), otherwise None. /// NB No search for fields within variants (needs recursive call). -fn field_for_offset(l: LayoutShape, offset: usize) -> Option { - // FieldIdx - match l.fields { +fn field_for_offset(l: &LayoutShape, offset: usize) -> Option { + match &l.fields { FieldsShape::Primitive | FieldsShape::Union(_) | FieldsShape::Array { .. } => None, + FieldsShape::Arbitrary { offsets } => offsets + .iter() + .enumerate() + .find(|(_, o)| o.bytes() == offset) + .map(|(i, _)| i), + } +} + +/// Find the field whose byte range contains the given offset. +/// Returns (field_index, field_start_byte_offset). +fn field_containing_offset(l: &LayoutShape, offset: usize) -> Option<(usize, usize)> { + match &l.fields { FieldsShape::Arbitrary { offsets } => { - let fields: Vec = offsets.into_iter().map(|o| o.bytes()).collect(); - fields - .into_iter() + let mut indexed: Vec<(usize, usize)> = offsets + .iter() .enumerate() - .find(|(_, o)| *o == offset) - .map(|(i, _)| i) + .map(|(i, o)| (i, o.bytes())) + .collect(); + indexed.sort_by_key(|&(_, o)| o); + // The containing field is the one with the largest start offset <= target. + indexed.into_iter().rev().find(|&(_, o)| o <= offset) } + _ => None, } } fn get_prov_ty(ty: stable_mir::ty::Ty, offset: &usize) -> Option { use stable_mir::ty::RigidTy; let ty_kind = ty.kind(); + debug_log_println!("get_prov_ty: {:?} offset={}", ty_kind, offset); // if ty is a pointer, box, or Ref, expect no offset and dereference - if let Some(ty) = ty_kind.builtin_deref(true) { - assert!(*offset == 0); - return Some(ty.ty); + if let Some(derefed) = ty_kind.builtin_deref(true) { + if *offset != 0 { + eprintln!( + "get_prov_ty: unexpected non-zero offset {} for builtin_deref type {:?}", + offset, ty_kind + ); + return None; + } + debug_log_println!("get_prov_ty: resolved -> pointee {:?}", derefed.ty.kind()); + return Some(derefed.ty); } // Otherwise the allocation is a reference within another kind of data. // Decompose this outer data type to determine the reference type - let layout = ty - .layout() - .map(|l| l.shape()) - .expect("Unable to get layout for {ty_kind:?}"); - let ref_ty = match ty_kind - .rigid() - .expect("Non-rigid-ty allocation found! {ty_kind:?}") - { + let layout = match ty.layout().map(|l| l.shape()) { + Ok(l) => l, + Err(_) => { + eprintln!("get_prov_ty: unable to get layout for {:?}", ty_kind); + return None; + } + }; + let rigid = match ty_kind.rigid() { + Some(r) => r, + None => { + eprintln!( + "get_prov_ty: non-rigid type in allocation: {:?} (offset={})", + ty_kind, offset + ); + return None; + } + }; + let ref_ty = match rigid { // homogenous, so no choice. Could check alignment of the offset... RigidTy::Array(ty, _) | RigidTy::Slice(ty) => Some(*ty), // cases covered above @@ -887,16 +919,24 @@ fn get_prov_ty(ty: stable_mir::ty::Ty, offset: &usize) -> Option { unreachable!("Covered by builtin_deref above") } - // For other structs, consult layout to determine field type + // For structs, find the field containing this offset and recurse. + // The provenance offset may point into a nested struct field, so we + // walk down through the field hierarchy until we reach the pointer. RigidTy::Adt(adt_def, args) if ty_kind.is_struct() => { - let field_idx = field_for_offset(layout, *offset).unwrap(); + let (field_idx, field_start) = field_containing_offset(&layout, *offset)?; // NB struct, single variant - let fields = adt_def.variants().pop().map(|v| v.fields()).unwrap(); - fields.get(field_idx).map(|f| f.ty_with_args(args)) + let fields = adt_def.variants().pop().map(|v| v.fields())?; + let field_ty = fields.get(field_idx)?.ty_with_args(args); + let relative_offset = *offset - field_start; + debug_log_println!( + "get_prov_ty: struct {:?} offset={} -> field {} (start={}) type {:?}, relative_offset={}", + adt_def, offset, field_idx, field_start, field_ty.kind(), relative_offset + ); + return get_prov_ty(field_ty, &relative_offset); } RigidTy::Adt(_adt_def, _args) if ty_kind.is_enum() => { // we have to figure out which variant we are dealing with (requires the data) - match field_for_offset(layout, *offset) { + match field_for_offset(&layout, *offset) { None => // FIXME we'd have to figure out which variant we are dealing with (requires the data) { @@ -909,9 +949,16 @@ fn get_prov_ty(ty: stable_mir::ty::Ty, offset: &usize) -> Option { - let field_idx = field_for_offset(layout, *offset)?; - fields.get(field_idx).copied() + let (field_idx, field_start) = field_containing_offset(&layout, *offset)?; + let field_ty = *fields.get(field_idx)?; + let relative_offset = *offset - field_start; + debug_log_println!( + "get_prov_ty: tuple offset={} -> field {} (start={}) type {:?}, relative_offset={}", + offset, field_idx, field_start, field_ty.kind(), relative_offset + ); + return get_prov_ty(field_ty, &relative_offset); } RigidTy::FnPtr(_) => None, _unimplemented => { diff --git a/tests/ui/failing.tsv b/tests/ui/failing.tsv index e09990b..bb719d3 100644 --- a/tests/ui/failing.tsv +++ b/tests/ui/failing.tsv @@ -1,19 +1 @@ -tests/ui/async-await/issue-60709.rs 101 -tests/ui/async-await/issues/issue-59972.rs 101 -tests/ui/cfg/cfg_attr.rs 101 -tests/ui/cfg/cfgs-on-items.rs 101 -tests/ui/consts/const-eval/const_fn_ptr.rs 101 -tests/ui/consts/const-int-arithmetic-overflow.rs 101 -tests/ui/env-macro/env-env-overload.rs 101 -tests/ui/env-macro/env-env.rs 101 -tests/ui/fmt/fmt_debug/none.rs 101 tests/ui/issues/issue-26641.rs 101 -tests/ui/label/label_break_value_desugared_break.rs 101 -tests/ui/macros/macro-meta-items.rs 101 -tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs 101 -tests/ui/test-attrs/test-panic-while-printing.rs 101 -tests/ui/traits/const-traits/const-drop.rs 101 -tests/ui/try-block/issue-45124.rs 101 -tests/ui/try-block/try-block-in-match.rs 101 -tests/ui/try-block/try-block-in-return.rs 101 -tests/ui/try-block/try-block.rs 101 diff --git a/tests/ui/passing.tsv b/tests/ui/passing.tsv index 53cc969..2cbf143 100644 --- a/tests/ui/passing.tsv +++ b/tests/ui/passing.tsv @@ -181,6 +181,8 @@ tests/ui/associated-types/issue-54467.rs tests/ui/associated-types/issue-55846.rs tests/ui/associated-types/object-method-numbering.rs tests/ui/async-await/context-is-sorta-unwindsafe.rs +tests/ui/async-await/issue-60709.rs +tests/ui/async-await/issues/issue-59972.rs tests/ui/attributes/tool_attributes.rs tests/ui/augmented-assignments-rpass.rs tests/ui/auto-instantiate.rs @@ -416,7 +418,9 @@ tests/ui/cfg/cfg-macros-notfoo.rs tests/ui/cfg/cfg-target-abi.rs tests/ui/cfg/cfg-target-compact.rs tests/ui/cfg/cfg-target-vendor.rs +tests/ui/cfg/cfg_attr.rs tests/ui/cfg/cfg_stmt_expr.rs +tests/ui/cfg/cfgs-on-items.rs tests/ui/cfg/conditional-compile.rs tests/ui/cfg/true-false.rs tests/ui/cfguard-run.rs @@ -633,6 +637,7 @@ tests/ui/consts/const-enum-vec-index.rs tests/ui/consts/const-enum-vec-ptr.rs tests/ui/consts/const-enum-vector.rs tests/ui/consts/const-err-rpass.rs +tests/ui/consts/const-eval/const_fn_ptr.rs tests/ui/consts/const-eval/enum_discr.rs tests/ui/consts/const-eval/float_methods.rs tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs @@ -658,6 +663,7 @@ tests/ui/consts/const-fn-type-name.rs tests/ui/consts/const-fn-val.rs tests/ui/consts/const-fn.rs tests/ui/consts/const-index-feature-gate.rs +tests/ui/consts/const-int-arithmetic-overflow.rs tests/ui/consts/const-int-arithmetic.rs tests/ui/consts/const-int-conversion-rpass.rs tests/ui/consts/const-int-overflowing-rpass.rs @@ -964,6 +970,8 @@ tests/ui/enum/issue-19340-2.rs tests/ui/enum/issue-23304-1.rs tests/ui/enum/issue-23304-2.rs tests/ui/enum/issue-42747.rs +tests/ui/env-macro/env-env-overload.rs +tests/ui/env-macro/env-env.rs tests/ui/env-macro/option_env-not-defined.rs tests/ui/errors/remap-path-prefix-macro.rs tests/ui/explicit-i-suffix.rs @@ -1003,6 +1011,7 @@ tests/ui/float/classify-runtime-const.rs tests/ui/float/conv-bits-runtime-const.rs tests/ui/float/int-to-float-miscompile-issue-105626.rs tests/ui/fmt/fmt_debug/full.rs +tests/ui/fmt/fmt_debug/none.rs tests/ui/fmt/fmt_debug/shallow.rs tests/ui/fmt/format-args-capture-macro-hygiene-pass.rs tests/ui/fmt/format-args-capture.rs @@ -1653,6 +1662,7 @@ tests/ui/iterators/iter-sum-overflow-debug.rs tests/ui/iterators/iter-sum-overflow-ndebug.rs tests/ui/iterators/iter-sum-overflow-overflow-checks.rs tests/ui/iterators/skip-count-overflow.rs +tests/ui/label/label_break_value_desugared_break.rs tests/ui/last-use-in-cap-clause.rs tests/ui/last-use-is-capture.rs tests/ui/late-bound-lifetimes/issue-36381.rs @@ -1748,6 +1758,7 @@ tests/ui/macros/macro-lifetime-used-with-labels.rs tests/ui/macros/macro-lifetime-used-with-static.rs tests/ui/macros/macro-lifetime.rs tests/ui/macros/macro-literal.rs +tests/ui/macros/macro-meta-items.rs tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs tests/ui/macros/macro-metavar-expr-concat/repetitions.rs tests/ui/macros/macro-metavar-expr-concat/unicode-expansion.rs @@ -2277,6 +2288,7 @@ tests/ui/rfcs/rfc-2008-non-exhaustive/structs_same_crate.rs tests/ui/rfcs/rfc-2008-non-exhaustive/variants_same_crate.rs tests/ui/rfcs/rfc-2027-dyn-compatible-for-dispatch/manual-self-impl-for-unsafe-obj.rs tests/ui/rfcs/rfc-2091-track-caller/call-chain.rs +tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs tests/ui/rfcs/rfc-2091-track-caller/caller-location-intrinsic.rs tests/ui/rfcs/rfc-2091-track-caller/const-caller-location.rs tests/ui/rfcs/rfc-2091-track-caller/intrinsic-wrapper.rs @@ -2533,6 +2545,7 @@ tests/ui/syntax-extension-minor.rs tests/ui/tail-call-arg-leak.rs tests/ui/tail-cps.rs tests/ui/test-attrs/test-main-not-dead.rs +tests/ui/test-attrs/test-panic-while-printing.rs tests/ui/test-attrs/test-runner-hides-main.rs tests/ui/thread-local/tls.rs tests/ui/threads-sendsync/child-outlives-parent.rs @@ -2608,6 +2621,7 @@ tests/ui/traits/bug-7295.rs tests/ui/traits/coercion-generic.rs tests/ui/traits/coercion.rs tests/ui/traits/conditional-dispatch.rs +tests/ui/traits/const-traits/const-drop.rs tests/ui/traits/const-traits/specialization/const-default-const-specialized.rs tests/ui/traits/const-traits/specialization/non-const-default-const-specialized.rs tests/ui/traits/const-traits/trait-where-clause-run.rs @@ -2719,6 +2733,10 @@ tests/ui/traits/with-bounds-default.rs tests/ui/transmute-non-immediate-to-immediate.rs tests/ui/transmute/transmute-zst-generics.rs tests/ui/trivial_casts-rpass.rs +tests/ui/try-block/issue-45124.rs +tests/ui/try-block/try-block-in-match.rs +tests/ui/try-block/try-block-in-return.rs +tests/ui/try-block/try-block.rs tests/ui/try-block/try-is-identifier-edition2015.rs tests/ui/try-from-int-error-partial-eq.rs tests/ui/try-operator-hygiene.rs From 054fa324e8828df3a2924d973d4295390176b6e0 Mon Sep 17 00:00:00 2001 From: cds-amal Date: Thu, 26 Feb 2026 07:46:39 -0500 Subject: [PATCH 4/6] Update failing / passing tsvs --- tests/ui/failing.tsv | 1 + tests/ui/passing.tsv | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/failing.tsv b/tests/ui/failing.tsv index bb719d3..3d0b48b 100644 --- a/tests/ui/failing.tsv +++ b/tests/ui/failing.tsv @@ -1 +1,2 @@ tests/ui/issues/issue-26641.rs 101 +tests/ui/sanitizer/cfi/drop-in-place.rs 101 diff --git a/tests/ui/passing.tsv b/tests/ui/passing.tsv index 2cbf143..d802f40 100644 --- a/tests/ui/passing.tsv +++ b/tests/ui/passing.tsv @@ -2326,7 +2326,6 @@ tests/ui/runtime/stdout-before-main.rs tests/ui/runtime/stdout-during-shutdown-unix.rs tests/ui/runtime/stdout-during-shutdown-windows.rs tests/ui/sanitizer/cfi/complex-receiver.rs -tests/ui/sanitizer/cfi/drop-in-place.rs tests/ui/sanitizer/cfi/fn-ptr.rs tests/ui/sanitizer/cfi/self-ref.rs tests/ui/sanitizer/cfi/sized-associated-ty.rs From 868122dda90a482d6320d8ab4169a2f950eb3e62 Mon Sep 17 00:00:00 2001 From: cds-amal Date: Thu, 26 Feb 2026 07:56:12 -0500 Subject: [PATCH 5/6] chore(changelog): add UI test runner and provenance resolution entries --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a6e3a2..c6e70ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,10 +27,15 @@ retroactive best-effort summary; earlier changes were not formally tracked. - Restructured `printer.rs` into a declarative 3-phase pipeline: `collect_items` -> `collect_and_analyze_items` -> `assemble_smir`, with `CollectedCrate`/`DerivedInfo` interface types enforcing the boundary between collection and assembly - Added `AllocMap` with debug-mode coherence checking (`verify_coherence`): asserts that every `AllocId` in stored bodies exists in the alloc map and that no body is walked twice; zero cost in release builds - Removed dead static-item fixup from `assemble_smir` (violated the phase boundary, misclassified statics as functions; never triggered in integration tests) +- Rewrote `run_ui_tests.sh`: flag-based CLI (`--verbose`, `--save-generated-output`, `--save-debug-output`), build-once-then-invoke (eliminates per-test cargo overhead), arch-aware skip logic for cross-platform test lists +- UI test runners now extract and pass `//@ compile-flags:`, `//@ edition:`, and `//@ rustc-env:` directives from test files (previously silently ignored) - Switched from compiler build to `rustup`-managed toolchain ([#33]) - Removed forked rust dependency ([#19]) ### Fixed +- Fixed `get_prov_ty` to recursively walk nested struct/tuple fields when resolving provenance types; previously used exact byte-offset matching which panicked on pointers inside nested structs (e.g., `&str` at offset 56 inside `TestDesc` inside `TestDescAndFn`) +- Removed incorrect `builtin_deref` assertions from VTable and Static allocation collection that rejected valid non-pointer types (raw `*const ()` vtable pointers, non-pointer statics) +- Replaced panicking `unwrap`/`assert` calls in `get_prov_ty` with graceful fallbacks for layout failures, non-rigid types, and unexpected offsets - Fixed early `return` in `BodyAnalyzer::visit_terminator` that skipped `super_terminator()`, causing alloc/type/span collection to miss everything inside `Call` terminators with non-`ZeroSized` function operands (const-evaluated function pointers); bug present since [`aff2dd0`](https://github.com/runtimeverification/stable-mir-json/commit/aff2dd0) - Avoided duplicate `inst.body()` calls that were reallocating `AllocId`s ([#120]) - Prevented svg/png generation when `dot` is unavailable ([#117]) From 8c64cb6f6750d2599fe9fd64a8948384bb87a615 Mon Sep 17 00:00:00 2001 From: cds-amal Date: Thu, 26 Feb 2026 08:07:48 -0500 Subject: [PATCH 6/6] Cargo fmt --- src/printer.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/printer.rs b/src/printer.rs index 060a6ba..f220962 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -956,7 +956,11 @@ fn get_prov_ty(ty: stable_mir::ty::Ty, offset: &usize) -> Option field {} (start={}) type {:?}, relative_offset={}", - offset, field_idx, field_start, field_ty.kind(), relative_offset + offset, + field_idx, + field_start, + field_ty.kind(), + relative_offset ); return get_prov_ty(field_ty, &relative_offset); }