Skip to content

Commit

Permalink
Auto merge of #113169 - oli-obk:tait_must_be_constrained_if_in_sig, r…
Browse files Browse the repository at this point in the history
…=lcnr

Tait must be constrained if in sig

r? `@compiler-errors`

kind of reverts #62423, but that PR only removed cycles in cases that we want to forbid now anyway.

see https://rust-lang.zulipchat.com/#narrow/stream/315482-t-compiler.2Fetc.2Fopaque-types/topic/lcnr.20oli.20meeting/near/370712246 for related discussion and motivating example

a TAIT showing up in a signature doesn't just mean it can register a hidden type, but that it must.

This is the [design the types team decided upon](https://hackmd.io/QOsEaEJtQK-XDS_xN4UyQA#Proposal-preferred) for the following reasons

* avoids a hypothetical situation where getting smarter in trait solving could cause new cycle errors or new inference errors to show up, where today something is treated as opaque.
* avoids having the situation where a (minor?) change to a function body causes an error, because its TAIT usage suddenly isn't "opaque enough" anymore.
* avoids having to explain why in some cases you need to put a Tait into a tiny module together with its defining usages. Now you basically gotta always do this, the moment a Tait is in a signature that isn't in the defining scope.
* avoids false-cycle errors
* anything diverging from this pattern needs to either
    * move the TAIT declaration and defining function into their own helper sub module
    * use the attributes we'll provide in the future to explicitly opt in or out of being in the defining scope

fixes #117861

reverts #125362 cc `@Nilstrieb` `@joboet`
  • Loading branch information
bors committed Jun 12, 2024
2 parents bdb1b7f + 85f2eca commit 02c7a59
Show file tree
Hide file tree
Showing 123 changed files with 1,637 additions and 742 deletions.
23 changes: 13 additions & 10 deletions compiler/rustc_codegen_cranelift/example/issue-72793.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@

#![feature(type_alias_impl_trait)]

trait T {
type Item;
}
mod helper {
pub trait T {
type Item;
}

type Alias<'a> = impl T<Item = &'a ()>;
pub type Alias<'a> = impl T<Item = &'a ()>;

struct S;
impl<'a> T for &'a S {
type Item = &'a ();
}
struct S;
impl<'a> T for &'a S {
type Item = &'a ();
}

fn filter_positive<'a>() -> Alias<'a> {
&S
pub fn filter_positive<'a>() -> Alias<'a> {
&S
}
}
use helper::*;

fn with_positive(fun: impl Fn(Alias<'_>)) {
fun(filter_positive());
Expand Down
27 changes: 16 additions & 11 deletions compiler/rustc_data_structures/src/obligation_forest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,6 @@ pub enum ProcessResult<O, E> {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
struct ObligationTreeId(usize);

type ObligationTreeIdGenerator = impl Iterator<Item = ObligationTreeId>;

pub struct ObligationForest<O: ForestObligation> {
/// The list of obligations. In between calls to [Self::process_obligations],
/// this list only contains nodes in the `Pending` or `Waiting` state.
Expand Down Expand Up @@ -310,18 +308,25 @@ pub struct Error<O, E> {
pub backtrace: Vec<O>,
}

impl<O: ForestObligation> ObligationForest<O> {
pub fn new() -> ObligationForest<O> {
ObligationForest {
nodes: vec![],
done_cache: Default::default(),
active_cache: Default::default(),
reused_node_vec: vec![],
obligation_tree_id_generator: (0..).map(ObligationTreeId),
error_cache: Default::default(),
mod helper {
use super::*;
pub type ObligationTreeIdGenerator = impl Iterator<Item = ObligationTreeId>;
impl<O: ForestObligation> ObligationForest<O> {
pub fn new() -> ObligationForest<O> {
ObligationForest {
nodes: vec![],
done_cache: Default::default(),
active_cache: Default::default(),
reused_node_vec: vec![],
obligation_tree_id_generator: (0..).map(ObligationTreeId),
error_cache: Default::default(),
}
}
}
}
use helper::*;

impl<O: ForestObligation> ObligationForest<O> {
/// Returns the total number of nodes in the forest that have not
/// yet been fully resolved.
pub fn len(&self) -> usize {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0792.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ type Foo<T> = impl std::fmt::Debug;
fn foo<U>() -> Foo<U> {
5u32
}
fn main() {}
```

This means that no matter the generic parameter to `foo`,
Expand All @@ -57,4 +59,6 @@ type Foo<T: Debug> = impl Debug;
fn foo<U: Debug>() -> Foo<U> {
Vec::<U>::new()
}
fn main() {}
```
4 changes: 4 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,10 @@ hir_analysis_static_specialize = cannot specialize on `'static` lifetime
hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature
.note = this item must mention the opaque type in its signature in order to be able to register hidden types
hir_analysis_tait_forward_compat2 = item does not constrain `{$opaque_type}`, but has it in its signature
.note = consider moving the opaque type's declaration and defining uses into a separate module
.opaque = this opaque type is in the signature
hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]`
hir_analysis_too_large_static = extern static is too large for the current architecture
Expand Down
15 changes: 13 additions & 2 deletions compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP};

use crate::errors::{TaitForwardCompat, TypeOf, UnconstrainedOpaqueType};
use crate::errors::{TaitForwardCompat, TaitForwardCompat2, TypeOf, UnconstrainedOpaqueType};

pub fn test_opaque_hidden_types(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
let mut res = Ok(());
Expand Down Expand Up @@ -229,13 +229,14 @@ impl TaitConstraintLocator<'_> {
return;
}

let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);

let mut constrained = false;
for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
if opaque_type_key.def_id != self.def_id {
continue;
}
constrained = true;
let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);

if !opaque_types_defined_by.contains(&self.def_id) {
self.tcx.dcx().emit_err(TaitForwardCompat {
Expand All @@ -259,6 +260,16 @@ impl TaitConstraintLocator<'_> {

if !constrained {
debug!("no constraints in typeck results");
if opaque_types_defined_by.contains(&self.def_id) {
self.tcx.dcx().emit_err(TaitForwardCompat2 {
span: self
.tcx
.def_ident_span(item_def_id)
.unwrap_or_else(|| self.tcx.def_span(item_def_id)),
opaque_type_span: self.tcx.def_span(self.def_id),
opaque_type: self.tcx.def_path_str(self.def_id),
});
}
return;
};

Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,17 @@ pub struct TaitForwardCompat {
pub item_span: Span,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_tait_forward_compat2)]
#[note]
pub struct TaitForwardCompat2 {
#[primary_span]
pub span: Span,
#[note(hir_analysis_opaque)]
pub opaque_type_span: Span,
pub opaque_type: String,
}

pub struct MissingTypeParams {
pub span: Span,
pub def_span: Span,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ declare_lint! {
/// fn test() -> impl Trait<Assoc = Tait> {
/// 42
/// }
///
/// fn main() {}
/// ```
///
/// {{produces}}
Expand Down
151 changes: 81 additions & 70 deletions compiler/rustc_middle/src/mir/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,6 @@ pub struct Terminator<'tcx> {
pub kind: TerminatorKind<'tcx>,
}

pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
pub type SuccessorsMut<'a> = impl DoubleEndedIterator<Item = &'a mut BasicBlock> + 'a;

impl<'tcx> Terminator<'tcx> {
#[inline]
pub fn successors(&self) -> Successors<'_> {
Expand Down Expand Up @@ -407,81 +404,95 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
}
}

#[inline]
pub fn successors(&self) -> Successors<'_> {
use self::TerminatorKind::*;
match *self {
Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
| Yield { resume: ref t, drop: Some(u), .. }
| Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
| Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
| FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
slice::from_ref(t).into_iter().copied().chain(Some(u))
}
Goto { target: ref t }
| Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. }
| Call { target: Some(ref t), unwind: _, .. }
| Yield { resume: ref t, drop: None, .. }
| Drop { target: ref t, unwind: _, .. }
| Assert { target: ref t, unwind: _, .. }
| FalseUnwind { real_target: ref t, unwind: _ } => {
slice::from_ref(t).into_iter().copied().chain(None)
}
UnwindResume
| UnwindTerminate(_)
| CoroutineDrop
| Return
| Unreachable
| Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None),
InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
targets.iter().copied().chain(Some(u))
}
InlineAsm { ref targets, unwind: _, .. } => targets.iter().copied().chain(None),
SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None),
FalseEdge { ref real_target, imaginary_target } => {
slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target))
pub use helper::*;

mod helper {
use super::*;
pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
pub type SuccessorsMut<'a> = impl DoubleEndedIterator<Item = &'a mut BasicBlock> + 'a;
impl<'tcx> TerminatorKind<'tcx> {
#[inline]
pub fn successors(&self) -> Successors<'_> {
use self::TerminatorKind::*;
match *self {
Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
| Yield { resume: ref t, drop: Some(u), .. }
| Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
| Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
| FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
slice::from_ref(t).into_iter().copied().chain(Some(u))
}
Goto { target: ref t }
| Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. }
| Call { target: Some(ref t), unwind: _, .. }
| Yield { resume: ref t, drop: None, .. }
| Drop { target: ref t, unwind: _, .. }
| Assert { target: ref t, unwind: _, .. }
| FalseUnwind { real_target: ref t, unwind: _ } => {
slice::from_ref(t).into_iter().copied().chain(None)
}
UnwindResume
| UnwindTerminate(_)
| CoroutineDrop
| Return
| Unreachable
| Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None),
InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
targets.iter().copied().chain(Some(u))
}
InlineAsm { ref targets, unwind: _, .. } => targets.iter().copied().chain(None),
SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None),
FalseEdge { ref real_target, imaginary_target } => {
slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target))
}
}
}
}

#[inline]
pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
use self::TerminatorKind::*;
match *self {
Call { target: Some(ref mut t), unwind: UnwindAction::Cleanup(ref mut u), .. }
| Yield { resume: ref mut t, drop: Some(ref mut u), .. }
| Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
| Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
| FalseUnwind { real_target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u) } => {
slice::from_mut(t).into_iter().chain(Some(u))
}
Goto { target: ref mut t }
| Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. }
| Call { target: Some(ref mut t), unwind: _, .. }
| Yield { resume: ref mut t, drop: None, .. }
| Drop { target: ref mut t, unwind: _, .. }
| Assert { target: ref mut t, unwind: _, .. }
| FalseUnwind { real_target: ref mut t, unwind: _ } => {
slice::from_mut(t).into_iter().chain(None)
}
UnwindResume
| UnwindTerminate(_)
| CoroutineDrop
| Return
| Unreachable
| Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None),
InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => {
targets.iter_mut().chain(Some(u))
}
InlineAsm { ref mut targets, unwind: _, .. } => targets.iter_mut().chain(None),
SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None),
FalseEdge { ref mut real_target, ref mut imaginary_target } => {
slice::from_mut(real_target).into_iter().chain(Some(imaginary_target))
#[inline]
pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
use self::TerminatorKind::*;
match *self {
Call {
target: Some(ref mut t), unwind: UnwindAction::Cleanup(ref mut u), ..
}
| Yield { resume: ref mut t, drop: Some(ref mut u), .. }
| Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
| Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
| FalseUnwind {
real_target: ref mut t,
unwind: UnwindAction::Cleanup(ref mut u),
} => slice::from_mut(t).into_iter().chain(Some(u)),
Goto { target: ref mut t }
| Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. }
| Call { target: Some(ref mut t), unwind: _, .. }
| Yield { resume: ref mut t, drop: None, .. }
| Drop { target: ref mut t, unwind: _, .. }
| Assert { target: ref mut t, unwind: _, .. }
| FalseUnwind { real_target: ref mut t, unwind: _ } => {
slice::from_mut(t).into_iter().chain(None)
}
UnwindResume
| UnwindTerminate(_)
| CoroutineDrop
| Return
| Unreachable
| Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None),
InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => {
targets.iter_mut().chain(Some(u))
}
InlineAsm { ref mut targets, unwind: _, .. } => targets.iter_mut().chain(None),
SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None),
FalseEdge { ref mut real_target, ref mut imaginary_target } => {
slice::from_mut(real_target).into_iter().chain(Some(imaginary_target))
}
}
}
}
}

impl<'tcx> TerminatorKind<'tcx> {
#[inline]
pub fn unwind(&self) -> Option<&UnwindAction> {
match *self {
Expand Down
41 changes: 41 additions & 0 deletions library/core/src/internal_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,47 @@ macro_rules! forward_ref_op_assign {
}
}

/// Create a zero-size type similar to a closure type, but named.
macro_rules! impl_fn_for_zst {
($(
$( #[$attr: meta] )*
struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn =
|$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty
$body: block;
)+) => {
$(
$( #[$attr] )*
struct $Name;

impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name {
#[inline]
extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy {
$body
}
}

impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name {
#[inline]
extern "rust-call" fn call_mut(
&mut self,
($( $arg, )*): ($( $ArgTy, )*)
) -> $ReturnTy {
Fn::call(&*self, ($( $arg, )*))
}
}

impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name {
type Output = $ReturnTy;

#[inline]
extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy {
Fn::call(&self, ($( $arg, )*))
}
}
)+
}
}

/// A macro for defining `#[cfg]` if-else statements.
///
/// `cfg_if` is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade
Expand Down
Loading

0 comments on commit 02c7a59

Please sign in to comment.