Skip to content

Rollup of 7 pull requests #110753

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 28 commits into from
Closed
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d5357c1
stdarch: update submodule
KisaragiEffective Apr 13, 2023
d9256f9
Add Lazy{Cell,Lock}::into_inner
SUPERCILEX Dec 26, 2022
41e3cc4
Add some tests around (lack of) object safety of associated types and…
oli-obk Apr 20, 2023
adb5ded
add known-bug test for unsound issue 25860
whtahy Apr 18, 2023
2fb2098
add known-bug test for unsound issue 49206
whtahy Apr 18, 2023
232d685
add known-bug test for unsound issue 57893
whtahy Apr 19, 2023
fbfb620
add known-bug test for unsound issue 84366
whtahy Apr 19, 2023
cac62ab
add known-bug test for unsound issue 84533
whtahy Apr 19, 2023
be68c69
add known-bug test for unsound issue 84591
whtahy Apr 19, 2023
3c5de9a
add known-bug test for unsound issue 85099
whtahy Apr 20, 2023
0e68cbd
Remove useless special case.
cjgillot Apr 15, 2023
629cdb4
Move eval_discriminant.
cjgillot Apr 15, 2023
dd452ae
Simplify logic.
cjgillot Apr 15, 2023
dd78b99
Reduce rightward drift.
cjgillot Apr 15, 2023
3141262
add known-bug test for unsound issue 98117
whtahy Apr 22, 2023
cff6c0e
add known-bug test for unsound issue 100041
whtahy Apr 22, 2023
6f6550f
add known-bug test for unsound issue 100051
whtahy Apr 22, 2023
ebe61ce
add known-bug test for unsound issue 104005
whtahy Apr 22, 2023
7410960
format panic message only once
Apr 23, 2023
49413bb
stdarch: update submodule, take 2
KisaragiEffective Apr 23, 2023
f5e535c
bootstrap: update paths cargo-credential crate
weihanglo Apr 23, 2023
8292aef
Rollup merge of #106152 - SUPERCILEX:lazycell, r=Amanieu
matthiaskrgr Apr 24, 2023
66ee5e0
Rollup merge of #110285 - KisaragiEffective:sync-stdarch, r=Amanieu
matthiaskrgr Apr 24, 2023
0968aa0
Rollup merge of #110480 - whtahy:105107/known-bug-tests-for-unsound-i…
matthiaskrgr Apr 24, 2023
8210191
Rollup merge of #110590 - oli-obk:object_safe_assoc_types, r=jackh726
matthiaskrgr Apr 24, 2023
52ca6e4
Rollup merge of #110685 - cjgillot:clean-dcp, r=oli-obk
matthiaskrgr Apr 24, 2023
453daea
Rollup merge of #110721 - lukas-code:panic-fmt, r=Amanieu
matthiaskrgr Apr 24, 2023
723777d
Rollup merge of #110744 - weihanglo:cargo-credential-install, r=ehuss
matthiaskrgr Apr 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 52 additions & 73 deletions compiler/rustc_mir_transform/src/dataflow_const_prop.rs
Original file line number Diff line number Diff line change
@@ -70,22 +70,6 @@ struct ConstAnalysis<'a, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
}

impl<'tcx> ConstAnalysis<'_, 'tcx> {
fn eval_discriminant(
&self,
enum_ty: Ty<'tcx>,
variant_index: VariantIdx,
) -> Option<ScalarTy<'tcx>> {
if !enum_ty.is_enum() {
return None;
}
let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?;
let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?;
let discr_value = Scalar::try_from_uint(discr.val, discr_layout.size)?;
Some(ScalarTy(discr_value, discr.ty))
}
}

impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
type Value = FlatSet<ScalarTy<'tcx>>;

@@ -126,59 +110,55 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
// we must make sure that all `target as Variant#i` are `Top`.
state.flood(target.as_ref(), self.map());

if let Some(target_idx) = self.map().find(target.as_ref()) {
let (variant_target, variant_index) = match **kind {
AggregateKind::Tuple | AggregateKind::Closure(..) => {
(Some(target_idx), None)
}
AggregateKind::Adt(def_id, variant_index, ..) => {
match self.tcx.def_kind(def_id) {
DefKind::Struct => (Some(target_idx), None),
DefKind::Enum => (
self.map.apply(target_idx, TrackElem::Variant(variant_index)),
Some(variant_index),
),
_ => (None, None),
}
}
_ => (None, None),
};
if let Some(variant_target_idx) = variant_target {
for (field_index, operand) in operands.iter().enumerate() {
if let Some(field) = self.map().apply(
variant_target_idx,
TrackElem::Field(FieldIdx::from_usize(field_index)),
) {
let result = self.handle_operand(operand, state);
state.insert_idx(field, result, self.map());
}
let Some(target_idx) = self.map().find(target.as_ref()) else { return };

let (variant_target, variant_index) = match **kind {
AggregateKind::Tuple | AggregateKind::Closure(..) => (Some(target_idx), None),
AggregateKind::Adt(def_id, variant_index, ..) => {
match self.tcx.def_kind(def_id) {
DefKind::Struct => (Some(target_idx), None),
DefKind::Enum => (
self.map.apply(target_idx, TrackElem::Variant(variant_index)),
Some(variant_index),
),
_ => return,
}
}
if let Some(variant_index) = variant_index
&& let Some(discr_idx) = self.map().apply(target_idx, TrackElem::Discriminant)
{
// We are assigning the discriminant as part of an aggregate.
// This discriminant can only alias a variant field's value if the operand
// had an invalid value for that type.
// Using invalid values is UB, so we are allowed to perform the assignment
// without extra flooding.
let enum_ty = target.ty(self.local_decls, self.tcx).ty;
if let Some(discr_val) = self.eval_discriminant(enum_ty, variant_index) {
state.insert_value_idx(discr_idx, FlatSet::Elem(discr_val), &self.map);
_ => return,
};
if let Some(variant_target_idx) = variant_target {
for (field_index, operand) in operands.iter().enumerate() {
if let Some(field) = self.map().apply(
variant_target_idx,
TrackElem::Field(FieldIdx::from_usize(field_index)),
) {
let result = self.handle_operand(operand, state);
state.insert_idx(field, result, self.map());
}
}
}
if let Some(variant_index) = variant_index
&& let Some(discr_idx) = self.map().apply(target_idx, TrackElem::Discriminant)
{
// We are assigning the discriminant as part of an aggregate.
// This discriminant can only alias a variant field's value if the operand
// had an invalid value for that type.
// Using invalid values is UB, so we are allowed to perform the assignment
// without extra flooding.
let enum_ty = target.ty(self.local_decls, self.tcx).ty;
if let Some(discr_val) = self.eval_discriminant(enum_ty, variant_index) {
state.insert_value_idx(discr_idx, FlatSet::Elem(discr_val), &self.map);
}
}
}
Rvalue::CheckedBinaryOp(op, box (left, right)) => {
// Flood everything now, so we can use `insert_value_idx` directly later.
state.flood(target.as_ref(), self.map());

let target = self.map().find(target.as_ref());
let Some(target) = self.map().find(target.as_ref()) else { return };

let value_target = target
.and_then(|target| self.map().apply(target, TrackElem::Field(0_u32.into())));
let overflow_target = target
.and_then(|target| self.map().apply(target, TrackElem::Field(1_u32.into())));
let value_target = self.map().apply(target, TrackElem::Field(0_u32.into()));
let overflow_target = self.map().apply(target, TrackElem::Field(1_u32.into()));

if value_target.is_some() || overflow_target.is_some() {
let (val, overflow) = self.binary_op(state, *op, left, right);
@@ -377,6 +357,20 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
}
}

fn eval_discriminant(
&self,
enum_ty: Ty<'tcx>,
variant_index: VariantIdx,
) -> Option<ScalarTy<'tcx>> {
if !enum_ty.is_enum() {
return None;
}
let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?;
let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?;
let discr_value = Scalar::try_from_uint(discr.val, discr_layout.size)?;
Some(ScalarTy(discr_value, discr.ty))
}

fn wrap_scalar(&self, scalar: Scalar, ty: Ty<'tcx>) -> FlatSet<ScalarTy<'tcx>> {
FlatSet::Elem(ScalarTy(scalar, ty))
}
@@ -520,21 +514,6 @@ impl<'tcx, 'map, 'a> Visitor<'tcx> for OperandCollector<'tcx, 'map, 'a> {
_ => (),
}
}

fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
match rvalue {
Rvalue::Discriminant(place) => {
match self.state.get_discr(place.as_ref(), self.visitor.map) {
FlatSet::Top => (),
FlatSet::Elem(value) => {
self.visitor.before_effect.insert((location, *place), value);
}
FlatSet::Bottom => (),
}
}
_ => self.super_rvalue(rvalue, location),
}
}
}

struct DummyMachine;
28 changes: 28 additions & 0 deletions library/core/src/cell/lazy.rs
Original file line number Diff line number Diff line change
@@ -63,6 +63,34 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
LazyCell { state: UnsafeCell::new(State::Uninit(f)) }
}

/// Consumes this `LazyCell` returning the stored value.
///
/// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise.
///
/// # Examples
///
/// ```
/// #![feature(lazy_cell)]
/// #![feature(lazy_cell_consume)]
///
/// use std::cell::LazyCell;
///
/// let hello = "Hello, World!".to_string();
///
/// let lazy = LazyCell::new(|| hello.to_uppercase());
///
/// assert_eq!(&*lazy, "HELLO, WORLD!");
/// assert_eq!(LazyCell::into_inner(lazy).ok(), Some("HELLO, WORLD!".to_string()));
/// ```
#[unstable(feature = "lazy_cell_consume", issue = "109736")]
pub fn into_inner(this: Self) -> Result<T, F> {
match this.state.into_inner() {
State::Init(data) => Ok(data),
State::Uninit(f) => Err(f),
State::Poisoned => panic!("LazyCell instance has previously been poisoned"),
}
}

/// Forces the evaluation of this lazy value and returns a reference to
/// the result.
///
26 changes: 13 additions & 13 deletions library/std/src/panicking.rs
Original file line number Diff line number Diff line change
@@ -249,20 +249,20 @@ fn default_hook(info: &PanicInfo<'_>) {
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");

let write = |err: &mut dyn crate::io::Write| {
// Use the panic message directly if available, otherwise take it from
// the payload.
if let Some(msg) = info.message() {
let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}");
// The std panic runtime always sets a `&str` or `String` payload for `panic!` and related
// macros with the formatted message.
// We try using the payload first to avoid formatting the message twice.
let msg: &dyn fmt::Display = if let Some(s) = info.payload().downcast_ref::<&'static str>()
{
s
} else if let Some(s) = info.payload().downcast_ref::<String>() {
s
} else if let Some(msg) = info.message() {
msg
} else {
let msg = if let Some(s) = info.payload().downcast_ref::<&'static str>() {
*s
} else if let Some(s) = info.payload().downcast_ref::<String>() {
&s[..]
} else {
"Box<dyn Any>"
};
let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}");
}
&"Box<dyn Any>"
};
let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}");

static FIRST_PANIC: AtomicBool = AtomicBool::new(true);

38 changes: 37 additions & 1 deletion library/std/src/sync/lazy_lock.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::cell::UnsafeCell;
use crate::fmt;
use crate::mem::ManuallyDrop;
use crate::ops::Deref;
use crate::panic::{RefUnwindSafe, UnwindSafe};
use crate::sync::Once;
use crate::{fmt, ptr};

use super::once::ExclusiveState;

@@ -69,6 +69,42 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) }
}

/// Consumes this `LazyLock` returning the stored value.
///
/// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise.
///
/// # Examples
///
/// ```
/// #![feature(lazy_cell)]
/// #![feature(lazy_cell_consume)]
///
/// use std::sync::LazyLock;
///
/// let hello = "Hello, World!".to_string();
///
/// let lazy = LazyLock::new(|| hello.to_uppercase());
///
/// assert_eq!(&*lazy, "HELLO, WORLD!");
/// assert_eq!(LazyLock::into_inner(lazy).ok(), Some("HELLO, WORLD!".to_string()));
/// ```
#[unstable(feature = "lazy_cell_consume", issue = "109736")]
pub fn into_inner(mut this: Self) -> Result<T, F> {
let state = this.once.state();
match state {
ExclusiveState::Poisoned => panic!("LazyLock instance has previously been poisoned"),
state => {
let this = ManuallyDrop::new(this);
let data = unsafe { ptr::read(&this.data) }.into_inner();
match state {
ExclusiveState::Incomplete => Err(ManuallyDrop::into_inner(unsafe { data.f })),
ExclusiveState::Complete => Ok(ManuallyDrop::into_inner(unsafe { data.value })),
ExclusiveState::Poisoned => unreachable!(),
}
}
}
}

/// Forces the evaluation of this lazy value and
/// returns a reference to result. This is equivalent
/// to the `Deref` impl, but is explicit.
6 changes: 3 additions & 3 deletions src/bootstrap/tool.rs
Original file line number Diff line number Diff line change
@@ -588,18 +588,18 @@ impl Step for Cargo {
if self.target.contains("windows") {
build_cred(
"cargo-credential-wincred",
"src/tools/cargo/crates/credential/cargo-credential-wincred",
"src/tools/cargo/credential/cargo-credential-wincred",
);
}
if self.target.contains("apple-darwin") {
build_cred(
"cargo-credential-macos-keychain",
"src/tools/cargo/crates/credential/cargo-credential-macos-keychain",
"src/tools/cargo/credential/cargo-credential-macos-keychain",
);
}
build_cred(
"cargo-credential-1password",
"src/tools/cargo/crates/credential/cargo-credential-1password",
"src/tools/cargo/credential/cargo-credential-1password",
);
cargo_bin_path
}
15 changes: 15 additions & 0 deletions tests/ui/closures/static-closures-with-nonstatic-return.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// check-pass
// known-bug: #84366

// Should fail. Associated types of 'static types should be `'static`, but
// argument-free closures can be `'static` and return non-`'static` types.

#[allow(dead_code)]
fn foo<'a>() {
let closure = || -> &'a str { "" };
assert_static(closure);
}

fn assert_static<T: 'static>(_: T) {}

fn main() {}
25 changes: 25 additions & 0 deletions tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// check-pass
// known-bug: #57893

// Should fail. Because we see an impl that uses a certain associated type, we
// type-check assuming that impl is used. However, this conflicts with the
// "implicit impl" that we get for trait objects, violating coherence.

trait Object<U> {
type Output;
}

impl<T: ?Sized, U> Object<U> for T {
type Output = U;
}

fn foo<T: ?Sized, U>(x: <T as Object<U>>::Output) -> U {
x
}

#[allow(dead_code)]
fn transmute<T, U>(x: T) -> U {
foo::<dyn Object<U, Output = T>, U>(x)
}

fn main() {}
38 changes: 38 additions & 0 deletions tests/ui/consts/non-sync-references-in-const.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// check-pass
// known-bug: #49206

// Should fail. Compiles and prints 2 identical addresses, which shows 2 threads
// with the same `'static` reference to non-`Sync` struct. The problem is that
// promotion to static does not check if the type is `Sync`.

#[allow(dead_code)]
#[derive(Debug)]
struct Foo {
value: u32,
}

// stable negative impl trick from https://crates.io/crates/negative-impl
// see https://github.com/taiki-e/pin-project/issues/102#issuecomment-540472282
// for details.
struct Wrapper<'a, T>(::std::marker::PhantomData<&'a ()>, T);
unsafe impl<T> Sync for Wrapper<'_, T> where T: Sync {}
unsafe impl<'a> std::marker::Sync for Foo where Wrapper<'a, *const ()>: Sync {}
fn _assert_sync<T: Sync>() {}

fn inspect() {
let foo: &'static Foo = &Foo { value: 1 };
println!(
"I am in thread {:?}, address: {:p}",
std::thread::current().id(),
foo as *const Foo,
);
}

fn main() {
// _assert_sync::<Foo>(); // uncomment this line causes compile error
// "`*const ()` cannot be shared between threads safely"

let handle = std::thread::spawn(inspect);
inspect();
handle.join().unwrap();
}
37 changes: 37 additions & 0 deletions tests/ui/fn/fn-item-lifetime-bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// check-pass
// known-bug: #84533

// Should fail. Lifetimes are checked correctly when `foo` is called, but NOT
// when only the lifetime parameters are instantiated.

use std::marker::PhantomData;

#[allow(dead_code)]
fn foo<'b, 'a>() -> PhantomData<&'b &'a ()> {
PhantomData
}

#[allow(dead_code)]
#[allow(path_statements)]
fn caller<'b, 'a>() {
foo::<'b, 'a>;
}

// In contrast to above, below code correctly does NOT compile.
// fn caller<'b, 'a>() {
// foo::<'b, 'a>();
// }

// error: lifetime may not live long enough
// --> src/main.rs:22:5
// |
// 21 | fn caller<'b, 'a>() {
// | -- -- lifetime `'a` defined here
// | |
// | lifetime `'b` defined here
// 22 | foo::<'b, 'a>();
// | ^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
// |
// = help: consider adding the following bound: `'a: 'b`

fn main() {}
31 changes: 31 additions & 0 deletions tests/ui/fn/implied-bounds-impl-header-projections.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// check-pass
// known-bug: #100051

// Should fail. Implied bounds from projections in impl headers can create
// improper lifetimes. Variant of issue #98543 which was fixed by #99217.

trait Trait {
type Type;
}

impl<T> Trait for T {
type Type = ();
}

trait Extend<'a, 'b> {
fn extend(self, s: &'a str) -> &'b str;
}

impl<'a, 'b> Extend<'a, 'b> for <&'b &'a () as Trait>::Type
where
for<'what, 'ever> &'what &'ever (): Trait,
{
fn extend(self, s: &'a str) -> &'b str {
s
}
}

fn main() {
let y = <() as Extend<'_, '_>>::extend((), &String::from("Hello World"));
println!("{}", y);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// check-pass
// known-bug: #25860

// Should fail. The combination of variance and implied bounds for nested
// references allows us to infer a longer lifetime than we can prove.

static UNIT: &'static &'static () = &&();

fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }

fn bad<'a, T>(x: &'a T) -> &'static T {
let f: fn(_, &'a T) -> &'static T = foo;
f(UNIT, x)
}

fn main() {}
39 changes: 39 additions & 0 deletions tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// check-pass
// known-bug: #84591

// Should fail. Subtrait can incorrectly extend supertrait lifetimes even when
// supertrait has weaker implied bounds than subtrait. Strongly related to
// issue #25860.

trait Subtrait<T>: Supertrait {}
trait Supertrait {
fn action(self);
}

fn subs_to_soup<T, U>(x: T)
where
T: Subtrait<U>,
{
soup(x)
}

fn soup<T: Supertrait>(x: T) {
x.action();
}

impl<'a, 'b: 'a> Supertrait for (&'b str, &mut &'a str) {
fn action(self) {
*self.1 = self.0;
}
}

impl<'a, 'b> Subtrait<&'a &'b str> for (&'b str, &mut &'a str) {}

fn main() {
let mut d = "hi";
{
let x = "Hello World".to_string();
subs_to_soup((x.as_str(), &mut d));
}
println!("{}", d);
}
13 changes: 13 additions & 0 deletions tests/ui/object-safety/assoc_const_bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
trait Foo<T> {
const BAR: bool
where //~ ERROR: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
Self: Sized;
}

trait Cake {}
impl Cake for () {}

fn foo(_: &dyn Foo<()>) {}
fn bar(_: &dyn Foo<i32>) {}

fn main() {}
15 changes: 15 additions & 0 deletions tests/ui/object-safety/assoc_const_bounds.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
--> $DIR/assoc_const_bounds.rs:3:9
|
LL | trait Foo<T> {
| - while parsing this item list starting here
LL | const BAR: bool
| - expected one of 7 possible tokens
LL | where
| ^^^^^ unexpected token
LL | Self: Sized;
LL | }
| - the item list ends here

error: aborting due to previous error

9 changes: 9 additions & 0 deletions tests/ui/object-safety/assoc_const_bounds_sized.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
trait Foo {
const BAR: bool
where //~ ERROR: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
Self: Sized;
}

fn foo(_: &dyn Foo) {}

fn main() {}
15 changes: 15 additions & 0 deletions tests/ui/object-safety/assoc_const_bounds_sized.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
--> $DIR/assoc_const_bounds_sized.rs:3:9
|
LL | trait Foo {
| - while parsing this item list starting here
LL | const BAR: bool
| - expected one of 7 possible tokens
LL | where
| ^^^^^ unexpected token
LL | Self: Sized;
LL | }
| - the item list ends here

error: aborting due to previous error

13 changes: 13 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
trait Foo<T> {
type Bar
where
T: Cake;
}

trait Cake {}
impl Cake for () {}

fn foo(_: &dyn Foo<()>) {} //~ ERROR: the value of the associated type `Bar` (from trait `Foo`) must be specified
fn bar(_: &dyn Foo<i32>) {} //~ ERROR: the value of the associated type `Bar` (from trait `Foo`) must be specified

fn main() {}
21 changes: 21 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0191]: the value of the associated type `Bar` (from trait `Foo`) must be specified
--> $DIR/assoc_type_bounds.rs:10:16
|
LL | type Bar
| -------- `Bar` defined here
...
LL | fn foo(_: &dyn Foo<()>) {}
| ^^^^^^^ help: specify the associated type: `Foo<(), Bar = Type>`

error[E0191]: the value of the associated type `Bar` (from trait `Foo`) must be specified
--> $DIR/assoc_type_bounds.rs:11:16
|
LL | type Bar
| -------- `Bar` defined here
...
LL | fn bar(_: &dyn Foo<i32>) {}
| ^^^^^^^^ help: specify the associated type: `Foo<i32, Bar = Type>`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0191`.
13 changes: 13 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
trait Foo<T> {
type Bar
where
Self: Foo<()>;
}

trait Cake {}
impl Cake for () {}

fn foo(_: &dyn Foo<()>) {} //~ ERROR: the value of the associated type `Bar` (from trait `Foo`) must be specified
fn bar(_: &dyn Foo<i32>) {} //~ ERROR: the value of the associated type `Bar` (from trait `Foo`) must be specified

fn main() {}
21 changes: 21 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0191]: the value of the associated type `Bar` (from trait `Foo`) must be specified
--> $DIR/assoc_type_bounds2.rs:10:16
|
LL | type Bar
| -------- `Bar` defined here
...
LL | fn foo(_: &dyn Foo<()>) {}
| ^^^^^^^ help: specify the associated type: `Foo<(), Bar = Type>`

error[E0191]: the value of the associated type `Bar` (from trait `Foo`) must be specified
--> $DIR/assoc_type_bounds2.rs:11:16
|
LL | type Bar
| -------- `Bar` defined here
...
LL | fn bar(_: &dyn Foo<i32>) {}
| ^^^^^^^^ help: specify the associated type: `Foo<i32, Bar = Type>`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0191`.
9 changes: 9 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds_sized.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
trait Foo {
type Bar
where
Self: Sized;
}

fn foo(_: &dyn Foo) {} //~ ERROR: the value of the associated type `Bar` (from trait `Foo`) must be specified

fn main() {}
12 changes: 12 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds_sized.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0191]: the value of the associated type `Bar` (from trait `Foo`) must be specified
--> $DIR/assoc_type_bounds_sized.rs:7:16
|
LL | type Bar
| -------- `Bar` defined here
...
LL | fn foo(_: &dyn Foo) {}
| ^^^ help: specify the associated type: `Foo<Bar = Type>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0191`.
21 changes: 21 additions & 0 deletions tests/ui/panics/fmt-only-once.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// run-fail
// check-run-results
// exec-env:RUST_BACKTRACE=0

// Test that we format the panic message only once.
// Regression test for https://github.com/rust-lang/rust/issues/110717

use std::fmt;

struct PrintOnFmt;

impl fmt::Display for PrintOnFmt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
eprintln!("fmt");
f.write_str("PrintOnFmt")
}
}

fn main() {
panic!("{}", PrintOnFmt)
}
3 changes: 3 additions & 0 deletions tests/ui/panics/fmt-only-once.run.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fmt
thread 'main' panicked at 'PrintOnFmt', $DIR/fmt-only-once.rs:20:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
68 changes: 68 additions & 0 deletions tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// check-pass
// known-bug: #85099

// Should fail. Can coerce `Pin<T>` into `Pin<U>` where
// `T: Deref<Target: Unpin>` and `U: Deref<Target: !Unpin>`, using the
// `CoerceUnsized` impl on `Pin` and an unorthodox `DerefMut` impl for
// `Pin<&_>`.

// This should not be allowed, since one can unpin `T::Target` (since it is
// `Unpin`) to gain unpinned access to the previously pinned `U::Target` (which
// is `!Unpin`) and then move it.

use std::{
cell::{RefCell, RefMut},
future::Future,
ops::DerefMut,
pin::Pin,
};

struct SomeLocalStruct<'a, Fut>(&'a RefCell<Fut>);

trait SomeTrait<'a, Fut> {
#[allow(clippy::mut_from_ref)]
fn deref_helper(&self) -> &mut (dyn SomeTrait<'a, Fut> + 'a) {
unimplemented!()
}
fn downcast(self: Pin<&mut Self>) -> Pin<&mut Fut> {
unimplemented!()
}
}

impl<'a, Fut: Future<Output = ()>> SomeTrait<'a, Fut> for SomeLocalStruct<'a, Fut> {
fn deref_helper(&self) -> &mut (dyn SomeTrait<'a, Fut> + 'a) {
let x = Box::new(self.0.borrow_mut());
let x: &'a mut RefMut<'a, Fut> = Box::leak(x);
&mut **x
}
}
impl<'a, Fut: Future<Output = ()>> SomeTrait<'a, Fut> for Fut {
fn downcast(self: Pin<&mut Self>) -> Pin<&mut Fut> {
self
}
}

impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> {
fn deref_mut<'c>(
self: &'c mut Pin<&'b dyn SomeTrait<'a, Fut>>,
) -> &'c mut (dyn SomeTrait<'a, Fut> + 'b) {
self.deref_helper()
}
}

// obviously a "working" function with this signature is problematic
pub fn unsound_pin<Fut: Future<Output = ()>>(
fut: Fut,
callback: impl FnOnce(Pin<&mut Fut>),
) -> Fut {
let cell = RefCell::new(fut);
let s: &SomeLocalStruct<'_, Fut> = &SomeLocalStruct(&cell);
let p: Pin<Pin<&SomeLocalStruct<'_, Fut>>> = Pin::new(Pin::new(s));
let mut p: Pin<Pin<&dyn SomeTrait<'_, Fut>>> = p;
let r: Pin<&mut dyn SomeTrait<'_, Fut>> = p.as_mut();
let f: Pin<&mut Fut> = r.downcast();
callback(f);
cell.into_inner()
}

fn main() {}
37 changes: 37 additions & 0 deletions tests/ui/wf/wf-in-fn-type-implicit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// check-pass
// known-bug: #104005

// Should fail. Function type parameters with implicit type annotations are not
// checked for well-formedness, which allows incorrect borrowing.

// In contrast, user annotations are always checked for well-formedness, and the
// commented code below is correctly rejected by the borrow checker.

use std::fmt::Display;

trait Displayable {
fn display(self) -> Box<dyn Display>;
}

impl<T: Display> Displayable for (T, Option<&'static T>) {
fn display(self) -> Box<dyn Display> {
Box::new(self.0)
}
}

fn extend_lt<T, U>(val: T) -> Box<dyn Display>
where
(T, Option<U>): Displayable,
{
Displayable::display((val, None))
}

fn main() {
// *incorrectly* compiles
let val = extend_lt(&String::from("blah blah blah"));
println!("{}", val);

// *correctly* fails to compile
// let val = extend_lt::<_, &_>(&String::from("blah blah blah"));
// println!("{}", val);
}
23 changes: 23 additions & 0 deletions tests/ui/wf/wf-in-where-clause-static.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// check-pass
// known-bug: #98117

// Should fail. Functions are responsible for checking the well-formedness of
// their own where clauses, so this should fail and require an explicit bound
// `T: 'static`.

use std::fmt::Display;

trait Static: 'static {}
impl<T> Static for &'static T {}

fn foo<S: Display>(x: S) -> Box<dyn Display>
where
&'static S: Static,
{
Box::new(x)
}

fn main() {
let s = foo(&String::from("blah blah blah"));
println!("{}", s);
}
19 changes: 19 additions & 0 deletions tests/ui/wf/wf-normalization-sized.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// check-pass
// known-bug: #100041

// Should fail. Normalization can bypass well-formedness checking.
// `[[[[[[u8]]]]]]` is not a well-formed type since size of type `[u8]` cannot
// be known at compile time (since `Sized` is not implemented for `[u8]`).

trait WellUnformed {
type RequestNormalize;
}

impl<T: ?Sized> WellUnformed for T {
type RequestNormalize = ();
}

const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
const _: <Vec<str> as WellUnformed>::RequestNormalize = ();

fn main() {}