From d548747c8531f2f0dfd3921b85aafb09ded27d49 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Thu, 22 Dec 2022 14:36:09 +0300 Subject: [PATCH 1/9] use implied bounds when checking opaque types --- .../rustc_hir_analysis/src/check/check.rs | 17 +++++- compiler/rustc_ty_utils/src/implied_bounds.rs | 13 +++- .../wf-in-associated-type.fail.stderr | 25 ++++++++ .../wf-in-associated-type.rs | 45 ++++++++++++++ .../wf-nested.fail.stderr | 19 ++++++ .../wf-nested.pass_sound.stderr | 14 +++++ tests/ui/type-alias-impl-trait/wf-nested.rs | 60 +++++++++++++++++++ 7 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 tests/ui/type-alias-impl-trait/wf-in-associated-type.fail.stderr create mode 100644 tests/ui/type-alias-impl-trait/wf-in-associated-type.rs create mode 100644 tests/ui/type-alias-impl-trait/wf-nested.fail.stderr create mode 100644 tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr create mode 100644 tests/ui/type-alias-impl-trait/wf-nested.rs diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index c4d4e0d6d78bc..5187e63f8e3a1 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -31,6 +31,7 @@ use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCtxt, TraitEngine, TraitEngineExt as _}; use std::ops::ControlFlow; @@ -222,7 +223,7 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) { if check_opaque_for_cycles(tcx, item.owner_id.def_id, substs, span, &origin).is_err() { return; } - check_opaque_meets_bounds(tcx, item.owner_id.def_id, substs, span, &origin); + check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, &origin); } /// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result @@ -391,7 +392,6 @@ pub(super) fn check_opaque_for_cycles<'tcx>( fn check_opaque_meets_bounds<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, - substs: SubstsRef<'tcx>, span: Span, origin: &hir::OpaqueTyOrigin, ) { @@ -406,6 +406,8 @@ fn check_opaque_meets_bounds<'tcx>( .with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)) .build(); let ocx = ObligationCtxt::new(&infcx); + + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); // `ReErased` regions appear in the "parent_substs" of closures/generators. @@ -448,9 +450,18 @@ fn check_opaque_meets_bounds<'tcx>( match origin { // Checked when type checking the function containing them. hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {} + // Nested opaque types occur only in associated types: + // ` type Opaque = impl Trait<&'static T, AssocTy = impl Nested>; ` + // They can only be referenced as ` as Trait<&'static T>>::AssocTy`. + // We don't have to check them here because their well-formedness follows from the WF of + // the projection input types in the defining- and use-sites. + hir::OpaqueTyOrigin::TyAlias + if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {} // Can have different predicates to their defining use hir::OpaqueTyOrigin::TyAlias => { - let outlives_env = OutlivesEnvironment::new(param_env); + let wf_tys = ocx.assumed_wf_types(param_env, span, def_id); + let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys); + let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); let _ = ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env); } } diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 56d6cc28bc83f..5ca5d14337cf0 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -31,6 +31,18 @@ fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List> { } } DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)), + DefKind::OpaqueTy => match tcx.def_kind(tcx.parent(def_id)) { + DefKind::TyAlias => ty::List::empty(), + DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)), + // Nested opaque types only occur in associated types: + // ` type Opaque = impl Trait<&'static T, AssocTy = impl Nested>; ` + // assumed_wf_types should include those of `Opaque`, `Opaque` itself + // and `&'static T`. + DefKind::OpaqueTy => bug!("unimplemented implied bounds for neseted opaque types"), + def_kind @ _ => { + bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}") + } + }, DefKind::Mod | DefKind::Struct | DefKind::Union @@ -51,7 +63,6 @@ fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List> { | DefKind::ForeignMod | DefKind::AnonConst | DefKind::InlineConst - | DefKind::OpaqueTy | DefKind::ImplTraitPlaceholder | DefKind::Field | DefKind::LifetimeParam diff --git a/tests/ui/type-alias-impl-trait/wf-in-associated-type.fail.stderr b/tests/ui/type-alias-impl-trait/wf-in-associated-type.fail.stderr new file mode 100644 index 0000000000000..9e96323ab54bb --- /dev/null +++ b/tests/ui/type-alias-impl-trait/wf-in-associated-type.fail.stderr @@ -0,0 +1,25 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/wf-in-associated-type.rs:36:23 + | +LL | type Opaque = impl Sized + 'a; + | ^^^^^^^^^^^^^^^ ...so that the type `&'a T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | impl<'a, T: 'a> Trait<'a, T> for () { + | ++++ + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/wf-in-associated-type.rs:36:23 + | +LL | type Opaque = impl Sized + 'a; + | ^^^^^^^^^^^^^^^ ...so that the reference type `&'a T` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound... + | +LL | impl<'a, T: 'a> Trait<'a, T> for () { + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0309`. diff --git a/tests/ui/type-alias-impl-trait/wf-in-associated-type.rs b/tests/ui/type-alias-impl-trait/wf-in-associated-type.rs new file mode 100644 index 0000000000000..31fbef9f78f83 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/wf-in-associated-type.rs @@ -0,0 +1,45 @@ +// WF check for impl Trait in associated type position. +// +// revisions: pass fail +// [pass] check-pass +// [fail] check-fail + +#![feature(impl_trait_in_assoc_type)] + +// The hidden type here (`&'a T`) requires proving `T: 'a`. +// We know it holds because of implied bounds from the impl header. +#[cfg(pass)] +mod pass { + trait Trait { + type Opaque1; + fn constrain_opaque1(req: Req) -> Self::Opaque1; + } + + impl<'a, T> Trait<&'a T> for () { + type Opaque1 = impl IntoIterator; + fn constrain_opaque1(req: &'a T) -> Self::Opaque1 { + [req] + } + } +} + +// The hidden type here (`&'a T`) requires proving `T: 'a`, +// but that is not known to hold in the impl. +#[cfg(fail)] +mod fail { + trait Trait<'a, T> { + type Opaque; + fn constrain_opaque(req: &'a T) -> Self::Opaque; + } + + impl<'a, T> Trait<'a, T> for () { + type Opaque = impl Sized + 'a; + //[fail]~^ ERROR the parameter type `T` may not live long enough + //[fail]~| ERROR the parameter type `T` may not live long enough + fn constrain_opaque(req: &'a T) -> Self::Opaque { + req + } + } +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr b/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr new file mode 100644 index 0000000000000..753a46e882eda --- /dev/null +++ b/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr @@ -0,0 +1,19 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/wf-nested.rs:55:27 + | +LL | type InnerOpaque = impl Sized; + | ^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds... + | +note: ...that is required by this bound + --> $DIR/wf-nested.rs:12:20 + | +LL | struct IsStatic(T); + | ^^^^^^^ +help: consider adding an explicit lifetime bound... + | +LL | type InnerOpaque = impl Sized; + | +++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr b/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr new file mode 100644 index 0000000000000..9ab6685a7f73f --- /dev/null +++ b/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr @@ -0,0 +1,14 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/wf-nested.rs:46:17 + | +LL | let _ = outer.get(); + | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | fn test() { + | +++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/type-alias-impl-trait/wf-nested.rs b/tests/ui/type-alias-impl-trait/wf-nested.rs new file mode 100644 index 0000000000000..de38832948918 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/wf-nested.rs @@ -0,0 +1,60 @@ +// Well-formedness of nested opaque types, i.e. `impl Sized` in +// `type Outer = impl Trait`. +// See the comments below. +// +// revisions: pass pass_sound fail +// [pass] check-pass +// [pass_sound] check-fail +// [fail] check-fail + +#![feature(type_alias_impl_trait)] + +struct IsStatic(T); + +trait Trait { + type Out; + + fn get(&self) -> Result { + Err(()) + } +} + +impl Trait<&'static T> for () { + type Out = IsStatic; +} + +// The hidden type for `impl Sized` is `IsStatic`, which requires `T: 'static`. +// We know it is well-formed because it can *only* be referenced as a projection: +// as Trait<&'static T>>::Out`. +// So any instantiation of the type already requires proving `T: 'static`. +#[cfg(pass)] +mod pass { + use super::*; + type OuterOpaque = impl Trait<&'static T, Out = impl Sized>; + fn define() -> OuterOpaque {} +} + +// Test the soundness of `pass` - We should require `T: 'static` at the use site. +#[cfg(pass_sound)] +mod pass_sound { + use super::*; + type OuterOpaque = impl Trait<&'static T, Out = impl Sized>; + fn define() -> OuterOpaque {} + + fn test() { + let outer = define::(); + let _ = outer.get(); //[pass_sound]~ ERROR `T` may not live long enough + } +} + +// Similar to `pass` but here `impl Sized` can be referenced directly as +// InnerOpaque, so we require an explicit bound `T: 'static`. +#[cfg(fail)] +mod fail { + use super::*; + type InnerOpaque = impl Sized; //[fail]~ ERROR `T` may not live long enough + type OuterOpaque = impl Trait<&'static T, Out = InnerOpaque>; + fn define() -> OuterOpaque {} +} + +fn main() {} From 7c7b22e62cd3aa34ef60ec98b145258caa55261f Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Wed, 3 May 2023 22:22:24 +0000 Subject: [PATCH 2/9] CFI: Fix SIGILL reached via trait objects Fix #106547 by transforming the concrete self into a reference to a trait object before emitting type metadata identifiers for trait methods. --- compiler/rustc_codegen_llvm/src/callee.rs | 4 +- .../src/coverageinfo/mod.rs | 1 + compiler/rustc_codegen_llvm/src/declare.rs | 64 ++++++++++++----- compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 +- compiler/rustc_codegen_llvm/src/mono_item.rs | 2 +- compiler/rustc_symbol_mangling/src/typeid.rs | 24 ++++++- .../src/typeid/typeid_itanium_cxx_abi.rs | 57 ++++++++++++++- compiler/rustc_target/src/abi/call/mod.rs | 8 +-- ...er-cfi-emit-type-metadata-trait-objects.rs | 44 ++++++++++++ ...r-kcfi-emit-type-metadata-trait-objects.rs | 69 +++++++++++++++++++ 10 files changed, 247 insertions(+), 28 deletions(-) create mode 100644 tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs create mode 100644 tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 30a0cf1d01914..4b9ca2e7d19c3 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -94,11 +94,11 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> // LLVM will prefix the name with `__imp_`. Ideally, we'd like the // existing logic below to set the Storage Class, but it has an // exemption for MinGW for backwards compatability. - let llfn = cx.declare_fn(&common::i686_decorated_name(&dllimport, common::is_mingw_gnu_toolchain(&tcx.sess.target), true), fn_abi); + let llfn = cx.declare_fn(&common::i686_decorated_name(&dllimport, common::is_mingw_gnu_toolchain(&tcx.sess.target), true), fn_abi, Some(instance)); unsafe { llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); } llfn } else { - cx.declare_fn(sym, fn_abi) + cx.declare_fn(sym, fn_abi, Some(instance)) }; debug!("get_fn: not casting pointer!"); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 3dc0ac03312e9..cd261293e9b2d 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -207,6 +207,7 @@ fn declare_unused_fn<'tcx>(cx: &CodegenCx<'_, 'tcx>, def_id: DefId) -> Instance< )), ty::List::empty(), ), + None, ); llvm::set_linkage(llfn, llvm::Linkage::PrivateLinkage); diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index cc2a5d158be82..164b12cf8d411 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -19,8 +19,11 @@ use crate::llvm::AttributePlace::Function; use crate::type_::Type; use crate::value::Value; use rustc_codegen_ssa::traits::TypeMembershipMethods; -use rustc_middle::ty::Ty; -use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions}; +use rustc_middle::ty::{Instance, Ty}; +use rustc_symbol_mangling::typeid::{ + kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, + TypeIdOptions, +}; use smallvec::SmallVec; /// Declare a function. @@ -116,7 +119,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { /// /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. - pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Value { + pub fn declare_fn( + &self, + name: &str, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + instance: Option>, + ) -> &'ll Value { debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi); // Function addresses in Rust are never significant, allowing functions to @@ -132,18 +140,35 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { fn_abi.apply_attrs_llfn(self, llfn); if self.tcx.sess.is_sanitizer_cfi_enabled() { - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty()); - self.set_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::GENERALIZE_POINTERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::NORMALIZE_INTEGERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi( - self.tcx, - fn_abi, - TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, - ); - self.add_type_metadata(llfn, typeid); + if let Some(instance) = instance { + let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::empty()); + self.set_type_metadata(llfn, typeid); + let typeid = + typeid_for_instance(self.tcx, &instance, TypeIdOptions::GENERALIZE_POINTERS); + self.add_type_metadata(llfn, typeid); + let typeid = + typeid_for_instance(self.tcx, &instance, TypeIdOptions::NORMALIZE_INTEGERS); + self.add_type_metadata(llfn, typeid); + let typeid = typeid_for_instance( + self.tcx, + &instance, + TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, + ); + self.add_type_metadata(llfn, typeid); + } else { + let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty()); + self.set_type_metadata(llfn, typeid); + let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::GENERALIZE_POINTERS); + self.add_type_metadata(llfn, typeid); + let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::NORMALIZE_INTEGERS); + self.add_type_metadata(llfn, typeid); + let typeid = typeid_for_fnabi( + self.tcx, + fn_abi, + TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, + ); + self.add_type_metadata(llfn, typeid); + } } if self.tcx.sess.is_sanitizer_kcfi_enabled() { @@ -156,8 +181,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { options.insert(TypeIdOptions::NORMALIZE_INTEGERS); } - let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); - self.set_kcfi_type_metadata(llfn, kcfi_typeid); + if let Some(instance) = instance { + let kcfi_typeid = kcfi_typeid_for_instance(self.tcx, &instance, options); + self.set_kcfi_type_metadata(llfn, kcfi_typeid); + } else { + let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); + self.set_kcfi_type_metadata(llfn, kcfi_typeid); + } } llfn diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 00d1796f210bb..4e28034a8507b 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -772,7 +772,7 @@ fn gen_fn<'ll, 'tcx>( ) -> (&'ll Type, &'ll Value) { let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty()); let llty = fn_abi.llvm_type(cx); - let llfn = cx.declare_fn(name, fn_abi); + let llfn = cx.declare_fn(name, fn_abi, None); cx.set_frame_pointer_type(llfn); cx.apply_target_cpu_attr(llfn); // FIXME(eddyb) find a nicer way to do this. diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 59bdc60830f80..e8f8c321510a2 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -51,7 +51,7 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { assert!(!instance.substs.has_infer()); let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); - let lldecl = self.declare_fn(symbol_name, fn_abi); + let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance)); unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) }; let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); base::set_link_section(lldecl, attrs); diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index 81dbff9ea4e40..cda16e3a3f566 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -4,7 +4,7 @@ /// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, /// see design document in the tracking issue #89653. use bitflags::bitflags; -use rustc_middle::ty::{FnSig, Ty, TyCtxt}; +use rustc_middle::ty::{FnSig, Instance, Ty, TyCtxt}; use rustc_target::abi::call::FnAbi; use std::hash::Hasher; use twox_hash::XxHash64; @@ -38,6 +38,15 @@ pub fn typeid_for_fnsig<'tcx>( typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, options) } +/// Returns a type metadata identifier for the specified Instance. +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: &Instance<'tcx>, + options: TypeIdOptions, +) -> String { + typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options) +} + /// Returns a KCFI type metadata identifier for the specified FnAbi. pub fn kcfi_typeid_for_fnabi<'tcx>( tcx: TyCtxt<'tcx>, @@ -63,3 +72,16 @@ pub fn kcfi_typeid_for_fnsig<'tcx>( hash.write(typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, options).as_bytes()); hash.finish() as u32 } + +/// Returns a KCFI type metadata identifier for the specified Instance. +pub fn kcfi_typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: &Instance<'tcx>, + options: TypeIdOptions, +) -> u32 { + // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the + // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) + let mut hash: XxHash64 = Default::default(); + hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); + hash.finish() as u32 +} diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 5310ef26da768..c281aa7e83a85 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -14,8 +14,8 @@ use rustc_errors::DiagnosticMessage; use rustc_hir as hir; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{ - self, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind, TermKind, - Ty, TyCtxt, UintTy, + self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind, + TermKind, Ty, TyCtxt, UintTy, }; use rustc_span::def_id::DefId; use rustc_span::sym; @@ -1010,3 +1010,56 @@ pub fn typeid_for_fnsig<'tcx>( typeid } + +/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with +/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: &Instance<'tcx>, + options: TypeIdOptions, +) -> String { + let fn_abi = tcx + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, ty::List::empty()))) + .unwrap_or_else(|instance| { + bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance) + }); + + // If this instance is a method and self is a reference, get the impl it belongs to + let impl_def_id = tcx.impl_of_method(instance.def_id()); + if impl_def_id.is_some() && !fn_abi.args.is_empty() && fn_abi.args[0].layout.ty.is_ref() { + // If this impl is not an inherent impl, get the trait it implements + if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id.unwrap()) { + // Transform the concrete self into a reference to a trait object + let existential_predicate = trait_ref.map_bound(|trait_ref| { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( + tcx, trait_ref, + )) + }); + let existential_predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy( + existential_predicate.skip_binder(), + )]); + // Is the concrete self mutable? + let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { + tcx.mk_mut_ref( + tcx.lifetimes.re_erased, + tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), + ) + } else { + tcx.mk_imm_ref( + tcx.lifetimes.re_erased, + tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), + ) + }; + + // Replace the concrete self in an fn_abi clone by the reference to a trait object + let mut fn_abi = fn_abi.clone(); + // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the + // other fields are never used. + fn_abi.args[0].layout.ty = self_ty; + + return typeid_for_fnabi(tcx, &fn_abi, options); + } + } + + typeid_for_fnabi(tcx, &fn_abi, options) +} diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 57011aa8a1474..0aacc8ba5de95 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -28,7 +28,7 @@ mod x86; mod x86_64; mod x86_win64; -#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub enum PassMode { /// Ignore the argument. /// @@ -211,7 +211,7 @@ impl Uniform { } } -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub struct CastTarget { pub prefix: [Option; 8], pub rest: Uniform, @@ -458,7 +458,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// Information about how to pass an argument to, /// or return a value from, a function, under some ABI. -#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub struct ArgAbi<'a, Ty> { pub layout: TyAndLayout<'a, Ty>, pub mode: PassMode, @@ -605,7 +605,7 @@ pub enum Conv { /// /// I will do my best to describe this structure, but these /// comments are reverse-engineered and may be inaccurate. -NDM -#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub struct FnAbi<'a, Ty> { /// The LLVM types of each argument. pub args: Box<[ArgAbi<'a, Ty>]>, diff --git a/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs new file mode 100644 index 0000000000000..ab5dcec7936ca --- /dev/null +++ b/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs @@ -0,0 +1,44 @@ +// Verifies that type metadata identifiers for trait objects are emitted correctly. +// +// needs-sanitizer-cfi +// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi + +#![crate_type="lib"] + +trait Trait1 { + fn foo(&self); +} + +struct Type1; + +impl Trait1 for Type1 { + fn foo(&self) { + } +} + +pub fn foo() { + let a = Type1; + a.foo(); + // CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}} + // CHECK: call ::foo +} + +pub fn bar() { + let a = Type1; + let b = &a as &dyn Trait1; + b.foo(); + // CHECK-LABEL: define{{.*}}bar{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0|%1}}, metadata !"[[TYPE1:[[:print:]]+]]") +} + +pub fn baz() { + let a = Type1; + let b = &a as &dyn Trait1; + a.foo(); + b.foo(); + // CHECK-LABEL: define{{.*}}baz{{.*}}!type !{{[0-9]+}} + // CHECK: call ::foo + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0|%1}}, metadata !"[[TYPE1:[[:print:]]+]]") +} + +// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE1]]"} diff --git a/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs new file mode 100644 index 0000000000000..81e0d9344f7e4 --- /dev/null +++ b/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs @@ -0,0 +1,69 @@ +// Verifies that type metadata identifiers for trait objects are emitted correctly. +// +// revisions: aarch64 x86_64 +// [aarch64] compile-flags: --target aarch64-unknown-none +// [aarch64] needs-llvm-components: aarch64 +// [x86_64] compile-flags: --target x86_64-unknown-none +// [x86_64] needs-llvm-components: +// compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 + +#![crate_type="lib"] +#![feature(arbitrary_self_types, no_core, lang_items)] +#![no_core] + +#[lang="sized"] +trait Sized { } +#[lang="copy"] +trait Copy { } +#[lang="receiver"] +trait Receiver { } +#[lang="dispatch_from_dyn"] +trait DispatchFromDyn { } +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} +#[lang = "unsize"] +trait Unsize { } +#[lang = "coerce_unsized"] +pub trait CoerceUnsized { } +impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} +#[lang="freeze"] +trait Freeze { } +#[lang="drop_in_place"] +fn drop_in_place_fn() { } + +trait Trait1 { + fn foo(&self); +} + +struct Type1; + +impl Trait1 for Type1 { + fn foo(&self) { + } +} + +pub fn foo() { + let a = Type1; + a.foo(); + // CHECK-LABEL: define{{.*}}foo{{.*}}!{{|kcfi_type}} !{{[0-9]+}} + // CHECK: call ::foo +} + +pub fn bar() { + let a = Type1; + let b = &a as &dyn Trait1; + b.foo(); + // CHECK-LABEL: define{{.*}}bar{{.*}}!{{|kcfi_type}} !{{[0-9]+}} + // CHECK: call void %0({{\{\}\*|ptr}} align 1 {{%b\.0|%_1}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ] +} + +pub fn baz() { + let a = Type1; + let b = &a as &dyn Trait1; + a.foo(); + b.foo(); + // CHECK-LABEL: define{{.*}}baz{{.*}}!{{|kcfi_type}} !{{[0-9]+}} + // CHECK: call ::foo + // CHECK: call void %0({{\{\}\*|ptr}} align 1 {{%b\.0|%_1}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ] +} + +// CHECK: !{{[0-9]+}} = !{i32 [[TYPE1]]} From 2198faeee2a1e3abf30fa71a032aa76c2545c4e3 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Mon, 8 May 2023 23:38:54 +0300 Subject: [PATCH 3/9] Make `NonUseContext::AscribeUserTy` carry `ty::Variance` --- compiler/rustc_borrowck/src/def_use.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- compiler/rustc_middle/src/mir/visit.rs | 8 ++++---- compiler/rustc_type_ir/src/lib.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs index 74e6ce37e971a..b775739fed2ae 100644 --- a/compiler/rustc_borrowck/src/def_use.rs +++ b/compiler/rustc_borrowck/src/def_use.rs @@ -55,7 +55,7 @@ pub fn categorize(context: PlaceContext) -> Option { // `PlaceMention` and `AscribeUserType` both evaluate the place, which must not // contain dangling references. PlaceContext::NonMutatingUse(NonMutatingUseContext::PlaceMention) | - PlaceContext::NonUse(NonUseContext::AscribeUserTy) | + PlaceContext::NonUse(NonUseContext::AscribeUserTy(_)) | PlaceContext::MutatingUse(MutatingUseContext::AddressOf) | PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf) | diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index dcabeb792be3e..33b24b68f7cfc 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -777,7 +777,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { Inspect | Copy | Move | PlaceMention | SharedBorrow | ShallowBorrow | UniqueBorrow | AddressOf | Projection, ) => ty::Covariant, - PlaceContext::NonUse(AscribeUserTy) => ty::Covariant, + PlaceContext::NonUse(AscribeUserTy(variance)) => variance, } } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 6718605ed0bc4..4b7014e31090b 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -64,7 +64,7 @@ use crate::mir::*; use crate::ty::subst::SubstsRef; -use crate::ty::{CanonicalUserTypeAnnotation, Ty}; +use crate::ty::{self, CanonicalUserTypeAnnotation, Ty}; use rustc_span::Span; macro_rules! make_mir_visitor { @@ -782,12 +782,12 @@ macro_rules! make_mir_visitor { fn super_ascribe_user_ty(&mut self, place: & $($mutability)? Place<'tcx>, - _variance: $(& $mutability)? ty::Variance, + variance: $(& $mutability)? ty::Variance, user_ty: & $($mutability)? UserTypeProjection, location: Location) { self.visit_place( place, - PlaceContext::NonUse(NonUseContext::AscribeUserTy), + PlaceContext::NonUse(NonUseContext::AscribeUserTy($(* &$mutability *)? variance)), location ); self.visit_user_type_projection(user_ty); @@ -1320,7 +1320,7 @@ pub enum NonUseContext { /// Ending a storage live range. StorageDead, /// User type annotation assertions for NLL. - AscribeUserTy, + AscribeUserTy(ty::Variance), /// The data of a user variable, for debug info. VarDebugInfo, } diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 1e91e26e2afe9..da043ba691c64 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -640,7 +640,7 @@ impl UnifyKey for FloatVid { } } -#[derive(Copy, Clone, PartialEq, Decodable, Encodable, Hash, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Decodable, Encodable, Hash, HashStable_Generic)] #[rustc_pass_by_value] pub enum Variance { Covariant, // T <: T iff A <: B -- e.g., function return type From a2fe9935ea6b2cef2cc9b3aca6d1fee3ae15524b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 10 May 2023 19:46:40 +0000 Subject: [PATCH 4/9] Only warn single-use lifetime when the binders match. --- compiler/rustc_resolve/src/late.rs | 16 +++++++++++----- .../ui/associated-inherent-types/issue-109790.rs | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2a8287d5554f8..d7509cbf10e33 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1482,7 +1482,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { if let Some(&(_, res)) = rib.bindings.get(&normalized_ident) { self.record_lifetime_res(lifetime.id, res, LifetimeElisionCandidate::Named); - if let LifetimeRes::Param { param, .. } = res { + if let LifetimeRes::Param { param, binder } = res { match self.lifetime_uses.entry(param) { Entry::Vacant(v) => { debug!("First use of {:?} at {:?}", res, ident.span); @@ -1496,10 +1496,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { LifetimeRibKind::Item | LifetimeRibKind::AnonymousReportError | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), - // An anonymous lifetime is legal here, go ahead. - LifetimeRibKind::AnonymousCreateParameter { .. } => { - Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt }) - } + // An anonymous lifetime is legal here, and bound to the right + // place, go ahead. + LifetimeRibKind::AnonymousCreateParameter { + binder: anon_binder, + .. + } => Some(if binder == anon_binder { + LifetimeUseSet::One { use_span: ident.span, use_ctxt } + } else { + LifetimeUseSet::Many + }), // Only report if eliding the lifetime would have the same // semantics. LifetimeRibKind::Elided(r) => Some(if res == r { diff --git a/tests/ui/associated-inherent-types/issue-109790.rs b/tests/ui/associated-inherent-types/issue-109790.rs index b2be19a28f442..88327f864237a 100644 --- a/tests/ui/associated-inherent-types/issue-109790.rs +++ b/tests/ui/associated-inherent-types/issue-109790.rs @@ -2,6 +2,7 @@ #![feature(inherent_associated_types)] #![allow(incomplete_features)] +#![deny(single_use_lifetimes)] struct Foo(T); From 616fb420986aad3151468c1ca5e874561b452401 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 10 May 2023 10:58:19 +0200 Subject: [PATCH 5/9] Update browser-ui-test version to 0.16.0 --- .../docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 7092c7c46f861..d183d4ace05b9 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.15.0 \ No newline at end of file +0.16.0 \ No newline at end of file From 0630283e9dca4ffe442f4541be8651fa3c676fa2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 10 May 2023 10:56:59 +0200 Subject: [PATCH 6/9] Migrate to 0.16.0 browser-ui-test version --- src/tools/rustdoc-gui/tester.js | 3 +- tests/rustdoc-gui/check-stab-in-docblock.goml | 18 ++++--- tests/rustdoc-gui/codeblock-sub.goml | 2 +- tests/rustdoc-gui/item-info.goml | 4 +- tests/rustdoc-gui/notable-trait.goml | 8 ++-- .../scrape-examples-button-focus.goml | 6 +-- tests/rustdoc-gui/scrape-examples-layout.goml | 7 ++- tests/rustdoc-gui/search-result-display.goml | 4 +- tests/rustdoc-gui/settings.goml | 2 +- tests/rustdoc-gui/sidebar.goml | 14 +++--- tests/rustdoc-gui/source-code-page.goml | 47 ++++++------------- tests/rustdoc-gui/src-font-size.goml | 2 +- tests/rustdoc-gui/struct-fields.goml | 2 +- .../rustdoc-gui/type-declation-overflow.goml | 2 +- 14 files changed, 55 insertions(+), 66 deletions(-) diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js index 692d5e3fcef90..b26480f668b37 100644 --- a/src/tools/rustdoc-gui/tester.js +++ b/src/tools/rustdoc-gui/tester.js @@ -143,7 +143,7 @@ async function runTests(opts, framework_options, files, results, status_bar, sho const tests_queue = []; for (const testPath of files) { - const callback = runTest(testPath, framework_options) + const callback = runTest(testPath, {"options": framework_options}) .then(out => { const [output, nb_failures] = out; results[nb_failures === 0 ? "successful" : "failed"].push({ @@ -323,6 +323,7 @@ async function main(argv) { if (results.failed.length > 0 || results.errored.length > 0) { process.exit(1); } + process.exit(0); } main(process.argv); diff --git a/tests/rustdoc-gui/check-stab-in-docblock.goml b/tests/rustdoc-gui/check-stab-in-docblock.goml index 2f62636211b82..f25c88690e551 100644 --- a/tests/rustdoc-gui/check-stab-in-docblock.goml +++ b/tests/rustdoc-gui/check-stab-in-docblock.goml @@ -7,20 +7,26 @@ set-window-size: (786, 600) // Confirms that there 3 paragraphs. assert-count: (".top-doc .docblock p", 3) // Checking that there is no scrollable content. -store-property: (clientHeight, ".top-doc .docblock p:nth-of-type(1)", "clientHeight") -store-property: (clientWidth, ".top-doc .docblock p:nth-of-type(1)", "clientWidth") +store-property: (".top-doc .docblock p:nth-of-type(1)", { + "clientHeight": clientHeight, + "clientWidth": clientWidth, +}) assert-property: ( ".top-doc .docblock p:nth-of-type(1)", {"scrollHeight": |clientHeight|, "scrollWidth": |clientWidth|}, ) -store-property: (clientHeight, ".top-doc .docblock p:nth-of-type(2)", "clientHeight") -store-property: (clientWidth, ".top-doc .docblock p:nth-of-type(2)", "clientWidth") +store-property: (".top-doc .docblock p:nth-of-type(2)", { + "clientHeight": clientHeight, + "clientWidth": clientWidth, +}) assert-property: ( ".top-doc .docblock p:nth-of-type(2)", {"scrollHeight": |clientHeight|, "scrollWidth": |clientWidth|}, ) -store-property: (clientHeight, ".top-doc .docblock p:nth-of-type(3)", "clientHeight") -store-property: (clientWidth, ".top-doc .docblock p:nth-of-type(3)", "clientWidth") +store-property: (".top-doc .docblock p:nth-of-type(3)", { + "clientHeight": clientHeight, + "clientWidth": clientWidth, +}) assert-property: ( ".top-doc .docblock p:nth-of-type(3)", {"scrollHeight": |clientHeight|, "scrollWidth": |clientWidth|}, diff --git a/tests/rustdoc-gui/codeblock-sub.goml b/tests/rustdoc-gui/codeblock-sub.goml index 03575cc6aaa93..a4b0558765abe 100644 --- a/tests/rustdoc-gui/codeblock-sub.goml +++ b/tests/rustdoc-gui/codeblock-sub.goml @@ -1,5 +1,5 @@ // Test that code blocks nested within do not have a line height of 0. go-to: "file://" + |DOC_PATH| + "/test_docs/codeblock_sub/index.html" -store-property: (codeblock_sub_1, "#codeblock-sub-1", "offsetHeight") +store-property: ("#codeblock-sub-1", {"offsetHeight": codeblock_sub_1}) assert-property-false: ("#codeblock-sub-3", { "offsetHeight": |codeblock_sub_1| }) diff --git a/tests/rustdoc-gui/item-info.goml b/tests/rustdoc-gui/item-info.goml index 60fd7c4e19817..030ff8f8a3e70 100644 --- a/tests/rustdoc-gui/item-info.goml +++ b/tests/rustdoc-gui/item-info.goml @@ -4,8 +4,8 @@ go-to: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html" // We set a fixed size so there is no chance of "random" resize. set-window-size: (1100, 800) // We check that ".item-info" is bigger than its content. -assert-css: (".item-info", {"width": "840px"}) -assert-css: (".item-info .stab", {"width": "289px"}) +assert-size: (".item-info", {"width": 840}) +assert-size: (".item-info .stab", {"width": 289}) assert-position: (".item-info .stab", {"x": 245}) // Now we ensure that they're not rendered on the same line. diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml index f65da57747872..ecb57c274a5d3 100644 --- a/tests/rustdoc-gui/notable-trait.goml +++ b/tests/rustdoc-gui/notable-trait.goml @@ -225,12 +225,12 @@ assert: "#method\.create_an_iterator_from_read .tooltip:focus" // Now we check that the focus isn't given back to the wrong item when opening // another popover. -store-window-property: (scroll, "scrollY") +store-window-property: {"scrollY": scroll} click: "#method\.create_an_iterator_from_read .fn" // We ensure that the scroll position changed. assert-window-property-false: {"scrollY": |scroll|} // Store the new position. -store-window-property: (scroll, "scrollY") +store-window-property: {"scrollY": scroll} click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" wait-for: "//*[@class='tooltip popover']" click: "#settings-menu a" @@ -239,12 +239,12 @@ click: ".search-input" assert-window-property-false: {"scrollY": |scroll|} // Same but with Escape handling. -store-window-property: (scroll, "scrollY") +store-window-property: {"scrollY": scroll} click: "#method\.create_an_iterator_from_read .fn" // We ensure that the scroll position changed. assert-window-property-false: {"scrollY": |scroll|} // Store the new position. -store-window-property: (scroll, "scrollY") +store-window-property: {"scrollY": scroll} click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" wait-for: "//*[@class='tooltip popover']" click: "#settings-menu a" diff --git a/tests/rustdoc-gui/scrape-examples-button-focus.goml b/tests/rustdoc-gui/scrape-examples-button-focus.goml index 77061ea2a3f3f..af4293dfc0057 100644 --- a/tests/rustdoc-gui/scrape-examples-button-focus.goml +++ b/tests/rustdoc-gui/scrape-examples-button-focus.goml @@ -3,7 +3,7 @@ go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html" // The next/prev buttons vertically scroll the code viewport between examples -store-property: (initialScrollTop, ".scraped-example-list > .scraped-example pre", "scrollTop") +store-property: (".scraped-example-list > .scraped-example pre", {"scrollTop": initialScrollTop}) focus: ".scraped-example-list > .scraped-example .next" press-key: "Enter" assert-property-false: (".scraped-example-list > .scraped-example pre", { @@ -16,7 +16,7 @@ assert-property: (".scraped-example-list > .scraped-example pre", { }, NEAR) // The expand button increases the scrollHeight of the minimized code viewport -store-property: (smallOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight") +store-property: (".scraped-example-list > .scraped-example pre", {"offsetHeight": smallOffsetHeight}) assert-property-false: (".scraped-example-list > .scraped-example pre", { "scrollHeight": |smallOffsetHeight| }, NEAR) @@ -25,7 +25,7 @@ press-key: "Enter" assert-property-false: (".scraped-example-list > .scraped-example pre", { "offsetHeight": |smallOffsetHeight| }, NEAR) -store-property: (fullOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight") +store-property: (".scraped-example-list > .scraped-example pre", {"offsetHeight": fullOffsetHeight}) assert-property: (".scraped-example-list > .scraped-example pre", { "scrollHeight": |fullOffsetHeight| }, NEAR) diff --git a/tests/rustdoc-gui/scrape-examples-layout.goml b/tests/rustdoc-gui/scrape-examples-layout.goml index 160056d6d0598..4fc1c1ac065f4 100644 --- a/tests/rustdoc-gui/scrape-examples-layout.goml +++ b/tests/rustdoc-gui/scrape-examples-layout.goml @@ -9,9 +9,8 @@ assert-property-false: ( // Check that examples with very long lines have the same width as ones that don't. store-property: ( - clientWidth, ".more-scraped-examples .scraped-example:nth-child(2) .code-wrapper .src-line-numbers", - "clientWidth" + {"clientWidth": clientWidth}, ) assert-property: ( @@ -40,8 +39,8 @@ assert-property: ( store-value: (offset_y, 4) // First with desktop -assert-position: (".scraped-example .code-wrapper", {"y": 253}) -assert-position: (".scraped-example .code-wrapper .prev", {"y": 253 + |offset_y|}) +assert-position: (".scraped-example .code-wrapper", {"y": 226}) +assert-position: (".scraped-example .code-wrapper .prev", {"y": 226 + |offset_y|}) // Then with mobile set-window-size: (600, 600) diff --git a/tests/rustdoc-gui/search-result-display.goml b/tests/rustdoc-gui/search-result-display.goml index 93c71f23f24e0..ee5598e4b21d0 100644 --- a/tests/rustdoc-gui/search-result-display.goml +++ b/tests/rustdoc-gui/search-result-display.goml @@ -32,8 +32,8 @@ set-text: ( ) // Then we compare again to confirm the height didn't change. -assert-css: ("#crate-search", {"width": "527px"}) -assert-css: (".search-results-title", {"height": "50px", "width": "640px"}) +assert-size: ("#crate-search", {"width": 527}) +assert-size: (".search-results-title", {"height": 50, "width": 640}) // And we check that the `