From be492d3678d073fde1597c9a67183282fb3ac389 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 22 Jul 2026 14:34:14 -0700 Subject: [PATCH 1/7] Add experimental wasm externref lang type Adds core::arch::wasm32::externref behind feature(wasm_externref): an opaque host reference lowering to LLVM ptr addrspace(10), giving real wasm reference types in extern "C" signatures. externref is a bare-position-only type, following clang's __externref_t semantics: legal only as the top-level type of function parameters, return values and locals (function pointer signature slots included), enforced at type-check time via wf checks and typeck writeback. A monomorphization-time check backstops the remaining codegen-only channels (e.g. coroutine captures). Co-authored-by: Hood Chatham --- compiler/rustc_abi/src/lib.rs | 2 + compiler/rustc_hir/src/lang_items.rs | 3 + .../rustc_hir_analysis/src/check/check.rs | 14 ++ .../rustc_hir_analysis/src/check/wfcheck.rs | 28 +++ compiler/rustc_hir_typeck/src/writeback.rs | 44 +++++ .../rustc_lint/src/types/improper_ctypes.rs | 5 + compiler/rustc_middle/src/ty/util.rs | 76 +++++++- .../src/mono_checks/externref_check.rs | 180 ++++++++++++++++++ .../rustc_monomorphize/src/mono_checks/mod.rs | 2 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_ty_utils/src/layout.rs | 6 + library/stdarch/crates/core_arch/src/lib.rs | 2 + .../crates/core_arch/src/wasm32/mod.rs | 32 ++++ tests/assembly-llvm/wasm-externref.rs | 24 +++ tests/codegen-llvm/wasm-externref.rs | 36 ++++ tests/ui/wasm/externref-position-errors.rs | 90 +++++++++ .../ui/wasm/externref-position-errors.stderr | 80 ++++++++ 17 files changed, 624 insertions(+), 1 deletion(-) create mode 100644 compiler/rustc_monomorphize/src/mono_checks/externref_check.rs create mode 100644 tests/assembly-llvm/wasm-externref.rs create mode 100644 tests/codegen-llvm/wasm-externref.rs create mode 100644 tests/ui/wasm/externref-position-errors.rs create mode 100644 tests/ui/wasm/externref-position-errors.stderr diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index bff4c9bdf47ef..6d5b01ef28b17 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1839,6 +1839,8 @@ impl AddressSpace { /// The address space for workgroup memory on nvptx and amdgpu. /// See e.g. the `gpu_launch_sized_workgroup_mem` intrinsic for details. pub const GPU_WORKGROUP: Self = AddressSpace(3); + /// The address space LLVM's WebAssembly backend uses for `externref` values. + pub const WASM_EXTERNREF: Self = AddressSpace(10); } /// How many scalable vectors are in a `BackendRepr::ScalableVector`? diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 1592dfdde4e6f..fe7fbb8719b43 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -271,6 +271,9 @@ language_item_table! { PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; + // An opaque handle to a WebAssembly `externref` value, lowered as a pointer + // in the wasm externref address space. + ExternRef, sym::externref, externref, Target::Struct, GenericRequirement::Exact(0); Type, sym::type_info, type_struct, Target::Struct, GenericRequirement::None; TypeId, sym::type_id, type_id, Target::Struct, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 626529cb6fc5b..7ab79c3b26e26 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -797,6 +797,13 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), check_static_inhabited(tcx, def_id); check_static_linkage(tcx, def_id); let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip(); + if tcx.ty_mentions_externref_illegally(ty, /* allow_bare */ false) { + tcx.dcx().span_err( + tcx.ty_span(def_id), + "wasm `externref` cannot be used in a `static`: it may only appear as a \ + bare function parameter, return value or local", + ); + } res = res.and(wfcheck::check_static_item( tcx, def_id, ty, /* should_check_for_sync */ true, )); @@ -947,6 +954,13 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), let ty_span = tcx.ty_span(def_id); let ty = wfcx.deeply_normalize(ty_span, Some(WellFormedLoc::Ty(def_id)), ty); wfcx.register_wf_obligation(ty_span, Some(WellFormedLoc::Ty(def_id)), ty.into()); + if tcx.ty_mentions_externref_illegally(ty, /* allow_bare */ false) { + tcx.dcx().span_err( + ty_span, + "wasm `externref` cannot be used in a `const`: it may only appear as a \ + bare function parameter, return value or local", + ); + } wfcx.register_bound( traits::ObligationCause::new( ty_span, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f59345cd970eb..8c179e86708ff 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1016,6 +1016,17 @@ pub(crate) fn check_type_defn<'tcx>( ); wfcx.register_wf_obligation(span, Some(WellFormedLoc::Ty(field_id)), ty.into()); + if tcx.ty_mentions_externref_illegally(ty, /* allow_bare */ false) { + tcx.dcx().span_err( + span, + format!( + "wasm `externref` cannot be used in a {} field: it may only appear \ + as a bare function parameter, return value or local", + adt_def.variant_descr() + ), + ); + } + if matches!(ty.kind(), ty::Adt(def, _) if def.repr().scalable()) && !matches!(adt_def.repr().scalable, Some(ScalableElt::Container)) { @@ -1647,6 +1658,23 @@ fn check_fn_or_method<'tcx>( ); } + // wasm `externref` may appear bare in parameter/return slots, but never + // inside another type. Impls of `externref` itself (core-only, by + // coherence) are exempt so `Clone` is expressible. + if !tcx.is_externref_impl_item(def_id.to_def_id()) { + for (idx, ty) in sig.inputs_and_output.iter().enumerate() { + if tcx.ty_mentions_externref_illegally(ty, /* allow_bare */ true) { + tcx.dcx().span_err( + arg_span(idx), + format!( + "wasm `externref` cannot be used inside `{ty}`: it may only appear \ + as a bare function parameter, return value or local" + ), + ); + } + } + } + check_where_clauses(wfcx, def_id); if sig.abi() == ExternAbi::RustCall { diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 4161975d88ea9..f5ad56b01ec52 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -103,6 +103,15 @@ struct WritebackCx<'cx, 'tcx> { body: &'tcx hir::Body<'tcx>, rustc_dump_user_args: bool, + + /// Whether to enforce wasm `externref` position rules in this body + /// (the lang item exists and this body is not part of `externref`'s own + /// core-only impls). + externref_checks: bool, + + /// Spans already reported for externref position errors, to avoid + /// duplicate diagnostics for the same node. + reported_externref: FxHashSet, } impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { @@ -113,11 +122,16 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { ) -> WritebackCx<'cx, 'tcx> { let owner = body.id().hir_id.owner; + let externref_checks = fcx.tcx.lang_items().externref().is_some() + && !fcx.tcx.is_externref_impl_item(owner.to_def_id()); + let mut wbcx = WritebackCx { fcx, typeck_results: ty::TypeckResults::new(owner), body, rustc_dump_user_args, + externref_checks, + reported_externref: FxHashSet::default(), }; // HACK: We specifically don't want the (opaque) error from tainting our @@ -134,6 +148,29 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.fcx.tcx } + /// Enforce wasm `externref` position rules: values may only occupy bare + /// value slots (function parameters, return values, locals and function + /// pointer signature slots), never appear inside another type or as a + /// generic argument. + fn check_externref_position(&mut self, span: Span, ty: Ty<'tcx>, allow_bare: bool) { + if !self.externref_checks { + return; + } + if self.tcx().ty_mentions_externref_illegally(ty, allow_bare) + && self.reported_externref.insert(span) + { + let msg = if allow_bare { + format!( + "wasm `externref` cannot be used inside `{ty}`: it may only appear as a \ + bare function parameter, return value or local" + ) + } else { + "wasm `externref` cannot be used as a generic argument".to_string() + }; + self.tcx().dcx().span_err(span, msg); + } + } + fn write_ty_to_typeck_results(&mut self, hir_id: HirId, ty: Ty<'tcx>) { debug!("write_ty_to_typeck_results({:?}, {:?})", hir_id, ty); assert!( @@ -675,6 +712,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let n_ty = self.fcx.node_ty(hir_id); let n_ty = self.resolve(n_ty, &span); self.write_ty_to_typeck_results(hir_id, n_ty); + self.check_externref_position(span, n_ty, /* allow_bare */ true); debug!(?n_ty); // Resolve any generic parameters @@ -682,6 +720,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let args = self.resolve(args, &span); debug!("write_args_to_tcx({:?}, {:?})", hir_id, args); assert!(!args.has_infer() && !args.has_placeholders()); + for ty in args.types() { + self.check_externref_position(span, ty, /* allow_bare */ false); + } self.typeck_results.node_args_mut().insert(hir_id, args); } } @@ -697,6 +738,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { Some(adjustment) => { let resolved_adjustment = self.resolve(adjustment, &span); debug!(?resolved_adjustment); + for adjust in &resolved_adjustment { + self.check_externref_position(span, adjust.target, /* allow_bare */ true); + } self.typeck_results.adjustments_mut().insert(hir_id, resolved_adjustment); } } diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 0c34d9da66f1d..fd14cb3551723 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -758,6 +758,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if def.is_phantom_data() { return FfiPhantom(ty); } + // wasm `externref` lowers to a wasm reference type, which is a + // first-class type in the wasm C ABI. + if tcx.is_lang_item(def.did(), hir::LangItem::ExternRef) { + return FfiSafe; + } match def.adt_kind() { AdtKind::Struct | AdtKind::Union => { if let Some(sym::cstring_type | sym::cstr_type) = diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index c4d9ec9f64289..eaa96781e1438 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -28,7 +28,8 @@ use crate::traits::ObligationCause; use crate::ty::layout::{FloatExt, IntegerExt}; use crate::ty::{ self, Asyncness, FallibleTypeFolder, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeFoldable, - TypeFolder, TypeSuperFoldable, TypeVisitableExt, Unnormalized, Upcast, + TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + TypeVisitor, Unnormalized, Upcast, }; #[derive(Copy, Clone, Debug)] @@ -143,6 +144,79 @@ impl<'tcx> TyCtxt<'tcx> { }) } + /// Whether `ty` is the wasm `externref` lang type itself. + pub fn is_externref(self, ty: Ty<'tcx>) -> bool { + matches!(ty.kind(), ty::Adt(def, _) if self.is_lang_item(def.did(), hir::LangItem::ExternRef)) + } + + /// wasm `externref` values exist only in wasm value slots: function + /// parameters, return values and locals. Function pointer signature slots + /// are also value positions, so bare `externref` is permitted there. + /// + /// This reports whether `ty` mentions `externref` in any other position, + /// e.g. behind a reference or pointer, as an aggregate member, or as a + /// generic argument. `allow_bare` permits `ty` itself being `externref` + /// (i.e. `ty` occupies a value slot). + pub fn ty_mentions_externref_illegally(self, ty: Ty<'tcx>, allow_bare: bool) -> bool { + use std::ops::ControlFlow; + + if self.lang_items().externref().is_none() { + return false; + } + if allow_bare && self.is_externref(ty) { + return false; + } + + struct MentionVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + } + impl<'tcx> TypeVisitor> for MentionVisitor<'tcx> { + type Result = ControlFlow<()>; + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { + match t.kind() { + ty::Adt(..) if self.tcx.is_externref(t) => ControlFlow::Break(()), + // Function pointer parameter/return slots are value + // positions: bare `externref` is allowed in them. + ty::FnPtr(sig_tys, _) => { + for io in sig_tys.skip_binder().inputs_and_output { + if !self.tcx.is_externref(io) { + io.visit_with(self)?; + } + } + ControlFlow::Continue(()) + } + // Fn items are code, not storage; their signatures are + // value positions and their generic instantiations are + // checked separately as generic arguments. + ty::FnDef(..) => ControlFlow::Continue(()), + _ => t.super_visit_with(self), + } + } + } + ty.visit_with(&mut MentionVisitor { tcx: self }).is_break() + } + + /// Whether `def_id` is (an item within) an `impl` of the wasm `externref` + /// lang type. By coherence such impls can only be written in the defining + /// crate (`core`); they are exempt from externref position checks so that + /// `Copy`/`Clone` impls are expressible. + pub fn is_externref_impl_item(self, mut def_id: DefId) -> bool { + if self.lang_items().externref().is_none() { + return false; + } + loop { + if let DefKind::Impl { .. } = self.def_kind(def_id) { + return self.is_externref( + self.type_of(def_id).instantiate_identity().skip_normalization(), + ); + } + match self.opt_parent(def_id) { + Some(parent) => def_id = parent, + None => return false, + } + } + } + pub fn res_generics_def_id(self, res: Res) -> Option { match res { Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id) => { diff --git a/compiler/rustc_monomorphize/src/mono_checks/externref_check.rs b/compiler/rustc_monomorphize/src/mono_checks/externref_check.rs new file mode 100644 index 0000000000000..6d99f44d8afa5 --- /dev/null +++ b/compiler/rustc_monomorphize/src/mono_checks/externref_check.rs @@ -0,0 +1,180 @@ +//! Monomorphization-time enforcement of wasm `externref` exclusion rules: +//! `externref` values only exist as bare wasm locals, function arguments and +//! return values. They can never be placed in linear memory, so we reject any +//! local whose layout would aggregate one, any borrow of one, and any load or +//! store of one through a pointer. + +use rustc_abi::{AddressSpace, BackendRepr, FieldsShape, Layout, Primitive, Scalar, Variants}; +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::mir::visit::Visitor as MirVisitor; +use rustc_middle::mir::{self, Location, traversal}; +use rustc_middle::ty::layout::{LayoutCx, TyAndLayout}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_span::Span; + +pub(crate) fn check_externref<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, +) { + if tcx.lang_items().externref().is_none() { + return; + } + + let mut visitor = + ExternRefVisitor { tcx, instance, body, contains_cache: FxHashMap::default() }; + + for decl in body.local_decls.iter() { + let ty = visitor.monomorphize(decl.ty); + if let Some(layout) = visitor.layout_of(ty) + && !matches!(layout.backend_repr, BackendRepr::Scalar(_)) + && visitor.contains_externref(layout) + { + visitor.emit_storage_error(ty, decl.source_info.span); + } + } + + for (bb, data) in traversal::mono_reachable(body, tcx, instance) { + visitor.visit_basic_block_data(bb, data); + } +} + +struct ExternRefVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, + contains_cache: FxHashMap, bool>, +} + +fn scalar_is_externref(scalar: Scalar) -> bool { + matches!(scalar.primitive(), Primitive::Pointer(AddressSpace::WASM_EXTERNREF)) +} + +impl<'tcx> ExternRefVisitor<'tcx> { + fn monomorphize(&self, value: T) -> T + where + T: TypeFoldable>, + { + self.instance.instantiate_mir_and_normalize_erasing_regions( + self.tcx, + ty::TypingEnv::fully_monomorphized(), + ty::EarlyBinder::bind(self.tcx, value), + ) + } + + fn layout_of(&self, ty: Ty<'tcx>) -> Option> { + self.tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)).ok() + } + + /// Whether any value of this layout includes an `externref`, at any depth, + /// without following pointer indirection. + fn contains_externref(&mut self, layout: TyAndLayout<'tcx>) -> bool { + if let Some(&cached) = self.contains_cache.get(&layout.layout) { + return cached; + } + let result = self.contains_externref_uncached(layout); + self.contains_cache.insert(layout.layout, result); + result + } + + fn contains_externref_uncached(&mut self, layout: TyAndLayout<'tcx>) -> bool { + match layout.backend_repr { + BackendRepr::Scalar(s) => scalar_is_externref(s), + BackendRepr::ScalarPair { a, b, .. } => { + scalar_is_externref(a) || scalar_is_externref(b) + } + BackendRepr::SimdVector { element, .. } + | BackendRepr::SimdScalableVector { element, .. } => scalar_is_externref(element), + BackendRepr::Memory { .. } => { + let cx = LayoutCx::new(self.tcx, ty::TypingEnv::fully_monomorphized()); + let fields_contain = |this: &mut Self, layout: TyAndLayout<'tcx>| match layout + .fields + { + FieldsShape::Primitive => false, + FieldsShape::Array { count, .. } => { + count > 0 && this.contains_externref(layout.field(&cx, 0)) + } + FieldsShape::Union(_) | FieldsShape::Arbitrary { .. } => (0..layout + .fields + .count()) + .any(|i| this.contains_externref(layout.field(&cx, i))), + }; + if fields_contain(self, layout) { + return true; + } + if let Variants::Multiple { variants, .. } = &layout.variants { + let variant_indices = variants.indices(); + for vidx in variant_indices { + if fields_contain(self, layout.for_variant(&cx, vidx)) { + return true; + } + } + } + false + } + } + } + + fn place_contains_externref(&mut self, place: &mir::Place<'tcx>) -> Option> { + let ty = self.monomorphize(place.ty(self.body, self.tcx).ty); + let layout = self.layout_of(ty)?; + self.contains_externref(layout).then_some(ty) + } + + fn emit_storage_error(&self, ty: Ty<'tcx>, span: Span) { + self.tcx + .dcx() + .struct_span_err( + span, + format!("values of type `{ty}` cannot exist: it contains a wasm `externref`, which cannot be stored in memory"), + ) + .with_help("wasm `externref` values may only be used as bare function arguments, return values and locals") + .emit(); + } + + fn span_of(&self, location: Location) -> Span { + self.body.source_info(location).span + } +} + +impl<'tcx> MirVisitor<'tcx> for ExternRefVisitor<'tcx> { + fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { + if let mir::Rvalue::Ref(_, _, place) | mir::Rvalue::RawPtr(_, place) = rvalue + && let Some(ty) = self.place_contains_externref(place) + { + self.tcx + .dcx() + .struct_span_err( + self.span_of(location), + format!("cannot take a reference to `{ty}`: wasm `externref` values have no memory address"), + ) + .emit(); + } + self.super_rvalue(rvalue, location); + } + + fn visit_place( + &mut self, + place: &mir::Place<'tcx>, + context: mir::visit::PlaceContext, + location: Location, + ) { + // References to externref-containing values cannot be created, but + // unsafe code can still conjure pointers; reject the loads/stores. + // Borrows of indirect places are reported by `visit_rvalue` instead. + if !context.is_borrow() + && !context.is_address_of() + && place.is_indirect() + && let Some(ty) = self.place_contains_externref(place) + { + self.tcx + .dcx() + .struct_span_err( + self.span_of(location), + format!("cannot load or store `{ty}` through a pointer: wasm `externref` values cannot be placed in memory"), + ) + .emit(); + } + self.super_place(place, context, location); + } +} diff --git a/compiler/rustc_monomorphize/src/mono_checks/mod.rs b/compiler/rustc_monomorphize/src/mono_checks/mod.rs index 6569eeafec17d..105f7ab873ba1 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/mod.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/mod.rs @@ -6,12 +6,14 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{Instance, TyCtxt}; mod abi_check; +mod externref_check; mod move_check; fn check_mono_item<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { let body = tcx.instance_mir(instance.def); abi_check::check_feature_dependent_abi(tcx, instance, body); move_check::check_moves(tcx, instance, body); + externref_check::check_externref(tcx, instance, body); } pub(super) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7917478cc2c60..555c3b31d6d1f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -936,6 +936,7 @@ symbols! { extern_weak, external, external_doc, + externref, f16, f16_nan, f16c_target_feature, diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index abec1850502b6..2b96962510797 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -614,6 +614,12 @@ fn layout_of_uncached<'tcx>( univariant(tys, kind)? } + // The wasm `externref` type: an opaque reference lowered as a pointer + // in the wasm externref address space. + ty::Adt(def, _args) if tcx.is_lang_item(def.did(), hir::LangItem::ExternRef) => { + scalar(Pointer(AddressSpace::WASM_EXTERNREF)) + } + // Scalable vector types // // ```rust (ignore, example) diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index 0991ec1acf902..0523ce42915c9 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -13,6 +13,8 @@ proc_macro_hygiene, stmt_expr_attributes, core_intrinsics, + lang_items, + negative_impls, no_core, fmt_helpers_for_derive, rustc_attrs, diff --git a/library/stdarch/crates/core_arch/src/wasm32/mod.rs b/library/stdarch/crates/core_arch/src/wasm32/mod.rs index 57c9157bede89..8a4000d583207 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/mod.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/mod.rs @@ -19,6 +19,38 @@ mod memory; #[stable(feature = "simd_wasm32", since = "1.33.0")] pub use self::memory::*; +/// A WebAssembly `externref`: an opaque, unforgeable reference to a host +/// value, valid only while it remains live on the wasm stack. +/// +/// `externref` is a bare-position-only type: it may appear only as the +/// top-level type of a function parameter, return value or local binding +/// (function pointer signature slots included). It cannot appear inside any +/// other type — no references, aggregates, statics or generic arguments — +/// which is enforced at type-check time. +/// +/// The primary use is typing `extern "C"` imports and exports, where values +/// cross the host boundary directly and identity-preserving, with liveness +/// traced by the host GC: +/// +/// ```ignore (wasm-only) +/// unsafe extern "C" { +/// fn create_ref() -> externref; +/// fn use_ref(v: externref); +/// } +/// ``` +#[allow(non_camel_case_types)] +#[lang = "externref"] +#[non_exhaustive] +#[derive(Copy, Clone)] +#[unstable(feature = "wasm_externref", issue = "none")] +pub struct externref; + +#[unstable(feature = "wasm_externref", issue = "none")] +impl !Send for externref {} + +#[unstable(feature = "wasm_externref", issue = "none")] +impl !Sync for externref {} + /// Generates the [`unreachable`] instruction, which causes an unconditional [trap]. /// /// This function is safe to call and immediately aborts the execution. diff --git a/tests/assembly-llvm/wasm-externref.rs b/tests/assembly-llvm/wasm-externref.rs new file mode 100644 index 0000000000000..2a2bed6425490 --- /dev/null +++ b/tests/assembly-llvm/wasm-externref.rs @@ -0,0 +1,24 @@ +//! Verify that the wasm `externref` lang type produces real wasm reference +//! types in function signatures. + +//@ add-minicore +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=3 --target wasm32-unknown-unknown +//@ needs-llvm-components: webassembly + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core, lang_items)] + +extern crate minicore; + +#[lang = "externref"] +#[non_exhaustive] +pub struct externref; + +// CHECK: .functype describe (externref) -> (externref) +#[no_mangle] +pub extern "C" fn describe(v: externref) -> externref { + v +} diff --git a/tests/codegen-llvm/wasm-externref.rs b/tests/codegen-llvm/wasm-externref.rs new file mode 100644 index 0000000000000..baa6c0294923d --- /dev/null +++ b/tests/codegen-llvm/wasm-externref.rs @@ -0,0 +1,36 @@ +//! Verify that the wasm `externref` lang type lowers to `ptr addrspace(10)` +//! in function signatures and stays a direct SSA value. + +//@ add-minicore +//@ compile-flags: -Copt-level=3 --target wasm32-unknown-unknown +//@ needs-llvm-components: webassembly + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core, lang_items)] + +extern crate minicore; + +#[lang = "externref"] +#[non_exhaustive] +pub struct externref; + +extern "C" { + fn create_ref() -> externref; + fn use_ref(v: externref); +} + +// CHECK: define {{.*}}ptr addrspace(10) @describe(ptr addrspace(10) {{.*}}%v) +#[no_mangle] +pub extern "C" fn describe(v: externref) -> externref { + v +} + +// CHECK-LABEL: @roundtrip +#[no_mangle] +pub extern "C" fn roundtrip() { + // CHECK: %[[V:.+]] = {{.*}}call {{.*}}ptr addrspace(10) @create_ref() + // CHECK: call void @use_ref(ptr addrspace(10) {{.*}}%[[V]]) + unsafe { use_ref(create_ref()) } +} diff --git a/tests/ui/wasm/externref-position-errors.rs b/tests/ui/wasm/externref-position-errors.rs new file mode 100644 index 0000000000000..8f660c72c60a3 --- /dev/null +++ b/tests/ui/wasm/externref-position-errors.rs @@ -0,0 +1,90 @@ +//! wasm `externref` is a bare-position-only type: legal as a function +//! parameter, return value or local (including function pointer signature +//! slots), and nowhere else. All violations are type-check-time errors. + +//@ add-minicore +//@ compile-flags: --target wasm32-unknown-unknown +//@ needs-llvm-components: webassembly +//@ check-fail + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core, lang_items)] +#![allow(non_camel_case_types)] + +extern crate minicore; +use minicore::*; + +#[lang = "externref"] +#[non_exhaustive] +pub struct externref; + +impl Copy for externref {} + +extern "C" { + fn create_ref() -> externref; // OK: bare return slot + fn use_ref(v: externref); // OK: bare parameter slot + fn bad_ref_param(v: &externref); + //~^ ERROR wasm `externref` cannot be used inside `&externref` + static BAD_STATIC: externref; + //~^ ERROR wasm `externref` cannot be used in a `static` +} + +pub struct BadField { + pub v: externref, + //~^ ERROR wasm `externref` cannot be used in a struct field +} + +// Even single-field wrappers are rejected: fields are not value slots. +pub struct BadWrapper(externref); +//~^ ERROR wasm `externref` cannot be used in a struct field + +pub extern "C" fn ok_identity(v: externref) -> externref { + v +} + +pub fn ok_locals() { + let v = unsafe { create_ref() }; + let w = v; // Copy + unsafe { use_ref(w) }; + unsafe { use_ref(v) }; +} + +pub fn ok_fn_ptr(v: externref) -> externref { + let f: extern "C" fn(externref) -> externref = ok_identity; + f(v) +} + +pub fn bad_tuple(v: externref) { + let t = (v, v); + //~^ ERROR wasm `externref` cannot be used inside `(externref, externref)` + //~| ERROR wasm `externref` cannot be used inside `(externref, externref)` +} + +pub fn bad_array(v: externref) { + let a = [v, v]; + //~^ ERROR wasm `externref` cannot be used inside `[externref; 2]` + //~| ERROR wasm `externref` cannot be used inside `[externref; 2]` +} + +pub fn bad_borrow(v: externref) { + let r = &v; + //~^ ERROR wasm `externref` cannot be used inside `&externref` + //~| ERROR wasm `externref` cannot be used inside `&externref` +} + +pub fn generic(t: T) -> T { + t +} + +pub fn bad_generic_arg(v: externref) { + generic(v); + //~^ ERROR wasm `externref` cannot be used as a generic argument +} + +pub fn bad_closure_capture(v: externref) { + let _c = move || v; + //~^ ERROR wasm `externref` cannot be used inside + //~| ERROR wasm `externref` cannot be used inside +} diff --git a/tests/ui/wasm/externref-position-errors.stderr b/tests/ui/wasm/externref-position-errors.stderr new file mode 100644 index 0000000000000..30decea9533be --- /dev/null +++ b/tests/ui/wasm/externref-position-errors.stderr @@ -0,0 +1,80 @@ +error: wasm `externref` cannot be used in a struct field: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:35:12 + | +LL | pub v: externref, + | ^^^^^^^^^ + +error: wasm `externref` cannot be used in a struct field: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:40:23 + | +LL | pub struct BadWrapper(externref); + | ^^^^^^^^^ + +error: wasm `externref` cannot be used inside `&externref`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:28:25 + | +LL | fn bad_ref_param(v: &externref); + | ^^^^^^^^^^ + +error: wasm `externref` cannot be used in a `static`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:30:24 + | +LL | static BAD_STATIC: externref; + | ^^^^^^^^^ + +error: wasm `externref` cannot be used inside `(externref, externref)`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:60:13 + | +LL | let t = (v, v); + | ^^^^^^ + +error: wasm `externref` cannot be used inside `(externref, externref)`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:60:9 + | +LL | let t = (v, v); + | ^ + +error: wasm `externref` cannot be used inside `[externref; 2]`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:66:13 + | +LL | let a = [v, v]; + | ^^^^^^ + +error: wasm `externref` cannot be used inside `[externref; 2]`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:66:9 + | +LL | let a = [v, v]; + | ^ + +error: wasm `externref` cannot be used inside `&externref`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:72:13 + | +LL | let r = &v; + | ^^ + +error: wasm `externref` cannot be used inside `&externref`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:72:9 + | +LL | let r = &v; + | ^ + +error: wasm `externref` cannot be used as a generic argument + --> $DIR/externref-position-errors.rs:82:5 + | +LL | generic(v); + | ^^^^^^^ + +error: wasm `externref` cannot be used inside `{closure@$DIR/externref-position-errors.rs:87:14: 87:21}`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:87:14 + | +LL | let _c = move || v; + | ^^^^^^^^^ + +error: wasm `externref` cannot be used inside `{closure@$DIR/externref-position-errors.rs:87:14: 87:21}`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:87:9 + | +LL | let _c = move || v; + | ^^ + +error: aborting due to 13 previous errors + From 90b0bb44dd2113dc2dd7cd1ab6c3be343f199aeb Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 22 Jul 2026 15:44:37 -0700 Subject: [PATCH 2/7] Move externref definition out of the stdarch subtree Define the type in core's own arch module (shadowing the core_arch glob re-export) since the lang item is tightly coupled to compiler support and stdarch syncs to a separate repository. Also adds a Debug impl, required by core lints. Co-authored-by: Hood Chatham --- library/core/src/arch.rs | 52 +++++++++++++++++++ library/stdarch/crates/core_arch/src/lib.rs | 2 - .../crates/core_arch/src/wasm32/mod.rs | 32 ------------ 3 files changed, 52 insertions(+), 34 deletions(-) diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 737a643ef8659..17d54e17aa8e5 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -12,6 +12,58 @@ #[stable(feature = "simd_arch", since = "1.27.0")] pub use crate::core_arch::arch::*; +/// Platform-specific intrinsics for the `wasm32` platform. +/// +/// This module shadows the `core_arch` re-export to additionally provide the +/// [`externref`](wasm32::externref) lang type, which is defined here (rather +/// than in `stdarch`) as it is tightly coupled to compiler support. +#[cfg(any(target_arch = "wasm32", doc))] +#[doc(cfg(target_arch = "wasm32"))] +#[stable(feature = "simd_wasm32", since = "1.33.0")] +pub mod wasm32 { + #[stable(feature = "simd_wasm32", since = "1.33.0")] + pub use crate::core_arch::arch::wasm32::*; + + /// A WebAssembly `externref`: an opaque, unforgeable reference to a host + /// value, valid only while it remains live on the wasm stack. + /// + /// `externref` is a bare-position-only type: it may appear only as the + /// top-level type of a function parameter, return value or local binding + /// (function pointer signature slots included). It cannot appear inside + /// any other type — no references, aggregates, statics or generic + /// arguments — which is enforced at type-check time. + /// + /// The primary use is typing `extern "C"` imports and exports, where + /// values cross the host boundary directly and identity-preserving, with + /// liveness traced by the host GC: + /// + /// ```ignore (wasm-only) + /// unsafe extern "C" { + /// fn create_ref() -> externref; + /// fn use_ref(v: externref); + /// } + /// ``` + #[allow(non_camel_case_types)] + #[lang = "externref"] + #[non_exhaustive] + #[derive(Copy, Clone)] + #[unstable(feature = "wasm_externref", issue = "none")] + pub struct externref; + + #[unstable(feature = "wasm_externref", issue = "none")] + impl !Send for externref {} + + #[unstable(feature = "wasm_externref", issue = "none")] + impl !Sync for externref {} + + #[unstable(feature = "wasm_externref", issue = "none")] + impl crate::fmt::Debug for externref { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + f.write_str("externref") + } + } +} + /// Inline assembly. /// /// Refer to [Rust By Example] for a usage guide and the [reference] for diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index 0523ce42915c9..0991ec1acf902 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -13,8 +13,6 @@ proc_macro_hygiene, stmt_expr_attributes, core_intrinsics, - lang_items, - negative_impls, no_core, fmt_helpers_for_derive, rustc_attrs, diff --git a/library/stdarch/crates/core_arch/src/wasm32/mod.rs b/library/stdarch/crates/core_arch/src/wasm32/mod.rs index 8a4000d583207..57c9157bede89 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/mod.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/mod.rs @@ -19,38 +19,6 @@ mod memory; #[stable(feature = "simd_wasm32", since = "1.33.0")] pub use self::memory::*; -/// A WebAssembly `externref`: an opaque, unforgeable reference to a host -/// value, valid only while it remains live on the wasm stack. -/// -/// `externref` is a bare-position-only type: it may appear only as the -/// top-level type of a function parameter, return value or local binding -/// (function pointer signature slots included). It cannot appear inside any -/// other type — no references, aggregates, statics or generic arguments — -/// which is enforced at type-check time. -/// -/// The primary use is typing `extern "C"` imports and exports, where values -/// cross the host boundary directly and identity-preserving, with liveness -/// traced by the host GC: -/// -/// ```ignore (wasm-only) -/// unsafe extern "C" { -/// fn create_ref() -> externref; -/// fn use_ref(v: externref); -/// } -/// ``` -#[allow(non_camel_case_types)] -#[lang = "externref"] -#[non_exhaustive] -#[derive(Copy, Clone)] -#[unstable(feature = "wasm_externref", issue = "none")] -pub struct externref; - -#[unstable(feature = "wasm_externref", issue = "none")] -impl !Send for externref {} - -#[unstable(feature = "wasm_externref", issue = "none")] -impl !Sync for externref {} - /// Generates the [`unreachable`] instruction, which causes an unconditional [trap]. /// /// This function is safe to call and immediately aborts the execution. From e6299fbe32c8c6eca3dbe09ff7afdfd9b9846470 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 22 Jul 2026 15:45:15 -0700 Subject: [PATCH 3/7] fmt Co-authored-by: Hood Chatham --- .../src/mono_checks/externref_check.rs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_monomorphize/src/mono_checks/externref_check.rs b/compiler/rustc_monomorphize/src/mono_checks/externref_check.rs index 6d99f44d8afa5..d7d69f1186e82 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/externref_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/externref_check.rs @@ -87,18 +87,17 @@ impl<'tcx> ExternRefVisitor<'tcx> { | BackendRepr::SimdScalableVector { element, .. } => scalar_is_externref(element), BackendRepr::Memory { .. } => { let cx = LayoutCx::new(self.tcx, ty::TypingEnv::fully_monomorphized()); - let fields_contain = |this: &mut Self, layout: TyAndLayout<'tcx>| match layout - .fields - { - FieldsShape::Primitive => false, - FieldsShape::Array { count, .. } => { - count > 0 && this.contains_externref(layout.field(&cx, 0)) - } - FieldsShape::Union(_) | FieldsShape::Arbitrary { .. } => (0..layout - .fields - .count()) - .any(|i| this.contains_externref(layout.field(&cx, i))), - }; + let fields_contain = + |this: &mut Self, layout: TyAndLayout<'tcx>| match layout.fields { + FieldsShape::Primitive => false, + FieldsShape::Array { count, .. } => { + count > 0 && this.contains_externref(layout.field(&cx, 0)) + } + FieldsShape::Union(_) | FieldsShape::Arbitrary { .. } => { + (0..layout.fields.count()) + .any(|i| this.contains_externref(layout.field(&cx, i))) + } + }; if fields_contain(self, layout) { return true; } From aff9ddd297c7ac6bf69aafd82fce20e8d8a261e2 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 22 Jul 2026 17:04:51 -0700 Subject: [PATCH 4/7] Add assembly test for many live externref locals at O0 At opt-level 0 every local gets an alloca, which the wasm backend must promote to wasm locals since reference types cannot enter linear memory. Verifies eight simultaneously-live externrefs lower to .local externref slots with no memory traffic. Co-authored-by: Hood Chatham --- tests/assembly-llvm/wasm-externref-locals.rs | 53 ++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/assembly-llvm/wasm-externref-locals.rs diff --git a/tests/assembly-llvm/wasm-externref-locals.rs b/tests/assembly-llvm/wasm-externref-locals.rs new file mode 100644 index 0000000000000..82687157b1add --- /dev/null +++ b/tests/assembly-llvm/wasm-externref-locals.rs @@ -0,0 +1,53 @@ +//! At `-Copt-level=0` every local gets an alloca; LLVM's wasm backend must +//! promote externref allocas to wasm locals, since reference types cannot be +//! stored to linear memory. Many simultaneously-live externref locals stress +//! this: all values below are created before any is consumed. + +//@ add-minicore +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=0 --target wasm32-unknown-unknown +//@ needs-llvm-components: webassembly + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core, lang_items)] + +extern crate minicore; + +#[lang = "externref"] +#[non_exhaustive] +pub struct externref; + +extern "C" { + fn create_ref() -> externref; + fn use_ref(v: externref); +} + +// CHECK: .functype many_live_refs () -> () +// CHECK: .local {{.*}}externref +#[no_mangle] +pub extern "C" fn many_live_refs() { + unsafe { + let a = create_ref(); + let b = create_ref(); + let c = create_ref(); + let d = create_ref(); + let e = create_ref(); + let f = create_ref(); + let g = create_ref(); + let h = create_ref(); + // Consume in reverse creation order so all eight are live at once. + use_ref(h); + use_ref(g); + use_ref(f); + use_ref(e); + use_ref(d); + use_ref(c); + use_ref(b); + use_ref(a); + } +} +// All externref traffic must go through locals, never linear memory. +// CHECK-NOT: i32.store +// CHECK: end_function From cb624b6f2ec8e25350adf5c81bb820f8eda1d997 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 22 Jul 2026 17:20:07 -0700 Subject: [PATCH 5/7] Fix ISel crash for externref locals under full debuginfo With -Cdebuginfo=2, user-visible SSA locals are spilled to allocas so dbg.declare can reference them. wasm reference types cannot be stored to linear memory, so the debug spill of an externref local hit a fatal 'Cannot select' in the wasm backend (any cargo dev-profile build using externref locals). Skip the debuginfo spill for externref operands, alongside the existing SVE predicate skip; like clang's __externref_t, such variables get no memory-based debug location. Co-authored-by: Hood Chatham --- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 9 ++++ .../wasm-externref-locals-debuginfo.rs | 54 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 tests/assembly-llvm/wasm-externref-locals-debuginfo.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index c586b8080ef30..688be6fe4a596 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -451,6 +451,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + // wasm `externref` values cannot be stored to linear memory, + // so spilling one for debuginfo would be unselectable in the + // wasm backend. They exist only as wasm locals; emit no + // memory-based debug location (clang likewise emits no + // location for `__externref_t` variables). + if bx.tcx().is_externref(operand.layout.ty) { + return; + } + Self::spill_operand_to_stack(*operand, name, bx) } diff --git a/tests/assembly-llvm/wasm-externref-locals-debuginfo.rs b/tests/assembly-llvm/wasm-externref-locals-debuginfo.rs new file mode 100644 index 0000000000000..fbe7e661ede9b --- /dev/null +++ b/tests/assembly-llvm/wasm-externref-locals-debuginfo.rs @@ -0,0 +1,54 @@ +//! Full debuginfo must not force externref locals into memory: debug spills +//! of wasm reference types are unselectable (they cannot be stored to linear +//! memory), so externref locals get no memory-based debug location and must +//! still lower to wasm locals. Regression test for the `-Cdebuginfo=2` ISel +//! crash on any function binding an externref local. + +//@ add-minicore +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=0 -Cdebuginfo=2 --target wasm32-unknown-unknown +//@ needs-llvm-components: webassembly + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core, lang_items)] + +extern crate minicore; + +#[lang = "externref"] +#[non_exhaustive] +pub struct externref; + +extern "C" { + fn create_ref() -> externref; + fn use_ref(v: externref); +} + +// CHECK: .functype many_live_refs () -> () +// CHECK: .local {{.*}}externref +#[no_mangle] +pub extern "C" fn many_live_refs() { + unsafe { + let a = create_ref(); + let b = create_ref(); + let c = create_ref(); + let d = create_ref(); + let e = create_ref(); + let f = create_ref(); + let g = create_ref(); + let h = create_ref(); + // Consume in reverse creation order so all eight are live at once. + use_ref(h); + use_ref(g); + use_ref(f); + use_ref(e); + use_ref(d); + use_ref(c); + use_ref(b); + use_ref(a); + } +} +// All externref traffic must go through locals, never linear memory. +// CHECK-NOT: i32.store +// CHECK: end_function From 19fabb9dffd7233d7b90c995a77c993ff3ea311d Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 22 Jul 2026 20:51:54 -0700 Subject: [PATCH 6/7] Ignore gcc backend for externref ui test The gcc codegen backend cannot target wasm32, so even the minicore auxiliary build panics at backend init in the gcc CI job. Co-authored-by: Hood Chatham --- tests/ui/wasm/externref-position-errors.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/wasm/externref-position-errors.rs b/tests/ui/wasm/externref-position-errors.rs index 8f660c72c60a3..6413d54517431 100644 --- a/tests/ui/wasm/externref-position-errors.rs +++ b/tests/ui/wasm/externref-position-errors.rs @@ -5,6 +5,7 @@ //@ add-minicore //@ compile-flags: --target wasm32-unknown-unknown //@ needs-llvm-components: webassembly +//@ ignore-backends: gcc //@ check-fail #![crate_type = "lib"] From 83464ff2f87575bda196d424a917cf60d8b7bd0c Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 22 Jul 2026 20:52:56 -0700 Subject: [PATCH 7/7] Rebless externref ui test line numbers Co-authored-by: Hood Chatham --- .../ui/wasm/externref-position-errors.stderr | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/ui/wasm/externref-position-errors.stderr b/tests/ui/wasm/externref-position-errors.stderr index 30decea9533be..4be9c6b3fddbf 100644 --- a/tests/ui/wasm/externref-position-errors.stderr +++ b/tests/ui/wasm/externref-position-errors.stderr @@ -1,77 +1,77 @@ error: wasm `externref` cannot be used in a struct field: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:35:12 + --> $DIR/externref-position-errors.rs:36:12 | LL | pub v: externref, | ^^^^^^^^^ error: wasm `externref` cannot be used in a struct field: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:40:23 + --> $DIR/externref-position-errors.rs:41:23 | LL | pub struct BadWrapper(externref); | ^^^^^^^^^ error: wasm `externref` cannot be used inside `&externref`: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:28:25 + --> $DIR/externref-position-errors.rs:29:25 | LL | fn bad_ref_param(v: &externref); | ^^^^^^^^^^ error: wasm `externref` cannot be used in a `static`: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:30:24 + --> $DIR/externref-position-errors.rs:31:24 | LL | static BAD_STATIC: externref; | ^^^^^^^^^ error: wasm `externref` cannot be used inside `(externref, externref)`: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:60:13 + --> $DIR/externref-position-errors.rs:61:13 | LL | let t = (v, v); | ^^^^^^ error: wasm `externref` cannot be used inside `(externref, externref)`: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:60:9 + --> $DIR/externref-position-errors.rs:61:9 | LL | let t = (v, v); | ^ error: wasm `externref` cannot be used inside `[externref; 2]`: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:66:13 + --> $DIR/externref-position-errors.rs:67:13 | LL | let a = [v, v]; | ^^^^^^ error: wasm `externref` cannot be used inside `[externref; 2]`: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:66:9 + --> $DIR/externref-position-errors.rs:67:9 | LL | let a = [v, v]; | ^ error: wasm `externref` cannot be used inside `&externref`: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:72:13 + --> $DIR/externref-position-errors.rs:73:13 | LL | let r = &v; | ^^ error: wasm `externref` cannot be used inside `&externref`: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:72:9 + --> $DIR/externref-position-errors.rs:73:9 | LL | let r = &v; | ^ error: wasm `externref` cannot be used as a generic argument - --> $DIR/externref-position-errors.rs:82:5 + --> $DIR/externref-position-errors.rs:83:5 | LL | generic(v); | ^^^^^^^ -error: wasm `externref` cannot be used inside `{closure@$DIR/externref-position-errors.rs:87:14: 87:21}`: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:87:14 +error: wasm `externref` cannot be used inside `{closure@$DIR/externref-position-errors.rs:88:14: 88:21}`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:88:14 | LL | let _c = move || v; | ^^^^^^^^^ -error: wasm `externref` cannot be used inside `{closure@$DIR/externref-position-errors.rs:87:14: 87:21}`: it may only appear as a bare function parameter, return value or local - --> $DIR/externref-position-errors.rs:87:9 +error: wasm `externref` cannot be used inside `{closure@$DIR/externref-position-errors.rs:88:14: 88:21}`: it may only appear as a bare function parameter, return value or local + --> $DIR/externref-position-errors.rs:88:9 | LL | let _c = move || v; | ^^