mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Auto merge of #126655 - jieyouxu:rollup-z7k1k6l, r=jieyouxu
Rollup of 10 pull requests Successful merges: - #124135 (delegation: Implement glob delegation) - #125078 (fix: break inside async closure has incorrect span for enclosing closure) - #125293 (Place tail expression behind terminating scope) - #126422 (Suggest using a standalone doctest for non-local impl defs) - #126493 (safe transmute: support non-ZST, variantful, uninhabited enums) - #126504 (Sync fuchsia test runner with clang test runner) - #126558 (hir_typeck: be more conservative in making "note caller chooses ty param" note) - #126586 (Add `@badboy` and `@BlackHoleFox` as Mac Catalyst maintainers) - #126615 (Add `rustc-ice*` to `.gitignore`) - #126632 (Replace `move||` with `move ||`) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
a1ca449981
1
.gitignore
vendored
1
.gitignore
vendored
@ -52,6 +52,7 @@ build/
|
||||
/src/tools/x/target
|
||||
# Created by default with `src/ci/docker/run.sh`
|
||||
/obj/
|
||||
/rustc-ice*
|
||||
|
||||
## Temporary files
|
||||
*~
|
||||
|
@ -3161,13 +3161,16 @@ pub struct Delegation {
|
||||
pub path: Path,
|
||||
pub rename: Option<Ident>,
|
||||
pub body: Option<P<Block>>,
|
||||
/// The item was expanded from a glob delegation item.
|
||||
pub from_glob: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub struct DelegationMac {
|
||||
pub qself: Option<P<QSelf>>,
|
||||
pub prefix: Path,
|
||||
pub suffixes: ThinVec<(Ident, Option<Ident>)>,
|
||||
// Some for list delegation, and None for glob delegation.
|
||||
pub suffixes: Option<ThinVec<(Ident, Option<Ident>)>>,
|
||||
pub body: Option<P<Block>>,
|
||||
}
|
||||
|
||||
@ -3294,7 +3297,7 @@ pub enum ItemKind {
|
||||
///
|
||||
/// E.g. `reuse <Type as Trait>::name { target_expr_template }`.
|
||||
Delegation(Box<Delegation>),
|
||||
/// A list delegation item (`reuse prefix::{a, b, c}`).
|
||||
/// A list or glob delegation item (`reuse prefix::{a, b, c}`, `reuse prefix::*`).
|
||||
/// Treated similarly to a macro call and expanded early.
|
||||
DelegationMac(Box<DelegationMac>),
|
||||
}
|
||||
@ -3375,7 +3378,7 @@ pub enum AssocItemKind {
|
||||
MacCall(P<MacCall>),
|
||||
/// An associated delegation item.
|
||||
Delegation(Box<Delegation>),
|
||||
/// An associated delegation item list.
|
||||
/// An associated list or glob delegation item.
|
||||
DelegationMac(Box<DelegationMac>),
|
||||
}
|
||||
|
||||
|
@ -1162,7 +1162,14 @@ impl NoopVisitItemKind for ItemKind {
|
||||
}
|
||||
ItemKind::MacCall(m) => vis.visit_mac_call(m),
|
||||
ItemKind::MacroDef(def) => vis.visit_macro_def(def),
|
||||
ItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => {
|
||||
ItemKind::Delegation(box Delegation {
|
||||
id,
|
||||
qself,
|
||||
path,
|
||||
rename,
|
||||
body,
|
||||
from_glob: _,
|
||||
}) => {
|
||||
vis.visit_id(id);
|
||||
vis.visit_qself(qself);
|
||||
vis.visit_path(path);
|
||||
@ -1176,12 +1183,14 @@ impl NoopVisitItemKind for ItemKind {
|
||||
ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
vis.visit_qself(qself);
|
||||
vis.visit_path(prefix);
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
vis.visit_ident(ident);
|
||||
if let Some(rename) = rename {
|
||||
vis.visit_ident(rename);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(body) = body {
|
||||
vis.visit_block(body);
|
||||
}
|
||||
@ -1218,7 +1227,14 @@ impl NoopVisitItemKind for AssocItemKind {
|
||||
visit_opt(ty, |ty| visitor.visit_ty(ty));
|
||||
}
|
||||
AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac),
|
||||
AssocItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => {
|
||||
AssocItemKind::Delegation(box Delegation {
|
||||
id,
|
||||
qself,
|
||||
path,
|
||||
rename,
|
||||
body,
|
||||
from_glob: _,
|
||||
}) => {
|
||||
visitor.visit_id(id);
|
||||
visitor.visit_qself(qself);
|
||||
visitor.visit_path(path);
|
||||
@ -1232,12 +1248,14 @@ impl NoopVisitItemKind for AssocItemKind {
|
||||
AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
visitor.visit_qself(qself);
|
||||
visitor.visit_path(prefix);
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
visitor.visit_ident(ident);
|
||||
if let Some(rename) = rename {
|
||||
visitor.visit_ident(rename);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(body) = body {
|
||||
visitor.visit_block(body);
|
||||
}
|
||||
|
@ -408,7 +408,14 @@ impl WalkItemKind for ItemKind {
|
||||
}
|
||||
ItemKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)),
|
||||
ItemKind::MacroDef(ts) => try_visit!(visitor.visit_mac_def(ts, item.id)),
|
||||
ItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => {
|
||||
ItemKind::Delegation(box Delegation {
|
||||
id,
|
||||
qself,
|
||||
path,
|
||||
rename,
|
||||
body,
|
||||
from_glob: _,
|
||||
}) => {
|
||||
if let Some(qself) = qself {
|
||||
try_visit!(visitor.visit_ty(&qself.ty));
|
||||
}
|
||||
@ -421,12 +428,14 @@ impl WalkItemKind for ItemKind {
|
||||
try_visit!(visitor.visit_ty(&qself.ty));
|
||||
}
|
||||
try_visit!(visitor.visit_path(prefix, item.id));
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
visitor.visit_ident(*ident);
|
||||
if let Some(rename) = rename {
|
||||
visitor.visit_ident(*rename);
|
||||
}
|
||||
}
|
||||
}
|
||||
visit_opt!(visitor, visit_block, body);
|
||||
}
|
||||
}
|
||||
@ -837,7 +846,14 @@ impl WalkItemKind for AssocItemKind {
|
||||
AssocItemKind::MacCall(mac) => {
|
||||
try_visit!(visitor.visit_mac_call(mac));
|
||||
}
|
||||
AssocItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => {
|
||||
AssocItemKind::Delegation(box Delegation {
|
||||
id,
|
||||
qself,
|
||||
path,
|
||||
rename,
|
||||
body,
|
||||
from_glob: _,
|
||||
}) => {
|
||||
if let Some(qself) = qself {
|
||||
try_visit!(visitor.visit_ty(&qself.ty));
|
||||
}
|
||||
@ -850,12 +866,14 @@ impl WalkItemKind for AssocItemKind {
|
||||
try_visit!(visitor.visit_ty(&qself.ty));
|
||||
}
|
||||
try_visit!(visitor.visit_path(prefix, item.id));
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
visitor.visit_ident(*ident);
|
||||
if let Some(rename) = rename {
|
||||
visitor.visit_ident(*rename);
|
||||
}
|
||||
}
|
||||
}
|
||||
visit_opt!(visitor, visit_block, body);
|
||||
}
|
||||
}
|
||||
|
@ -1716,24 +1716,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
// `mut iter => { ... }`
|
||||
let iter_arm = self.arm(iter_pat, loop_expr);
|
||||
|
||||
let into_iter_expr = match loop_kind {
|
||||
let match_expr = match loop_kind {
|
||||
ForLoopKind::For => {
|
||||
// `::std::iter::IntoIterator::into_iter(<head>)`
|
||||
self.expr_call_lang_item_fn(
|
||||
let into_iter_expr = self.expr_call_lang_item_fn(
|
||||
head_span,
|
||||
hir::LangItem::IntoIterIntoIter,
|
||||
arena_vec![self; head],
|
||||
)
|
||||
}
|
||||
// ` unsafe { Pin::new_unchecked(&mut into_async_iter(<head>)) }`
|
||||
ForLoopKind::ForAwait => {
|
||||
// `::core::async_iter::IntoAsyncIterator::into_async_iter(<head>)`
|
||||
let iter = self.expr_call_lang_item_fn(
|
||||
head_span,
|
||||
hir::LangItem::IntoAsyncIterIntoIter,
|
||||
arena_vec![self; head],
|
||||
);
|
||||
let iter = self.expr_mut_addr_of(head_span, iter);
|
||||
|
||||
self.arena.alloc(self.expr_match(
|
||||
for_span,
|
||||
into_iter_expr,
|
||||
arena_vec![self; iter_arm],
|
||||
hir::MatchSource::ForLoopDesugar,
|
||||
))
|
||||
}
|
||||
// `match into_async_iter(<head>) { ref mut iter => match unsafe { Pin::new_unchecked(iter) } { ... } }`
|
||||
ForLoopKind::ForAwait => {
|
||||
let iter_ident = iter;
|
||||
let (async_iter_pat, async_iter_pat_id) =
|
||||
self.pat_ident_binding_mode(head_span, iter_ident, hir::BindingMode::REF_MUT);
|
||||
let iter = self.expr_ident_mut(head_span, iter_ident, async_iter_pat_id);
|
||||
// `Pin::new_unchecked(...)`
|
||||
let iter = self.arena.alloc(self.expr_call_lang_item_fn_mut(
|
||||
head_span,
|
||||
@ -1742,17 +1746,29 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
));
|
||||
// `unsafe { ... }`
|
||||
let iter = self.arena.alloc(self.expr_unsafe(iter));
|
||||
iter
|
||||
}
|
||||
};
|
||||
|
||||
let match_expr = self.arena.alloc(self.expr_match(
|
||||
let inner_match_expr = self.arena.alloc(self.expr_match(
|
||||
for_span,
|
||||
into_iter_expr,
|
||||
iter,
|
||||
arena_vec![self; iter_arm],
|
||||
hir::MatchSource::ForLoopDesugar,
|
||||
));
|
||||
|
||||
// `::core::async_iter::IntoAsyncIterator::into_async_iter(<head>)`
|
||||
let iter = self.expr_call_lang_item_fn(
|
||||
head_span,
|
||||
hir::LangItem::IntoAsyncIterIntoIter,
|
||||
arena_vec![self; head],
|
||||
);
|
||||
let iter_arm = self.arm(async_iter_pat, inner_match_expr);
|
||||
self.arena.alloc(self.expr_match(
|
||||
for_span,
|
||||
iter,
|
||||
arena_vec![self; iter_arm],
|
||||
hir::MatchSource::ForLoopDesugar,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// This is effectively `{ let _result = ...; _result }`.
|
||||
// The construct was introduced in #21984 and is necessary to make sure that
|
||||
// temporaries in the `head` expression are dropped and do not leak to the
|
||||
|
@ -1319,6 +1319,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
CoroutineKind::AsyncGen { .. } => hir::CoroutineDesugaring::AsyncGen,
|
||||
};
|
||||
let closure_id = coroutine_kind.closure_id();
|
||||
|
||||
let span = if let FnRetTy::Default(span) = decl.output
|
||||
&& matches!(coroutine_source, rustc_hir::CoroutineSource::Closure)
|
||||
{
|
||||
body_span.with_lo(span.lo())
|
||||
} else {
|
||||
body_span
|
||||
};
|
||||
let coroutine_expr = self.make_desugared_coroutine_expr(
|
||||
// The default capture mode here is by-ref. Later on during upvar analysis,
|
||||
// we will force the captured arguments to by-move, but for async closures,
|
||||
@ -1327,7 +1335,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
CaptureBy::Ref,
|
||||
closure_id,
|
||||
None,
|
||||
body_span,
|
||||
span,
|
||||
desugaring_kind,
|
||||
coroutine_source,
|
||||
mkbody,
|
||||
|
@ -9,6 +9,12 @@ use rustc_ast::ptr::P;
|
||||
use rustc_ast::ModKind;
|
||||
use rustc_span::symbol::Ident;
|
||||
|
||||
enum DelegationKind<'a> {
|
||||
Single,
|
||||
List(&'a [(Ident, Option<Ident>)]),
|
||||
Glob,
|
||||
}
|
||||
|
||||
fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String {
|
||||
format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s)
|
||||
}
|
||||
@ -387,7 +393,7 @@ impl<'a> State<'a> {
|
||||
&item.vis,
|
||||
&deleg.qself,
|
||||
&deleg.path,
|
||||
None,
|
||||
DelegationKind::Single,
|
||||
&deleg.body,
|
||||
),
|
||||
ast::ItemKind::DelegationMac(deleg) => self.print_delegation(
|
||||
@ -395,7 +401,7 @@ impl<'a> State<'a> {
|
||||
&item.vis,
|
||||
&deleg.qself,
|
||||
&deleg.prefix,
|
||||
Some(&deleg.suffixes),
|
||||
deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)),
|
||||
&deleg.body,
|
||||
),
|
||||
}
|
||||
@ -579,7 +585,7 @@ impl<'a> State<'a> {
|
||||
vis,
|
||||
&deleg.qself,
|
||||
&deleg.path,
|
||||
None,
|
||||
DelegationKind::Single,
|
||||
&deleg.body,
|
||||
),
|
||||
ast::AssocItemKind::DelegationMac(deleg) => self.print_delegation(
|
||||
@ -587,20 +593,20 @@ impl<'a> State<'a> {
|
||||
vis,
|
||||
&deleg.qself,
|
||||
&deleg.prefix,
|
||||
Some(&deleg.suffixes),
|
||||
deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)),
|
||||
&deleg.body,
|
||||
),
|
||||
}
|
||||
self.ann.post(self, AnnNode::SubItem(id))
|
||||
}
|
||||
|
||||
pub(crate) fn print_delegation(
|
||||
fn print_delegation(
|
||||
&mut self,
|
||||
attrs: &[ast::Attribute],
|
||||
vis: &ast::Visibility,
|
||||
qself: &Option<P<ast::QSelf>>,
|
||||
path: &ast::Path,
|
||||
suffixes: Option<&[(Ident, Option<Ident>)]>,
|
||||
kind: DelegationKind<'_>,
|
||||
body: &Option<P<ast::Block>>,
|
||||
) {
|
||||
if body.is_some() {
|
||||
@ -614,7 +620,9 @@ impl<'a> State<'a> {
|
||||
} else {
|
||||
self.print_path(path, false, 0);
|
||||
}
|
||||
if let Some(suffixes) = suffixes {
|
||||
match kind {
|
||||
DelegationKind::Single => {}
|
||||
DelegationKind::List(suffixes) => {
|
||||
self.word("::");
|
||||
self.word("{");
|
||||
for (i, (ident, rename)) in suffixes.iter().enumerate() {
|
||||
@ -630,6 +638,11 @@ impl<'a> State<'a> {
|
||||
}
|
||||
self.word("}");
|
||||
}
|
||||
DelegationKind::Glob => {
|
||||
self.word("::");
|
||||
self.word("*");
|
||||
}
|
||||
}
|
||||
if let Some(body) = body {
|
||||
self.nbsp();
|
||||
self.print_block_with_attrs(body, attrs);
|
||||
|
@ -241,7 +241,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
variant_index: VariantIdx,
|
||||
) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> {
|
||||
match self.layout_of(ty)?.variants {
|
||||
abi::Variants::Single { .. } => Ok(None),
|
||||
abi::Variants::Single { .. } => {
|
||||
// The tag of a `Single` enum is like the tag of the niched
|
||||
// variant: there's no tag as the discriminant is encoded
|
||||
// entirely implicitly. If `write_discriminant` ever hits this
|
||||
// case, we do a "validation read" to ensure the the right
|
||||
// discriminant is encoded implicitly, so any attempt to write
|
||||
// the wrong discriminant for a `Single` enum will reliably
|
||||
// result in UB.
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
abi::Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Direct,
|
||||
|
@ -35,8 +35,8 @@ expand_duplicate_matcher_binding = duplicate matcher binding
|
||||
.label = duplicate binding
|
||||
.label2 = previous binding
|
||||
|
||||
expand_empty_delegation_list =
|
||||
empty list delegation is not supported
|
||||
expand_empty_delegation_mac =
|
||||
empty {$kind} delegation is not supported
|
||||
|
||||
expand_expected_paren_or_brace =
|
||||
expected `(` or `{"{"}`, found `{$token}`
|
||||
@ -58,6 +58,9 @@ expand_feature_removed =
|
||||
.label = feature has been removed
|
||||
.reason = {$reason}
|
||||
|
||||
expand_glob_delegation_outside_impls =
|
||||
glob delegation is only supported in impls
|
||||
|
||||
expand_helper_attribute_name_invalid =
|
||||
`{$name}` cannot be a name of derive helper attribute
|
||||
|
||||
|
@ -357,6 +357,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GlobDelegationExpander {
|
||||
fn expand(&self, ecx: &mut ExtCtxt<'_>) -> ExpandResult<Vec<(Ident, Option<Ident>)>, ()>;
|
||||
}
|
||||
|
||||
// Use a macro because forwarding to a simple function has type system issues
|
||||
macro_rules! make_stmts_default {
|
||||
($me:expr) => {
|
||||
@ -714,6 +718,9 @@ pub enum SyntaxExtensionKind {
|
||||
/// The produced AST fragment is appended to the input AST fragment.
|
||||
Box<dyn MultiItemModifier + sync::DynSync + sync::DynSend>,
|
||||
),
|
||||
|
||||
/// A glob delegation.
|
||||
GlobDelegation(Box<dyn GlobDelegationExpander + sync::DynSync + sync::DynSend>),
|
||||
}
|
||||
|
||||
/// A struct representing a macro definition in "lowered" form ready for expansion.
|
||||
@ -748,7 +755,9 @@ impl SyntaxExtension {
|
||||
/// Returns which kind of macro calls this syntax extension.
|
||||
pub fn macro_kind(&self) -> MacroKind {
|
||||
match self.kind {
|
||||
SyntaxExtensionKind::Bang(..) | SyntaxExtensionKind::LegacyBang(..) => MacroKind::Bang,
|
||||
SyntaxExtensionKind::Bang(..)
|
||||
| SyntaxExtensionKind::LegacyBang(..)
|
||||
| SyntaxExtensionKind::GlobDelegation(..) => MacroKind::Bang,
|
||||
SyntaxExtensionKind::Attr(..)
|
||||
| SyntaxExtensionKind::LegacyAttr(..)
|
||||
| SyntaxExtensionKind::NonMacroAttr => MacroKind::Attr,
|
||||
@ -922,6 +931,32 @@ impl SyntaxExtension {
|
||||
SyntaxExtension::default(SyntaxExtensionKind::NonMacroAttr, edition)
|
||||
}
|
||||
|
||||
pub fn glob_delegation(
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
edition: Edition,
|
||||
) -> SyntaxExtension {
|
||||
struct GlobDelegationExpanderImpl {
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
}
|
||||
impl GlobDelegationExpander for GlobDelegationExpanderImpl {
|
||||
fn expand(
|
||||
&self,
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
) -> ExpandResult<Vec<(Ident, Option<Ident>)>, ()> {
|
||||
match ecx.resolver.glob_delegation_suffixes(self.trait_def_id, self.impl_def_id) {
|
||||
Ok(suffixes) => ExpandResult::Ready(suffixes),
|
||||
Err(Indeterminate) if ecx.force_mode => ExpandResult::Ready(Vec::new()),
|
||||
Err(Indeterminate) => ExpandResult::Retry(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let expander = GlobDelegationExpanderImpl { trait_def_id, impl_def_id };
|
||||
SyntaxExtension::default(SyntaxExtensionKind::GlobDelegation(Box::new(expander)), edition)
|
||||
}
|
||||
|
||||
pub fn expn_data(
|
||||
&self,
|
||||
parent: LocalExpnId,
|
||||
@ -1030,6 +1065,16 @@ pub trait ResolverExpand {
|
||||
|
||||
/// Tools registered with `#![register_tool]` and used by tool attributes and lints.
|
||||
fn registered_tools(&self) -> &RegisteredTools;
|
||||
|
||||
/// Mark this invocation id as a glob delegation.
|
||||
fn register_glob_delegation(&mut self, invoc_id: LocalExpnId);
|
||||
|
||||
/// Names of specific methods to which glob delegation expands.
|
||||
fn glob_delegation_suffixes(
|
||||
&mut self,
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate>;
|
||||
}
|
||||
|
||||
pub trait LintStoreExpand {
|
||||
|
@ -435,8 +435,16 @@ pub struct ExpectedParenOrBrace<'a> {
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_empty_delegation_list)]
|
||||
pub(crate) struct EmptyDelegationList {
|
||||
#[diag(expand_empty_delegation_mac)]
|
||||
pub(crate) struct EmptyDelegationMac {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub kind: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_glob_delegation_outside_impls)]
|
||||
pub(crate) struct GlobDelegationOutsideImpls {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::base::*;
|
||||
use crate::config::StripUnconfigured;
|
||||
use crate::errors::{
|
||||
EmptyDelegationList, IncompleteParse, RecursionLimitReached, RemoveExprNotSupported,
|
||||
RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind,
|
||||
EmptyDelegationMac, GlobDelegationOutsideImpls, IncompleteParse, RecursionLimitReached,
|
||||
RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind,
|
||||
};
|
||||
use crate::mbe::diagnostics::annotate_err_with_kind;
|
||||
use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod};
|
||||
@ -343,6 +343,9 @@ pub enum InvocationKind {
|
||||
is_const: bool,
|
||||
item: Annotatable,
|
||||
},
|
||||
GlobDelegation {
|
||||
item: P<ast::AssocItem>,
|
||||
},
|
||||
}
|
||||
|
||||
impl InvocationKind {
|
||||
@ -370,6 +373,7 @@ impl Invocation {
|
||||
InvocationKind::Bang { span, .. } => *span,
|
||||
InvocationKind::Attr { attr, .. } => attr.span,
|
||||
InvocationKind::Derive { path, .. } => path.span,
|
||||
InvocationKind::GlobDelegation { item } => item.span,
|
||||
}
|
||||
}
|
||||
|
||||
@ -378,6 +382,7 @@ impl Invocation {
|
||||
InvocationKind::Bang { span, .. } => span,
|
||||
InvocationKind::Attr { attr, .. } => &mut attr.span,
|
||||
InvocationKind::Derive { path, .. } => &mut path.span,
|
||||
InvocationKind::GlobDelegation { item } => &mut item.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -800,6 +805,36 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
InvocationKind::GlobDelegation { item } => {
|
||||
let AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() };
|
||||
let suffixes = match ext {
|
||||
SyntaxExtensionKind::GlobDelegation(expander) => match expander.expand(self.cx)
|
||||
{
|
||||
ExpandResult::Ready(suffixes) => suffixes,
|
||||
ExpandResult::Retry(()) => {
|
||||
// Reassemble the original invocation for retrying.
|
||||
return ExpandResult::Retry(Invocation {
|
||||
kind: InvocationKind::GlobDelegation { item },
|
||||
..invoc
|
||||
});
|
||||
}
|
||||
},
|
||||
SyntaxExtensionKind::LegacyBang(..) => {
|
||||
let msg = "expanded a dummy glob delegation";
|
||||
let guar = self.cx.dcx().span_delayed_bug(span, msg);
|
||||
return ExpandResult::Ready(fragment_kind.dummy(span, guar));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
type Node = AstNodeWrapper<P<ast::AssocItem>, ImplItemTag>;
|
||||
let single_delegations = build_single_delegations::<Node>(
|
||||
self.cx, deleg, &item, &suffixes, item.span, true,
|
||||
);
|
||||
fragment_kind.expect_from_annotatables(
|
||||
single_delegations.map(|item| Annotatable::ImplItem(P(item))),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -1067,7 +1102,7 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
|
||||
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
|
||||
unreachable!()
|
||||
}
|
||||
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
None
|
||||
}
|
||||
fn delegation_item_kind(_deleg: Box<ast::Delegation>) -> Self::ItemKind {
|
||||
@ -1126,7 +1161,7 @@ impl InvocationCollectorNode for P<ast::Item> {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
match &self.kind {
|
||||
ItemKind::DelegationMac(deleg) => Some((deleg, self)),
|
||||
_ => None,
|
||||
@ -1270,7 +1305,7 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitItemTag>
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
match &self.wrapped.kind {
|
||||
AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)),
|
||||
_ => None,
|
||||
@ -1311,7 +1346,7 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, ImplItemTag>
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
match &self.wrapped.kind {
|
||||
AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)),
|
||||
_ => None,
|
||||
@ -1487,7 +1522,7 @@ impl InvocationCollectorNode for ast::Stmt {
|
||||
};
|
||||
(mac, attrs, if add_semicolon { AddSemicolon::Yes } else { AddSemicolon::No })
|
||||
}
|
||||
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
match &self.kind {
|
||||
StmtKind::Item(item) => match &item.kind {
|
||||
ItemKind::DelegationMac(deleg) => Some((deleg, item)),
|
||||
@ -1684,6 +1719,44 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, MethodReceiverTag>
|
||||
}
|
||||
}
|
||||
|
||||
fn build_single_delegations<'a, Node: InvocationCollectorNode>(
|
||||
ecx: &ExtCtxt<'_>,
|
||||
deleg: &'a ast::DelegationMac,
|
||||
item: &'a ast::Item<Node::ItemKind>,
|
||||
suffixes: &'a [(Ident, Option<Ident>)],
|
||||
item_span: Span,
|
||||
from_glob: bool,
|
||||
) -> impl Iterator<Item = ast::Item<Node::ItemKind>> + 'a {
|
||||
if suffixes.is_empty() {
|
||||
// Report an error for now, to avoid keeping stem for resolution and
|
||||
// stability checks.
|
||||
let kind = String::from(if from_glob { "glob" } else { "list" });
|
||||
ecx.dcx().emit_err(EmptyDelegationMac { span: item.span, kind });
|
||||
}
|
||||
|
||||
suffixes.iter().map(move |&(ident, rename)| {
|
||||
let mut path = deleg.prefix.clone();
|
||||
path.segments.push(ast::PathSegment { ident, id: ast::DUMMY_NODE_ID, args: None });
|
||||
|
||||
ast::Item {
|
||||
attrs: item.attrs.clone(),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: if from_glob { item_span } else { ident.span },
|
||||
vis: item.vis.clone(),
|
||||
ident: rename.unwrap_or(ident),
|
||||
kind: Node::delegation_item_kind(Box::new(ast::Delegation {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
qself: deleg.qself.clone(),
|
||||
path,
|
||||
rename,
|
||||
body: deleg.body.clone(),
|
||||
from_glob,
|
||||
})),
|
||||
tokens: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct InvocationCollector<'a, 'b> {
|
||||
cx: &'a mut ExtCtxt<'b>,
|
||||
invocations: Vec<(Invocation, Option<Lrc<SyntaxExtension>>)>,
|
||||
@ -1702,6 +1775,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||
|
||||
fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment {
|
||||
let expn_id = LocalExpnId::fresh_empty();
|
||||
if matches!(kind, InvocationKind::GlobDelegation { .. }) {
|
||||
// In resolver we need to know which invocation ids are delegations early,
|
||||
// before their `ExpnData` is filled.
|
||||
self.cx.resolver.register_glob_delegation(expn_id);
|
||||
}
|
||||
let vis = kind.placeholder_visibility();
|
||||
self.invocations.push((
|
||||
Invocation {
|
||||
@ -1734,6 +1812,14 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||
self.collect(kind, InvocationKind::Attr { attr, pos, item, derives })
|
||||
}
|
||||
|
||||
fn collect_glob_delegation(
|
||||
&mut self,
|
||||
item: P<ast::AssocItem>,
|
||||
kind: AstFragmentKind,
|
||||
) -> AstFragment {
|
||||
self.collect(kind, InvocationKind::GlobDelegation { item })
|
||||
}
|
||||
|
||||
/// If `item` is an attribute invocation, remove the attribute and return it together with
|
||||
/// its position and derives following it. We have to collect the derives in order to resolve
|
||||
/// legacy derive helpers (helpers written before derives that introduce them).
|
||||
@ -1901,37 +1987,27 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||
Node::post_flat_map_node_collect_bang(&mut res, add_semicolon);
|
||||
res
|
||||
}
|
||||
None if let Some((deleg, item)) = node.delegation_list() => {
|
||||
if deleg.suffixes.is_empty() {
|
||||
// Report an error for now, to avoid keeping stem for resolution and
|
||||
// stability checks.
|
||||
self.cx.dcx().emit_err(EmptyDelegationList { span: item.span });
|
||||
None if let Some((deleg, item)) = node.delegation() => {
|
||||
let Some(suffixes) = &deleg.suffixes else {
|
||||
let item = match node.to_annotatable() {
|
||||
Annotatable::ImplItem(item) => item,
|
||||
ann @ (Annotatable::Item(_)
|
||||
| Annotatable::TraitItem(_)
|
||||
| Annotatable::Stmt(_)) => {
|
||||
let span = ann.span();
|
||||
self.cx.dcx().emit_err(GlobDelegationOutsideImpls { span });
|
||||
return Default::default();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
return self.collect_glob_delegation(item, Node::KIND).make_ast::<Node>();
|
||||
};
|
||||
|
||||
Node::flatten_outputs(deleg.suffixes.iter().map(|&(ident, rename)| {
|
||||
let mut path = deleg.prefix.clone();
|
||||
path.segments.push(ast::PathSegment {
|
||||
ident,
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
args: None,
|
||||
});
|
||||
|
||||
let mut item = Node::from_item(ast::Item {
|
||||
attrs: item.attrs.clone(),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: ident.span,
|
||||
vis: item.vis.clone(),
|
||||
ident: rename.unwrap_or(ident),
|
||||
kind: Node::delegation_item_kind(Box::new(ast::Delegation {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
qself: deleg.qself.clone(),
|
||||
path,
|
||||
rename,
|
||||
body: deleg.body.clone(),
|
||||
})),
|
||||
tokens: None,
|
||||
});
|
||||
|
||||
let single_delegations = build_single_delegations::<Node>(
|
||||
self.cx, deleg, item, suffixes, item.span, false,
|
||||
);
|
||||
Node::flatten_outputs(single_delegations.map(|item| {
|
||||
let mut item = Node::from_item(item);
|
||||
assign_id!(self, item.node_id_mut(), || item.noop_flat_map(self))
|
||||
}))
|
||||
}
|
||||
@ -1983,7 +2059,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||
self.collect_bang(mac, Node::KIND).make_ast::<Node>()
|
||||
})
|
||||
}
|
||||
None if node.delegation_list().is_some() => unreachable!(),
|
||||
None if node.delegation().is_some() => unreachable!(),
|
||||
None => {
|
||||
assign_id!(self, node.node_id_mut(), || node.noop_visit(self))
|
||||
}
|
||||
|
@ -588,6 +588,8 @@ declare_features! (
|
||||
(incomplete, return_type_notation, "1.70.0", Some(109417)),
|
||||
/// Allows `extern "rust-cold"`.
|
||||
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
|
||||
/// Shortern the tail expression lifetime
|
||||
(unstable, shorter_tail_lifetimes, "1.79.0", Some(123739)),
|
||||
/// Allows the use of SIMD types in functions declared in `extern` blocks.
|
||||
(unstable, simd_ffi, "1.0.0", Some(27731)),
|
||||
/// Allows specialization of implementations (RFC 1210).
|
||||
|
@ -6,7 +6,6 @@
|
||||
//!
|
||||
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html
|
||||
|
||||
use rustc_ast::visit::visit_opt;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
@ -168,7 +167,14 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
||||
hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement),
|
||||
}
|
||||
}
|
||||
visit_opt!(visitor, visit_expr, &blk.expr);
|
||||
if let Some(tail_expr) = blk.expr {
|
||||
if visitor.tcx.features().shorter_tail_lifetimes
|
||||
&& blk.span.edition().at_least_rust_2024()
|
||||
{
|
||||
visitor.terminating_scopes.insert(tail_expr.hir_id.local_id);
|
||||
}
|
||||
visitor.visit_expr(tail_expr);
|
||||
}
|
||||
}
|
||||
|
||||
visitor.cx = prev_cx;
|
||||
|
@ -859,10 +859,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
} else {
|
||||
// Only point to return type if the expected type is the return type, as if they
|
||||
// are not, the expectation must have been caused by something else.
|
||||
debug!("return type {:?}", hir_ty);
|
||||
debug!(?hir_ty, "return type");
|
||||
let ty = self.lowerer().lower_ty(hir_ty);
|
||||
debug!("return type {:?}", ty);
|
||||
debug!("expected type {:?}", expected);
|
||||
debug!(?ty, "return type (lowered)");
|
||||
debug!(?expected, "expected type");
|
||||
let bound_vars = self.tcx.late_bound_vars(hir_ty.hir_id.owner.into());
|
||||
let ty = Binder::bind_with_vars(ty, bound_vars);
|
||||
let ty = self.normalize(hir_ty.span, ty);
|
||||
@ -873,7 +873,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
expected,
|
||||
});
|
||||
self.try_suggest_return_impl_trait(err, expected, found, fn_id);
|
||||
self.note_caller_chooses_ty_for_ty_param(err, expected, found);
|
||||
self.try_note_caller_chooses_ty_for_ty_param(err, expected, found);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -883,19 +883,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
false
|
||||
}
|
||||
|
||||
fn note_caller_chooses_ty_for_ty_param(
|
||||
fn try_note_caller_chooses_ty_for_ty_param(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) {
|
||||
if let ty::Param(expected_ty_as_param) = expected.kind() {
|
||||
// Only show the note if:
|
||||
// 1. `expected` ty is a type parameter;
|
||||
// 2. The `expected` type parameter does *not* occur in the return expression type. This can
|
||||
// happen for e.g. `fn foo<T>(t: &T) -> T { t }`, where `expected` is `T` but `found` is
|
||||
// `&T`. Saying "the caller chooses a type for `T` which can be different from `&T`" is
|
||||
// "well duh" and is only confusing and not helpful.
|
||||
let ty::Param(expected_ty_as_param) = expected.kind() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if found.contains(expected) {
|
||||
return;
|
||||
}
|
||||
|
||||
diag.subdiagnostic(errors::NoteCallerChoosesTyForTyParam {
|
||||
ty_param_name: expected_ty_as_param.name,
|
||||
found_ty: found,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// check whether the return type is a generic type with a trait bound
|
||||
/// only suggest this if the generic param is not present in the arguments
|
||||
|
@ -22,7 +22,7 @@ use std::ops::Deref;
|
||||
/// e.g. closures defined within the function. For example:
|
||||
/// ```ignore (illustrative)
|
||||
/// fn foo() {
|
||||
/// bar(move|| { ... })
|
||||
/// bar(move || { ... })
|
||||
/// }
|
||||
/// ```
|
||||
/// Here, the function `foo()` and the closure passed to
|
||||
|
@ -549,6 +549,7 @@ lint_non_local_definitions_impl = non-local `impl` definition, `impl` blocks sho
|
||||
.without_trait = methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl`
|
||||
.with_trait = an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl`
|
||||
.bounds = `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type
|
||||
.doctest = make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}`
|
||||
.exception = items in an anonymous const item (`const _: () = {"{"} ... {"}"}`) are treated as in the same scope as the anonymous const's declaration
|
||||
.const_anon = use a const-anon item to suppress this lint
|
||||
.macro_to_change = the {$macro_kind} `{$macro_to_change}` defines the non-local `impl`, and may need to be changed
|
||||
|
@ -1358,6 +1358,7 @@ pub enum NonLocalDefinitionsDiag {
|
||||
cargo_update: Option<NonLocalDefinitionsCargoUpdateNote>,
|
||||
const_anon: Option<Option<Span>>,
|
||||
move_to: Option<(Span, Vec<Span>)>,
|
||||
doctest: bool,
|
||||
may_remove: Option<(Span, String)>,
|
||||
has_trait: bool,
|
||||
self_ty_str: String,
|
||||
@ -1368,8 +1369,7 @@ pub enum NonLocalDefinitionsDiag {
|
||||
depth: u32,
|
||||
body_kind_descr: &'static str,
|
||||
body_name: String,
|
||||
help: Option<()>,
|
||||
doctest_help: Option<()>,
|
||||
doctest: bool,
|
||||
cargo_update: Option<NonLocalDefinitionsCargoUpdateNote>,
|
||||
},
|
||||
}
|
||||
@ -1384,6 +1384,7 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
|
||||
cargo_update,
|
||||
const_anon,
|
||||
move_to,
|
||||
doctest,
|
||||
may_remove,
|
||||
has_trait,
|
||||
self_ty_str,
|
||||
@ -1422,6 +1423,9 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
|
||||
}
|
||||
diag.span_help(ms, fluent::lint_non_local_definitions_impl_move_help);
|
||||
}
|
||||
if doctest {
|
||||
diag.help(fluent::lint_doctest);
|
||||
}
|
||||
|
||||
if let Some((span, part)) = may_remove {
|
||||
diag.arg("may_remove_part", part);
|
||||
@ -1451,8 +1455,7 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
|
||||
depth,
|
||||
body_kind_descr,
|
||||
body_name,
|
||||
help,
|
||||
doctest_help,
|
||||
doctest,
|
||||
cargo_update,
|
||||
} => {
|
||||
diag.primary_message(fluent::lint_non_local_definitions_macro_rules);
|
||||
@ -1460,11 +1463,10 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
|
||||
diag.arg("body_kind_descr", body_kind_descr);
|
||||
diag.arg("body_name", body_name);
|
||||
|
||||
if let Some(()) = help {
|
||||
diag.help(fluent::lint_help);
|
||||
}
|
||||
if let Some(()) = doctest_help {
|
||||
if doctest {
|
||||
diag.help(fluent::lint_help_doctest);
|
||||
} else {
|
||||
diag.help(fluent::lint_help);
|
||||
}
|
||||
|
||||
diag.note(fluent::lint_non_local);
|
||||
|
@ -111,6 +111,12 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
||||
}
|
||||
};
|
||||
|
||||
// determining if we are in a doctest context can't currently be determined
|
||||
// by the code itself (there are no specific attributes), but fortunately rustdoc
|
||||
// sets a perma-unstable env var for libtest so we just reuse that for now
|
||||
let is_at_toplevel_doctest =
|
||||
|| self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok();
|
||||
|
||||
match item.kind {
|
||||
ItemKind::Impl(impl_) => {
|
||||
// The RFC states:
|
||||
@ -191,29 +197,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
||||
None
|
||||
};
|
||||
|
||||
let mut collector = PathCollector { paths: Vec::new() };
|
||||
collector.visit_ty(&impl_.self_ty);
|
||||
if let Some(of_trait) = &impl_.of_trait {
|
||||
collector.visit_trait_ref(of_trait);
|
||||
}
|
||||
collector.visit_generics(&impl_.generics);
|
||||
|
||||
let mut may_move: Vec<Span> = collector
|
||||
.paths
|
||||
.into_iter()
|
||||
.filter_map(|path| {
|
||||
if let Some(did) = path.res.opt_def_id()
|
||||
&& did_has_local_parent(did, cx.tcx, parent, parent_parent)
|
||||
{
|
||||
Some(cx.tcx.def_span(did))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
may_move.sort();
|
||||
may_move.dedup();
|
||||
|
||||
let const_anon = matches!(parent_def_kind, DefKind::Const | DefKind::Static { .. })
|
||||
.then_some(span_for_const_anon_suggestion);
|
||||
|
||||
@ -248,6 +231,33 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (doctest, move_to) = if is_at_toplevel_doctest() {
|
||||
(true, None)
|
||||
} else {
|
||||
let mut collector = PathCollector { paths: Vec::new() };
|
||||
collector.visit_ty(&impl_.self_ty);
|
||||
if let Some(of_trait) = &impl_.of_trait {
|
||||
collector.visit_trait_ref(of_trait);
|
||||
}
|
||||
collector.visit_generics(&impl_.generics);
|
||||
|
||||
let mut may_move: Vec<Span> = collector
|
||||
.paths
|
||||
.into_iter()
|
||||
.filter_map(|path| {
|
||||
if let Some(did) = path.res.opt_def_id()
|
||||
&& did_has_local_parent(did, cx.tcx, parent, parent_parent)
|
||||
{
|
||||
Some(cx.tcx.def_span(did))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
may_move.sort();
|
||||
may_move.dedup();
|
||||
|
||||
let move_to = if may_move.is_empty() {
|
||||
ms.push_span_label(
|
||||
cx.tcx.def_span(parent),
|
||||
@ -258,6 +268,9 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
||||
Some((cx.tcx.def_span(parent), may_move))
|
||||
};
|
||||
|
||||
(false, move_to)
|
||||
};
|
||||
|
||||
let macro_to_change =
|
||||
if let ExpnKind::Macro(kind, name) = item.span.ctxt().outer_expn_data().kind {
|
||||
Some((name.to_string(), kind.descr()))
|
||||
@ -279,6 +292,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
||||
self_ty_str,
|
||||
of_trait_str,
|
||||
move_to,
|
||||
doctest,
|
||||
may_remove,
|
||||
has_trait: impl_.of_trait.is_some(),
|
||||
macro_to_change,
|
||||
@ -288,12 +302,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
||||
ItemKind::Macro(_macro, MacroKind::Bang)
|
||||
if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) =>
|
||||
{
|
||||
// determining we if are in a doctest context can't currently be determined
|
||||
// by the code it-self (no specific attrs), but fortunatly rustdoc sets a
|
||||
// perma-unstable env for libtest so we just re-use that env for now
|
||||
let is_at_toplevel_doctest =
|
||||
self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok();
|
||||
|
||||
cx.emit_span_lint(
|
||||
NON_LOCAL_DEFINITIONS,
|
||||
item.span,
|
||||
@ -304,8 +312,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
||||
.map(|s| s.to_ident_string())
|
||||
.unwrap_or_else(|| "<unnameable>".to_string()),
|
||||
cargo_update: cargo_update(),
|
||||
help: (!is_at_toplevel_doctest).then_some(()),
|
||||
doctest_help: is_at_toplevel_doctest.then_some(()),
|
||||
doctest: is_at_toplevel_doctest(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -2699,12 +2699,13 @@ pub(crate) struct SingleColonImportPath {
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_bad_item_kind)]
|
||||
#[help]
|
||||
pub(crate) struct BadItemKind {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub descr: &'static str,
|
||||
pub ctx: &'static str,
|
||||
#[help]
|
||||
pub help: Option<()>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
@ -707,15 +707,25 @@ impl<'a> Parser<'a> {
|
||||
};
|
||||
|
||||
let (ident, item_kind) = if self.eat(&token::PathSep) {
|
||||
let (suffixes, _) = self.parse_delim_comma_seq(Delimiter::Brace, |p| {
|
||||
Ok((p.parse_path_segment_ident()?, rename(p)?))
|
||||
})?;
|
||||
let suffixes = if self.eat(&token::BinOp(token::Star)) {
|
||||
None
|
||||
} else {
|
||||
let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?));
|
||||
Some(self.parse_delim_comma_seq(Delimiter::Brace, parse_suffix)?.0)
|
||||
};
|
||||
let deleg = DelegationMac { qself, prefix: path, suffixes, body: body(self)? };
|
||||
(Ident::empty(), ItemKind::DelegationMac(Box::new(deleg)))
|
||||
} else {
|
||||
let rename = rename(self)?;
|
||||
let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident);
|
||||
let deleg = Delegation { id: DUMMY_NODE_ID, qself, path, rename, body: body(self)? };
|
||||
let deleg = Delegation {
|
||||
id: DUMMY_NODE_ID,
|
||||
qself,
|
||||
path,
|
||||
rename,
|
||||
body: body(self)?,
|
||||
from_glob: false,
|
||||
};
|
||||
(ident, ItemKind::Delegation(Box::new(deleg)))
|
||||
};
|
||||
|
||||
@ -1237,7 +1247,11 @@ impl<'a> Parser<'a> {
|
||||
// FIXME(#100717): needs variant for each `ItemKind` (instead of using `ItemKind::descr()`)
|
||||
let span = self.psess.source_map().guess_head_span(span);
|
||||
let descr = kind.descr();
|
||||
self.dcx().emit_err(errors::BadItemKind { span, descr, ctx });
|
||||
let help = match kind {
|
||||
ItemKind::DelegationMac(deleg) if deleg.suffixes.is_none() => None,
|
||||
_ => Some(()),
|
||||
};
|
||||
self.dcx().emit_err(errors::BadItemKind { span, descr, ctx, help });
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind};
|
||||
use rustc_ast::{Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId};
|
||||
use rustc_attr as attr;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_expand::base::ResolverExpand;
|
||||
use rustc_expand::expand::AstFragment;
|
||||
use rustc_hir::def::{self, *};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
|
||||
@ -1358,6 +1359,14 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
|
||||
self.visit_invoc_in_module(item.id);
|
||||
}
|
||||
AssocCtxt::Impl => {
|
||||
let invoc_id = item.id.placeholder_to_expn_id();
|
||||
if !self.r.glob_delegation_invoc_ids.contains(&invoc_id) {
|
||||
self.r
|
||||
.impl_unexpanded_invocations
|
||||
.entry(self.r.invocation_parent(invoc_id))
|
||||
.or_default()
|
||||
.insert(invoc_id);
|
||||
}
|
||||
self.visit_invoc(item.id);
|
||||
}
|
||||
}
|
||||
@ -1379,18 +1388,21 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
|
||||
self.r.feed_visibility(feed, vis);
|
||||
}
|
||||
|
||||
if ctxt == AssocCtxt::Trait {
|
||||
let ns = match item.kind {
|
||||
AssocItemKind::Const(..)
|
||||
| AssocItemKind::Delegation(..)
|
||||
| AssocItemKind::Fn(..) => ValueNS,
|
||||
AssocItemKind::Const(..) | AssocItemKind::Delegation(..) | AssocItemKind::Fn(..) => {
|
||||
ValueNS
|
||||
}
|
||||
AssocItemKind::Type(..) => TypeNS,
|
||||
AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(..) => bug!(), // handled above
|
||||
};
|
||||
|
||||
if ctxt == AssocCtxt::Trait {
|
||||
let parent = self.parent_scope.module;
|
||||
let expansion = self.parent_scope.expansion;
|
||||
self.r.define(parent, item.ident, ns, (self.res(def_id), vis, item.span, expansion));
|
||||
} else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) {
|
||||
let impl_def_id = self.r.tcx.local_parent(local_def_id);
|
||||
let key = BindingKey::new(item.ident.normalize_to_macros_2_0(), ns);
|
||||
self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key);
|
||||
}
|
||||
|
||||
visit::walk_assoc_item(self, item, ctxt);
|
||||
|
@ -144,8 +144,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
}
|
||||
ItemKind::GlobalAsm(..) => DefKind::GlobalAsm,
|
||||
ItemKind::Use(..) => return visit::walk_item(self, i),
|
||||
ItemKind::MacCall(..) => return self.visit_macro_invoc(i.id),
|
||||
ItemKind::DelegationMac(..) => unreachable!(),
|
||||
ItemKind::MacCall(..) | ItemKind::DelegationMac(..) => {
|
||||
return self.visit_macro_invoc(i.id);
|
||||
}
|
||||
};
|
||||
let def_id = self.create_def(i.id, i.ident.name, def_kind, i.span);
|
||||
|
||||
@ -294,8 +295,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
AssocItemKind::Fn(..) | AssocItemKind::Delegation(..) => DefKind::AssocFn,
|
||||
AssocItemKind::Const(..) => DefKind::AssocConst,
|
||||
AssocItemKind::Type(..) => DefKind::AssocTy,
|
||||
AssocItemKind::MacCall(..) => return self.visit_macro_invoc(i.id),
|
||||
AssocItemKind::DelegationMac(..) => unreachable!(),
|
||||
AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
|
||||
return self.visit_macro_invoc(i.id);
|
||||
}
|
||||
};
|
||||
|
||||
let def = self.create_def(i.id, i.ident.name, def_kind, i.span);
|
||||
|
@ -1089,7 +1089,7 @@ pub struct Resolver<'a, 'tcx> {
|
||||
single_segment_macro_resolutions:
|
||||
Vec<(Ident, MacroKind, ParentScope<'a>, Option<NameBinding<'a>>)>,
|
||||
multi_segment_macro_resolutions:
|
||||
Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'a>, Option<Res>)>,
|
||||
Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'a>, Option<Res>, Namespace)>,
|
||||
builtin_attrs: Vec<(Ident, ParentScope<'a>)>,
|
||||
/// `derive(Copy)` marks items they are applied to so they are treated specially later.
|
||||
/// Derive macros cannot modify the item themselves and have to store the markers in the global
|
||||
@ -1163,6 +1163,15 @@ pub struct Resolver<'a, 'tcx> {
|
||||
doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
|
||||
doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
|
||||
all_macro_rules: FxHashMap<Symbol, Res>,
|
||||
|
||||
/// Invocation ids of all glob delegations.
|
||||
glob_delegation_invoc_ids: FxHashSet<LocalExpnId>,
|
||||
/// Analogue of module `unexpanded_invocations` but in trait impls, excluding glob delegations.
|
||||
/// Needed because glob delegations wait for all other neighboring macros to expand.
|
||||
impl_unexpanded_invocations: FxHashMap<LocalDefId, FxHashSet<LocalExpnId>>,
|
||||
/// Simplified analogue of module `resolutions` but in trait impls, excluding glob delegations.
|
||||
/// Needed because glob delegations exclude explicitly defined names.
|
||||
impl_binding_keys: FxHashMap<LocalDefId, FxHashSet<BindingKey>>,
|
||||
}
|
||||
|
||||
/// Nothing really interesting here; it just provides memory for the rest of the crate.
|
||||
@ -1504,6 +1513,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
doc_link_traits_in_scope: Default::default(),
|
||||
all_macro_rules: Default::default(),
|
||||
delegation_fn_sigs: Default::default(),
|
||||
glob_delegation_invoc_ids: Default::default(),
|
||||
impl_unexpanded_invocations: Default::default(),
|
||||
impl_binding_keys: Default::default(),
|
||||
};
|
||||
|
||||
let root_parent_scope = ParentScope::module(graph_root, &resolver);
|
||||
|
@ -5,7 +5,7 @@ use crate::errors::CannotDetermineMacroResolution;
|
||||
use crate::errors::{self, AddAsNonDerive, CannotFindIdentInThisScope};
|
||||
use crate::errors::{MacroExpectedFound, RemoveSurroundingDerive};
|
||||
use crate::Namespace::*;
|
||||
use crate::{BuiltinMacroState, Determinacy, MacroData, Used};
|
||||
use crate::{BindingKey, BuiltinMacroState, Determinacy, MacroData, Used};
|
||||
use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet};
|
||||
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
|
||||
use rustc_ast::expand::StrippedCfgItem;
|
||||
@ -198,6 +198,11 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
|
||||
self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope);
|
||||
|
||||
parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion);
|
||||
if let Some(unexpanded_invocations) =
|
||||
self.impl_unexpanded_invocations.get_mut(&self.invocation_parent(expansion))
|
||||
{
|
||||
unexpanded_invocations.remove(&expansion);
|
||||
}
|
||||
}
|
||||
|
||||
fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind) {
|
||||
@ -262,15 +267,21 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
let (path, kind, inner_attr, derives) = match invoc.kind {
|
||||
InvocationKind::Attr { ref attr, ref derives, .. } => (
|
||||
&attr.get_normal_item().path,
|
||||
MacroKind::Attr,
|
||||
attr.style == ast::AttrStyle::Inner,
|
||||
self.arenas.alloc_ast_paths(derives),
|
||||
),
|
||||
InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang, false, &[][..]),
|
||||
InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive, false, &[][..]),
|
||||
let (mut derives, mut inner_attr, mut deleg_impl) = (&[][..], false, None);
|
||||
let (path, kind) = match invoc.kind {
|
||||
InvocationKind::Attr { ref attr, derives: ref attr_derives, .. } => {
|
||||
derives = self.arenas.alloc_ast_paths(attr_derives);
|
||||
inner_attr = attr.style == ast::AttrStyle::Inner;
|
||||
(&attr.get_normal_item().path, MacroKind::Attr)
|
||||
}
|
||||
InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang),
|
||||
InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive),
|
||||
InvocationKind::GlobDelegation { ref item } => {
|
||||
let ast::AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() };
|
||||
deleg_impl = Some(self.invocation_parent(invoc_id));
|
||||
// It is sufficient to consider glob delegation a bang macro for now.
|
||||
(&deleg.prefix, MacroKind::Bang)
|
||||
}
|
||||
};
|
||||
|
||||
// Derives are not included when `invocations` are collected, so we have to add them here.
|
||||
@ -286,10 +297,11 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
|
||||
node_id,
|
||||
force,
|
||||
soft_custom_inner_attributes_gate(path, invoc),
|
||||
deleg_impl,
|
||||
)?;
|
||||
|
||||
let span = invoc.span();
|
||||
let def_id = res.opt_def_id();
|
||||
let def_id = if deleg_impl.is_some() { None } else { res.opt_def_id() };
|
||||
invoc_id.set_expn_data(
|
||||
ext.expn_data(
|
||||
parent_scope.expansion,
|
||||
@ -452,6 +464,45 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
|
||||
fn registered_tools(&self) -> &RegisteredTools {
|
||||
self.registered_tools
|
||||
}
|
||||
|
||||
fn register_glob_delegation(&mut self, invoc_id: LocalExpnId) {
|
||||
self.glob_delegation_invoc_ids.insert(invoc_id);
|
||||
}
|
||||
|
||||
fn glob_delegation_suffixes(
|
||||
&mut self,
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate> {
|
||||
let target_trait = self.expect_module(trait_def_id);
|
||||
if !target_trait.unexpanded_invocations.borrow().is_empty() {
|
||||
return Err(Indeterminate);
|
||||
}
|
||||
// FIXME: Instead of waiting try generating all trait methods, and pruning
|
||||
// the shadowed ones a bit later, e.g. when all macro expansion completes.
|
||||
// Pros: expansion will be stuck less (but only in exotic cases), the implementation may be
|
||||
// less hacky.
|
||||
// Cons: More code is generated just to be deleted later, deleting already created `DefId`s
|
||||
// may be nontrivial.
|
||||
if let Some(unexpanded_invocations) = self.impl_unexpanded_invocations.get(&impl_def_id)
|
||||
&& !unexpanded_invocations.is_empty()
|
||||
{
|
||||
return Err(Indeterminate);
|
||||
}
|
||||
|
||||
let mut idents = Vec::new();
|
||||
target_trait.for_each_child(self, |this, ident, ns, _binding| {
|
||||
// FIXME: Adjust hygiene for idents from globs, like for glob imports.
|
||||
if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id)
|
||||
&& overriding_keys.contains(&BindingKey::new(ident.normalize_to_macros_2_0(), ns))
|
||||
{
|
||||
// The name is overridden, do not produce it from the glob delegation.
|
||||
} else {
|
||||
idents.push((ident, None));
|
||||
}
|
||||
});
|
||||
Ok(idents)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
@ -468,15 +519,40 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
node_id: NodeId,
|
||||
force: bool,
|
||||
soft_custom_inner_attributes_gate: bool,
|
||||
deleg_impl: Option<LocalDefId>,
|
||||
) -> Result<(Lrc<SyntaxExtension>, Res), Indeterminate> {
|
||||
let (ext, res) = match self.resolve_macro_path(path, Some(kind), parent_scope, true, force)
|
||||
{
|
||||
let (ext, res) = match self.resolve_macro_or_delegation_path(
|
||||
path,
|
||||
Some(kind),
|
||||
parent_scope,
|
||||
true,
|
||||
force,
|
||||
deleg_impl,
|
||||
) {
|
||||
Ok((Some(ext), res)) => (ext, res),
|
||||
Ok((None, res)) => (self.dummy_ext(kind), res),
|
||||
Err(Determinacy::Determined) => (self.dummy_ext(kind), Res::Err),
|
||||
Err(Determinacy::Undetermined) => return Err(Indeterminate),
|
||||
};
|
||||
|
||||
// Everything below is irrelevant to glob delegation, take a shortcut.
|
||||
if deleg_impl.is_some() {
|
||||
if !matches!(res, Res::Err | Res::Def(DefKind::Trait, _)) {
|
||||
self.dcx().emit_err(MacroExpectedFound {
|
||||
span: path.span,
|
||||
expected: "trait",
|
||||
article: "a",
|
||||
found: res.descr(),
|
||||
macro_path: &pprust::path_to_string(path),
|
||||
remove_surrounding_derive: None,
|
||||
add_as_non_derive: None,
|
||||
});
|
||||
return Ok((self.dummy_ext(kind), Res::Err));
|
||||
}
|
||||
|
||||
return Ok((ext, res));
|
||||
}
|
||||
|
||||
// Report errors for the resolved macro.
|
||||
for segment in &path.segments {
|
||||
if let Some(args) = &segment.args {
|
||||
@ -605,12 +681,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
parent_scope: &ParentScope<'a>,
|
||||
trace: bool,
|
||||
force: bool,
|
||||
) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> {
|
||||
self.resolve_macro_or_delegation_path(path, kind, parent_scope, trace, force, None)
|
||||
}
|
||||
|
||||
fn resolve_macro_or_delegation_path(
|
||||
&mut self,
|
||||
path: &ast::Path,
|
||||
kind: Option<MacroKind>,
|
||||
parent_scope: &ParentScope<'a>,
|
||||
trace: bool,
|
||||
force: bool,
|
||||
deleg_impl: Option<LocalDefId>,
|
||||
) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> {
|
||||
let path_span = path.span;
|
||||
let mut path = Segment::from_path(path);
|
||||
|
||||
// Possibly apply the macro helper hack
|
||||
if kind == Some(MacroKind::Bang)
|
||||
if deleg_impl.is_none()
|
||||
&& kind == Some(MacroKind::Bang)
|
||||
&& path.len() == 1
|
||||
&& path[0].ident.span.ctxt().outer_expn_data().local_inner_macros
|
||||
{
|
||||
@ -618,13 +707,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
path.insert(0, Segment::from_ident(root));
|
||||
}
|
||||
|
||||
let res = if path.len() > 1 {
|
||||
let res = match self.maybe_resolve_path(&path, Some(MacroNS), parent_scope) {
|
||||
let res = if deleg_impl.is_some() || path.len() > 1 {
|
||||
let ns = if deleg_impl.is_some() { TypeNS } else { MacroNS };
|
||||
let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope) {
|
||||
PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res),
|
||||
PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined),
|
||||
PathResult::NonModule(..)
|
||||
| PathResult::Indeterminate
|
||||
| PathResult::Failed { .. } => Err(Determinacy::Determined),
|
||||
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
|
||||
Ok(module.res().unwrap())
|
||||
}
|
||||
PathResult::Module(..) => unreachable!(),
|
||||
};
|
||||
|
||||
@ -636,6 +729,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
kind,
|
||||
*parent_scope,
|
||||
res.ok(),
|
||||
ns,
|
||||
));
|
||||
}
|
||||
|
||||
@ -670,7 +764,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
res
|
||||
};
|
||||
|
||||
res.map(|res| (self.get_macro(res).map(|macro_data| macro_data.ext.clone()), res))
|
||||
let res = res?;
|
||||
let ext = match deleg_impl {
|
||||
Some(impl_def_id) => match res {
|
||||
def::Res::Def(DefKind::Trait, def_id) => {
|
||||
let edition = self.tcx.sess.edition();
|
||||
Some(Lrc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition)))
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
None => self.get_macro(res).map(|macro_data| macro_data.ext.clone()),
|
||||
};
|
||||
Ok((ext, res))
|
||||
}
|
||||
|
||||
pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) {
|
||||
@ -706,14 +811,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
};
|
||||
|
||||
let macro_resolutions = mem::take(&mut self.multi_segment_macro_resolutions);
|
||||
for (mut path, path_span, kind, parent_scope, initial_res) in macro_resolutions {
|
||||
for (mut path, path_span, kind, parent_scope, initial_res, ns) in macro_resolutions {
|
||||
// FIXME: Path resolution will ICE if segment IDs present.
|
||||
for seg in &mut path {
|
||||
seg.id = None;
|
||||
}
|
||||
match self.resolve_path(
|
||||
&path,
|
||||
Some(MacroNS),
|
||||
Some(ns),
|
||||
&parent_scope,
|
||||
Some(Finalize::new(ast::CRATE_NODE_ID, path_span)),
|
||||
None,
|
||||
@ -721,6 +826,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => {
|
||||
check_consistency(self, &path, path_span, kind, initial_res, res)
|
||||
}
|
||||
// This may be a trait for glob delegation expansions.
|
||||
PathResult::Module(ModuleOrUniformRoot::Module(module)) => check_consistency(
|
||||
self,
|
||||
&path,
|
||||
path_span,
|
||||
kind,
|
||||
initial_res,
|
||||
module.res().unwrap(),
|
||||
),
|
||||
path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => {
|
||||
let mut suggestion = None;
|
||||
let (span, label, module) =
|
||||
|
@ -1678,6 +1678,7 @@ symbols! {
|
||||
shadow_call_stack,
|
||||
shl,
|
||||
shl_assign,
|
||||
shorter_tail_lifetimes,
|
||||
should_panic,
|
||||
shr,
|
||||
shr_assign,
|
||||
|
@ -341,37 +341,29 @@ pub(crate) mod rustc {
|
||||
|
||||
// We consider three kinds of enums, each demanding a different
|
||||
// treatment of their layout computation:
|
||||
// 1. enums that are uninhabited
|
||||
// 2. enums for which all but one variant is uninhabited
|
||||
// 3. enums with multiple inhabited variants
|
||||
// 1. enums that are uninhabited ZSTs
|
||||
// 2. enums that delegate their layout to a variant
|
||||
// 3. enums with multiple variants
|
||||
match layout.variants() {
|
||||
_ if layout.abi.is_uninhabited() => {
|
||||
// Uninhabited enums are usually (always?) zero-sized. In
|
||||
// the (unlikely?) event that an uninhabited enum is
|
||||
// non-zero-sized, this assert will trigger an ICE, and this
|
||||
// code should be modified such that a `layout.size` amount
|
||||
// of uninhabited bytes is returned instead.
|
||||
//
|
||||
// Uninhabited enums are currently implemented such that
|
||||
// their layout is described with `Variants::Single`, even
|
||||
// though they don't necessarily have a 'single' variant to
|
||||
// defer to. That said, we don't bother specifically
|
||||
// matching on `Variants::Single` in this arm because the
|
||||
// behavioral principles here remain true even if, for
|
||||
// whatever reason, the compiler describes an uninhabited
|
||||
// enum with `Variants::Multiple`.
|
||||
assert_eq!(layout.size, Size::ZERO);
|
||||
Variants::Single { .. }
|
||||
if layout.abi.is_uninhabited() && layout.size == Size::ZERO =>
|
||||
{
|
||||
// The layout representation of uninhabited, ZST enums is
|
||||
// defined to be like that of the `!` type, as opposed of a
|
||||
// typical enum. Consequently, they cannot be descended into
|
||||
// as if they typical enums. We therefore special-case this
|
||||
// scenario and simply return an uninhabited `Tree`.
|
||||
Ok(Self::uninhabited())
|
||||
}
|
||||
Variants::Single { index } => {
|
||||
// `Variants::Single` on non-uninhabited enums denotes that
|
||||
// `Variants::Single` on enums with variants denotes that
|
||||
// the enum delegates its layout to the variant at `index`.
|
||||
layout_of_variant(*index)
|
||||
}
|
||||
Variants::Multiple { tag_field, .. } => {
|
||||
// `Variants::Multiple` denotes an enum with multiple
|
||||
// inhabited variants. The layout of such an enum is the
|
||||
// disjunction of the layouts of its tagged variants.
|
||||
// variants. The layout of such an enum is the disjunction
|
||||
// of the layouts of its tagged variants.
|
||||
|
||||
// For enums (but not coroutines), the tag field is
|
||||
// currently always the first field of the layout.
|
||||
|
@ -183,7 +183,7 @@
|
||||
//!
|
||||
//! let spinlock_clone = Arc::clone(&spinlock);
|
||||
//!
|
||||
//! let thread = thread::spawn(move|| {
|
||||
//! let thread = thread::spawn(move || {
|
||||
//! spinlock_clone.store(0, Ordering::Release);
|
||||
//! });
|
||||
//!
|
||||
|
@ -20,7 +20,7 @@ use crate::sync::{Condvar, Mutex};
|
||||
/// let c = Arc::clone(&barrier);
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// handles.push(thread::spawn(move|| {
|
||||
/// handles.push(thread::spawn(move || {
|
||||
/// println!("before wait");
|
||||
/// c.wait();
|
||||
/// println!("after wait");
|
||||
@ -115,7 +115,7 @@ impl Barrier {
|
||||
/// let c = Arc::clone(&barrier);
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// handles.push(thread::spawn(move|| {
|
||||
/// handles.push(thread::spawn(move || {
|
||||
/// println!("before wait");
|
||||
/// c.wait();
|
||||
/// println!("after wait");
|
||||
|
@ -88,7 +88,7 @@ impl WaitTimeoutResult {
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// // Inside of our lock, spawn a new thread, and then wait for it to start.
|
||||
/// thread::spawn(move|| {
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
@ -166,7 +166,7 @@ impl Condvar {
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
@ -221,7 +221,7 @@ impl Condvar {
|
||||
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut pending = lock.lock().unwrap();
|
||||
/// *pending = false;
|
||||
@ -280,7 +280,7 @@ impl Condvar {
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
@ -352,7 +352,7 @@ impl Condvar {
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
@ -420,7 +420,7 @@ impl Condvar {
|
||||
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut pending = lock.lock().unwrap();
|
||||
/// *pending = false;
|
||||
@ -484,7 +484,7 @@ impl Condvar {
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
@ -524,7 +524,7 @@ impl Condvar {
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
|
@ -51,7 +51,7 @@
|
||||
//!
|
||||
//! // Create a simple streaming channel
|
||||
//! let (tx, rx) = channel();
|
||||
//! thread::spawn(move|| {
|
||||
//! thread::spawn(move || {
|
||||
//! tx.send(10).unwrap();
|
||||
//! });
|
||||
//! assert_eq!(rx.recv().unwrap(), 10);
|
||||
@ -69,7 +69,7 @@
|
||||
//! let (tx, rx) = channel();
|
||||
//! for i in 0..10 {
|
||||
//! let tx = tx.clone();
|
||||
//! thread::spawn(move|| {
|
||||
//! thread::spawn(move || {
|
||||
//! tx.send(i).unwrap();
|
||||
//! });
|
||||
//! }
|
||||
@ -99,7 +99,7 @@
|
||||
//! use std::sync::mpsc::sync_channel;
|
||||
//!
|
||||
//! let (tx, rx) = sync_channel::<i32>(0);
|
||||
//! thread::spawn(move|| {
|
||||
//! thread::spawn(move || {
|
||||
//! // This will wait for the parent thread to start receiving
|
||||
//! tx.send(53).unwrap();
|
||||
//! });
|
||||
@ -510,7 +510,7 @@ pub enum TrySendError<T> {
|
||||
/// let (sender, receiver) = channel();
|
||||
///
|
||||
/// // Spawn off an expensive computation
|
||||
/// thread::spawn(move|| {
|
||||
/// thread::spawn(move || {
|
||||
/// # fn expensive_computation() {}
|
||||
/// sender.send(expensive_computation()).unwrap();
|
||||
/// });
|
||||
@ -561,7 +561,7 @@ pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
|
||||
/// // this returns immediately
|
||||
/// sender.send(1).unwrap();
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// thread::spawn(move || {
|
||||
/// // this will block until the previous message has been received
|
||||
/// sender.send(2).unwrap();
|
||||
/// });
|
||||
|
@ -62,7 +62,7 @@ use crate::fmt;
|
||||
/// FOO.set(2);
|
||||
///
|
||||
/// // each thread starts out with the initial value of 1
|
||||
/// let t = thread::spawn(move|| {
|
||||
/// let t = thread::spawn(move || {
|
||||
/// assert_eq!(FOO.get(), 1);
|
||||
/// FOO.set(3);
|
||||
/// });
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,8 @@ Apple Mac Catalyst targets.
|
||||
|
||||
## Target maintainers
|
||||
|
||||
- [@badboy](https://github.com/badboy)
|
||||
- [@BlackHoleFox](https://github.com/BlackHoleFox)
|
||||
- [@madsmtm](https://github.com/madsmtm)
|
||||
|
||||
## Requirements
|
||||
|
1
tests/rustdoc-ui/doctest/auxiliary/pub_trait.rs
Normal file
1
tests/rustdoc-ui/doctest/auxiliary/pub_trait.rs
Normal file
@ -0,0 +1 @@
|
||||
pub trait Trait {}
|
31
tests/rustdoc-ui/doctest/non-local-defs-impl.rs
Normal file
31
tests/rustdoc-ui/doctest/non-local-defs-impl.rs
Normal file
@ -0,0 +1,31 @@
|
||||
//@ check-fail
|
||||
//@ edition:2018
|
||||
//@ failure-status: 101
|
||||
//@ aux-build:pub_trait.rs
|
||||
//@ compile-flags: --test --test-args --test-threads=1
|
||||
//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR"
|
||||
//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
|
||||
|
||||
#![doc(test(attr(deny(non_local_definitions))))]
|
||||
#![doc(test(attr(allow(dead_code))))]
|
||||
|
||||
/// This will produce a warning:
|
||||
/// ```rust,no_run
|
||||
/// # extern crate pub_trait;
|
||||
/// # use pub_trait::Trait;
|
||||
///
|
||||
/// struct Local;
|
||||
/// impl Trait for &Local {}
|
||||
/// ```
|
||||
///
|
||||
/// But this shoudln't produce a warning:
|
||||
/// ```rust,no_run
|
||||
/// # extern crate pub_trait;
|
||||
/// # use pub_trait::Trait;
|
||||
///
|
||||
/// struct Local;
|
||||
/// impl Trait for &Local {}
|
||||
///
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
pub fn doctest() {}
|
37
tests/rustdoc-ui/doctest/non-local-defs-impl.stdout
Normal file
37
tests/rustdoc-ui/doctest/non-local-defs-impl.stdout
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
running 2 tests
|
||||
test $DIR/non-local-defs-impl.rs - doctest (line 13) - compile ... FAILED
|
||||
test $DIR/non-local-defs-impl.rs - doctest (line 22) - compile ... ok
|
||||
|
||||
failures:
|
||||
|
||||
---- $DIR/non-local-defs-impl.rs - doctest (line 13) stdout ----
|
||||
error: non-local `impl` definition, `impl` blocks should be written at the same level as their item
|
||||
--> $DIR/non-local-defs-impl.rs:18:1
|
||||
|
|
||||
LL | impl Trait for &Local {}
|
||||
| ^^^^^-----^^^^^------
|
||||
| | |
|
||||
| | `&'_ Local` is not local
|
||||
| | help: remove `&` to make the `impl` local
|
||||
| `Trait` is not local
|
||||
|
|
||||
= note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type
|
||||
= note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl`
|
||||
= help: make this doc-test a standalone test with its own `fn main() { ... }`
|
||||
= note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue <https://github.com/rust-lang/rust/issues/120363>
|
||||
note: the lint level is defined here
|
||||
--> $DIR/non-local-defs-impl.rs:11:9
|
||||
|
|
||||
LL | #![deny(non_local_definitions)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Couldn't compile the test.
|
||||
|
||||
failures:
|
||||
$DIR/non-local-defs-impl.rs - doctest (line 13)
|
||||
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
|
@ -20,15 +20,16 @@ LL | fn needs_async_fn(_: impl async Fn()) {}
|
||||
| ^^^^^^^^^^ required by this bound in `needs_async_fn`
|
||||
|
||||
error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
|
||||
--> $DIR/wrong-fn-kind.rs:9:29
|
||||
--> $DIR/wrong-fn-kind.rs:9:20
|
||||
|
|
||||
LL | fn needs_async_fn(_: impl async Fn()) {}
|
||||
| --------------- change this to accept `FnMut` instead of `Fn`
|
||||
...
|
||||
LL | needs_async_fn(async || {
|
||||
| _____--------------_--------_^
|
||||
| | | |
|
||||
| | | in this closure
|
||||
| -------------- ^-------
|
||||
| | |
|
||||
| _____|______________in this closure
|
||||
| | |
|
||||
| | expects `Fn` instead of `FnMut`
|
||||
LL | |
|
||||
LL | | x += 1;
|
||||
|
@ -18,6 +18,7 @@ async gen fn async_gen_fn() {
|
||||
|
||||
fn main() {
|
||||
let _ = async { break; }; //~ ERROR `break` inside `async` block
|
||||
|
||||
let _ = async || { break; }; //~ ERROR `break` inside `async` closure
|
||||
|
||||
let _ = gen { break; }; //~ ERROR `break` inside `gen` block
|
||||
|
@ -38,16 +38,16 @@ LL | let _ = async { break; };
|
||||
| enclosing `async` block
|
||||
|
||||
error[E0267]: `break` inside `async` closure
|
||||
--> $DIR/break-inside-coroutine-issue-124495.rs:21:24
|
||||
--> $DIR/break-inside-coroutine-issue-124495.rs:22:24
|
||||
|
|
||||
LL | let _ = async || { break; };
|
||||
| --^^^^^---
|
||||
| -----------^^^^^---
|
||||
| | |
|
||||
| | cannot `break` inside `async` closure
|
||||
| enclosing `async` closure
|
||||
|
||||
error[E0267]: `break` inside `gen` block
|
||||
--> $DIR/break-inside-coroutine-issue-124495.rs:23:19
|
||||
--> $DIR/break-inside-coroutine-issue-124495.rs:24:19
|
||||
|
|
||||
LL | let _ = gen { break; };
|
||||
| ------^^^^^---
|
||||
@ -56,7 +56,7 @@ LL | let _ = gen { break; };
|
||||
| enclosing `gen` block
|
||||
|
||||
error[E0267]: `break` inside `async gen` block
|
||||
--> $DIR/break-inside-coroutine-issue-124495.rs:25:25
|
||||
--> $DIR/break-inside-coroutine-issue-124495.rs:26:25
|
||||
|
|
||||
LL | let _ = async gen { break; };
|
||||
| ------------^^^^^---
|
||||
|
32
tests/ui/delegation/body-identity-glob.rs
Normal file
32
tests/ui/delegation/body-identity-glob.rs
Normal file
@ -0,0 +1,32 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) {}
|
||||
fn bar(&self) {}
|
||||
}
|
||||
|
||||
impl Trait for u8 {}
|
||||
|
||||
struct S(u8);
|
||||
|
||||
mod to_import {
|
||||
pub fn check(arg: &u8) -> &u8 { arg }
|
||||
}
|
||||
|
||||
impl Trait for S {
|
||||
reuse Trait::* {
|
||||
use to_import::check;
|
||||
|
||||
let _arr = Some(self.0).map(|x| [x * 2; 3]);
|
||||
check(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = S(0);
|
||||
s.foo();
|
||||
s.bar();
|
||||
}
|
11
tests/ui/delegation/empty-glob.rs
Normal file
11
tests/ui/delegation/empty-glob.rs
Normal file
@ -0,0 +1,11 @@
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {}
|
||||
|
||||
struct S;
|
||||
impl S {
|
||||
reuse Trait::*; //~ ERROR empty glob delegation is not supported
|
||||
}
|
||||
|
||||
fn main() {}
|
8
tests/ui/delegation/empty-glob.stderr
Normal file
8
tests/ui/delegation/empty-glob.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error: empty glob delegation is not supported
|
||||
--> $DIR/empty-glob.rs:8:5
|
||||
|
|
||||
LL | reuse Trait::*;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
12
tests/ui/delegation/glob-bad-path.rs
Normal file
12
tests/ui/delegation/glob-bad-path.rs
Normal file
@ -0,0 +1,12 @@
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {}
|
||||
struct S;
|
||||
|
||||
impl Trait for u8 {
|
||||
reuse unresolved::*; //~ ERROR failed to resolve: use of undeclared crate or module `unresolved`
|
||||
reuse S::*; //~ ERROR expected trait, found struct `S`
|
||||
}
|
||||
|
||||
fn main() {}
|
15
tests/ui/delegation/glob-bad-path.stderr
Normal file
15
tests/ui/delegation/glob-bad-path.stderr
Normal file
@ -0,0 +1,15 @@
|
||||
error: expected trait, found struct `S`
|
||||
--> $DIR/glob-bad-path.rs:9:11
|
||||
|
|
||||
LL | reuse S::*;
|
||||
| ^ not a trait
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `unresolved`
|
||||
--> $DIR/glob-bad-path.rs:8:11
|
||||
|
|
||||
LL | reuse unresolved::*;
|
||||
| ^^^^^^^^^^ use of undeclared crate or module `unresolved`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0433`.
|
33
tests/ui/delegation/glob-glob-conflict.rs
Normal file
33
tests/ui/delegation/glob-glob-conflict.rs
Normal file
@ -0,0 +1,33 @@
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait1 {
|
||||
fn method(&self) -> u8;
|
||||
}
|
||||
trait Trait2 {
|
||||
fn method(&self) -> u8;
|
||||
}
|
||||
trait Trait {
|
||||
fn method(&self) -> u8;
|
||||
}
|
||||
|
||||
impl Trait1 for u8 {
|
||||
fn method(&self) -> u8 { 0 }
|
||||
}
|
||||
impl Trait1 for u16 {
|
||||
fn method(&self) -> u8 { 1 }
|
||||
}
|
||||
impl Trait2 for u8 {
|
||||
fn method(&self) -> u8 { 2 }
|
||||
}
|
||||
|
||||
impl Trait for u8 {
|
||||
reuse Trait1::*;
|
||||
reuse Trait2::*; //~ ERROR duplicate definitions with name `method`
|
||||
}
|
||||
impl Trait for u16 {
|
||||
reuse Trait1::*;
|
||||
reuse Trait1::*; //~ ERROR duplicate definitions with name `method`
|
||||
}
|
||||
|
||||
fn main() {}
|
25
tests/ui/delegation/glob-glob-conflict.stderr
Normal file
25
tests/ui/delegation/glob-glob-conflict.stderr
Normal file
@ -0,0 +1,25 @@
|
||||
error[E0201]: duplicate definitions with name `method`:
|
||||
--> $DIR/glob-glob-conflict.rs:26:5
|
||||
|
|
||||
LL | fn method(&self) -> u8;
|
||||
| ----------------------- item in trait
|
||||
...
|
||||
LL | reuse Trait1::*;
|
||||
| ---------------- previous definition here
|
||||
LL | reuse Trait2::*;
|
||||
| ^^^^^^^^^^^^^^^^ duplicate definition
|
||||
|
||||
error[E0201]: duplicate definitions with name `method`:
|
||||
--> $DIR/glob-glob-conflict.rs:30:5
|
||||
|
|
||||
LL | fn method(&self) -> u8;
|
||||
| ----------------------- item in trait
|
||||
...
|
||||
LL | reuse Trait1::*;
|
||||
| ---------------- previous definition here
|
||||
LL | reuse Trait1::*;
|
||||
| ^^^^^^^^^^^^^^^^ duplicate definition
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0201`.
|
36
tests/ui/delegation/glob-glob.rs
Normal file
36
tests/ui/delegation/glob-glob.rs
Normal file
@ -0,0 +1,36 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
mod inner {
|
||||
pub trait TraitFoo {
|
||||
fn foo(&self) -> u8;
|
||||
}
|
||||
pub trait TraitBar {
|
||||
fn bar(&self) -> u8;
|
||||
}
|
||||
|
||||
impl TraitFoo for u8 {
|
||||
fn foo(&self) -> u8 { 0 }
|
||||
}
|
||||
impl TraitBar for u8 {
|
||||
fn bar(&self) -> u8 { 1 }
|
||||
}
|
||||
}
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) -> u8;
|
||||
fn bar(&self) -> u8;
|
||||
}
|
||||
|
||||
impl Trait for u8 {
|
||||
reuse inner::TraitFoo::*;
|
||||
reuse inner::TraitBar::*;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let u = 0u8;
|
||||
u.foo();
|
||||
u.bar();
|
||||
}
|
38
tests/ui/delegation/glob-non-fn.rs
Normal file
38
tests/ui/delegation/glob-non-fn.rs
Normal file
@ -0,0 +1,38 @@
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn method(&self);
|
||||
const CONST: u8;
|
||||
type Type;
|
||||
#[allow(non_camel_case_types)]
|
||||
type method;
|
||||
}
|
||||
|
||||
impl Trait for u8 {
|
||||
fn method(&self) {}
|
||||
const CONST: u8 = 0;
|
||||
type Type = u8;
|
||||
type method = u8;
|
||||
}
|
||||
|
||||
struct Good(u8);
|
||||
impl Trait for Good {
|
||||
reuse Trait::* { &self.0 }
|
||||
// Explicit definitions for non-delegatable items.
|
||||
const CONST: u8 = 0;
|
||||
type Type = u8;
|
||||
type method = u8;
|
||||
}
|
||||
|
||||
struct Bad(u8);
|
||||
impl Trait for Bad { //~ ERROR not all trait items implemented, missing: `CONST`, `Type`, `method`
|
||||
reuse Trait::* { &self.0 }
|
||||
//~^ ERROR item `CONST` is an associated method, which doesn't match its trait `Trait`
|
||||
//~| ERROR item `Type` is an associated method, which doesn't match its trait `Trait`
|
||||
//~| ERROR duplicate definitions with name `method`
|
||||
//~| ERROR expected function, found associated constant `Trait::CONST`
|
||||
//~| ERROR expected function, found associated type `Trait::Type`
|
||||
}
|
||||
|
||||
fn main() {}
|
62
tests/ui/delegation/glob-non-fn.stderr
Normal file
62
tests/ui/delegation/glob-non-fn.stderr
Normal file
@ -0,0 +1,62 @@
|
||||
error[E0324]: item `CONST` is an associated method, which doesn't match its trait `Trait`
|
||||
--> $DIR/glob-non-fn.rs:30:5
|
||||
|
|
||||
LL | const CONST: u8;
|
||||
| ---------------- item in trait
|
||||
...
|
||||
LL | reuse Trait::* { &self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait
|
||||
|
||||
error[E0324]: item `Type` is an associated method, which doesn't match its trait `Trait`
|
||||
--> $DIR/glob-non-fn.rs:30:5
|
||||
|
|
||||
LL | type Type;
|
||||
| ---------- item in trait
|
||||
...
|
||||
LL | reuse Trait::* { &self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait
|
||||
|
||||
error[E0201]: duplicate definitions with name `method`:
|
||||
--> $DIR/glob-non-fn.rs:30:5
|
||||
|
|
||||
LL | fn method(&self);
|
||||
| ----------------- item in trait
|
||||
...
|
||||
LL | reuse Trait::* { &self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| duplicate definition
|
||||
| previous definition here
|
||||
|
||||
error[E0423]: expected function, found associated constant `Trait::CONST`
|
||||
--> $DIR/glob-non-fn.rs:30:11
|
||||
|
|
||||
LL | reuse Trait::* { &self.0 }
|
||||
| ^^^^^ not a function
|
||||
|
||||
error[E0423]: expected function, found associated type `Trait::Type`
|
||||
--> $DIR/glob-non-fn.rs:30:11
|
||||
|
|
||||
LL | reuse Trait::* { &self.0 }
|
||||
| ^^^^^
|
||||
|
|
||||
= note: can't use a type alias as a constructor
|
||||
|
||||
error[E0046]: not all trait items implemented, missing: `CONST`, `Type`, `method`
|
||||
--> $DIR/glob-non-fn.rs:29:1
|
||||
|
|
||||
LL | const CONST: u8;
|
||||
| --------------- `CONST` from trait
|
||||
LL | type Type;
|
||||
| --------- `Type` from trait
|
||||
LL | #[allow(non_camel_case_types)]
|
||||
LL | type method;
|
||||
| ----------- `method` from trait
|
||||
...
|
||||
LL | impl Trait for Bad {
|
||||
| ^^^^^^^^^^^^^^^^^^ missing `CONST`, `Type`, `method` in implementation
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0046, E0201, E0324, E0423.
|
||||
For more information about an error, try `rustc --explain E0046`.
|
20
tests/ui/delegation/glob-non-impl.rs
Normal file
20
tests/ui/delegation/glob-non-impl.rs
Normal file
@ -0,0 +1,20 @@
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn method() {}
|
||||
}
|
||||
|
||||
reuse Trait::*; //~ ERROR glob delegation is only supported in impls
|
||||
|
||||
trait OtherTrait {
|
||||
reuse Trait::*; //~ ERROR glob delegation is only supported in impls
|
||||
}
|
||||
|
||||
extern {
|
||||
reuse Trait::*; //~ ERROR delegation is not supported in `extern` blocks
|
||||
}
|
||||
|
||||
fn main() {
|
||||
reuse Trait::*; //~ ERROR glob delegation is only supported in impls
|
||||
}
|
26
tests/ui/delegation/glob-non-impl.stderr
Normal file
26
tests/ui/delegation/glob-non-impl.stderr
Normal file
@ -0,0 +1,26 @@
|
||||
error: delegation is not supported in `extern` blocks
|
||||
--> $DIR/glob-non-impl.rs:15:5
|
||||
|
|
||||
LL | reuse Trait::*;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: glob delegation is only supported in impls
|
||||
--> $DIR/glob-non-impl.rs:8:1
|
||||
|
|
||||
LL | reuse Trait::*;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: glob delegation is only supported in impls
|
||||
--> $DIR/glob-non-impl.rs:11:5
|
||||
|
|
||||
LL | reuse Trait::*;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: glob delegation is only supported in impls
|
||||
--> $DIR/glob-non-impl.rs:19:5
|
||||
|
|
||||
LL | reuse Trait::*;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
37
tests/ui/delegation/glob-override.rs
Normal file
37
tests/ui/delegation/glob-override.rs
Normal file
@ -0,0 +1,37 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) -> u8;
|
||||
fn bar(&self) -> u8;
|
||||
}
|
||||
|
||||
impl Trait for u8 {
|
||||
fn foo(&self) -> u8 { 0 }
|
||||
fn bar(&self) -> u8 { 1 }
|
||||
}
|
||||
|
||||
struct S(u8);
|
||||
struct Z(u8);
|
||||
|
||||
impl Trait for S {
|
||||
reuse Trait::* { &self.0 }
|
||||
fn bar(&self) -> u8 { 2 }
|
||||
}
|
||||
|
||||
impl Trait for Z {
|
||||
reuse Trait::* { &self.0 }
|
||||
reuse Trait::bar { &self.0 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = S(2);
|
||||
s.foo();
|
||||
s.bar();
|
||||
|
||||
let z = Z(2);
|
||||
z.foo();
|
||||
z.bar();
|
||||
}
|
35
tests/ui/delegation/glob.rs
Normal file
35
tests/ui/delegation/glob.rs
Normal file
@ -0,0 +1,35 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) -> u8;
|
||||
fn bar(&self) -> u8;
|
||||
}
|
||||
|
||||
impl Trait for u8 {
|
||||
fn foo(&self) -> u8 { 0 }
|
||||
fn bar(&self) -> u8 { 1 }
|
||||
}
|
||||
|
||||
struct S(u8);
|
||||
struct Z(u8);
|
||||
|
||||
impl Trait for S {
|
||||
reuse Trait::* { &self.0 }
|
||||
}
|
||||
|
||||
impl Trait for Z {
|
||||
reuse <u8 as Trait>::* { &self.0 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = S(2);
|
||||
s.foo();
|
||||
s.bar();
|
||||
|
||||
let z = Z(3);
|
||||
z.foo();
|
||||
z.bar();
|
||||
}
|
26
tests/ui/delegation/macro-inside-glob.rs
Normal file
26
tests/ui/delegation/macro-inside-glob.rs
Normal file
@ -0,0 +1,26 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) -> u8 { 0 }
|
||||
fn bar(&self) -> u8 { 1 }
|
||||
}
|
||||
|
||||
impl Trait for u8 {}
|
||||
|
||||
struct S(u8);
|
||||
|
||||
// Macro expansion works inside delegation items.
|
||||
macro_rules! u8 { () => { u8 } }
|
||||
macro_rules! self_0 { ($self:ident) => { &$self.0 } }
|
||||
impl Trait for S {
|
||||
reuse <u8!() as Trait>::* { self_0!(self) }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = S(2);
|
||||
s.foo();
|
||||
s.bar();
|
||||
}
|
8
tests/ui/drop/auxiliary/edition-2021-macros.rs
Normal file
8
tests/ui/drop/auxiliary/edition-2021-macros.rs
Normal file
@ -0,0 +1,8 @@
|
||||
//@ edition:2021
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! edition_2021_block {
|
||||
($($c:tt)*) => {
|
||||
{ $($c)* }
|
||||
}
|
||||
}
|
9
tests/ui/drop/auxiliary/edition-2024-macros.rs
Normal file
9
tests/ui/drop/auxiliary/edition-2024-macros.rs
Normal file
@ -0,0 +1,9 @@
|
||||
//@ edition:2024
|
||||
//@ compile-flags: -Zunstable-options
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! edition_2024_block {
|
||||
($($c:tt)*) => {
|
||||
{ $($c)* }
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
error[E0716]: temporary value dropped while borrowed
|
||||
--> $DIR/tail-expr-drop-order-negative.rs:11:15
|
||||
|
|
||||
LL | x.replace(std::cell::RefCell::new(123).borrow()).is_some()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
|
||||
| |
|
||||
| creates a temporary value which is freed while still in use
|
||||
LL |
|
||||
LL | }
|
||||
| - borrow might be used here, when `x` is dropped and runs the destructor for type `Option<Ref<'_, i32>>`
|
||||
|
|
||||
= note: consider using a `let` binding to create a longer lived value
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0716`.
|
17
tests/ui/drop/tail-expr-drop-order-negative.rs
Normal file
17
tests/ui/drop/tail-expr-drop-order-negative.rs
Normal file
@ -0,0 +1,17 @@
|
||||
//@ revisions: edition2021 edition2024
|
||||
//@ [edition2024] compile-flags: -Zunstable-options
|
||||
//@ [edition2024] edition: 2024
|
||||
//@ [edition2021] check-pass
|
||||
|
||||
#![feature(shorter_tail_lifetimes)]
|
||||
|
||||
fn why_would_you_do_this() -> bool {
|
||||
let mut x = None;
|
||||
// Make a temporary `RefCell` and put a `Ref` that borrows it in `x`.
|
||||
x.replace(std::cell::RefCell::new(123).borrow()).is_some()
|
||||
//[edition2024]~^ ERROR: temporary value dropped while borrowed
|
||||
}
|
||||
|
||||
fn main() {
|
||||
why_would_you_do_this();
|
||||
}
|
108
tests/ui/drop/tail-expr-drop-order.rs
Normal file
108
tests/ui/drop/tail-expr-drop-order.rs
Normal file
@ -0,0 +1,108 @@
|
||||
//@ aux-build:edition-2021-macros.rs
|
||||
//@ aux-build:edition-2024-macros.rs
|
||||
//@ compile-flags: -Z validate-mir -Zunstable-options
|
||||
//@ edition: 2024
|
||||
//@ run-pass
|
||||
|
||||
#![feature(shorter_tail_lifetimes)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate edition_2021_macros;
|
||||
#[macro_use]
|
||||
extern crate edition_2024_macros;
|
||||
use std::cell::RefCell;
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[derive(Default)]
|
||||
struct DropOrderCollector(RefCell<Vec<u32>>);
|
||||
|
||||
struct LoudDrop<'a>(&'a DropOrderCollector, u32);
|
||||
|
||||
impl Drop for LoudDrop<'_> {
|
||||
fn drop(&mut self) {
|
||||
println!("{}", self.1);
|
||||
self.0.0.borrow_mut().push(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
impl DropOrderCollector {
|
||||
fn option_loud_drop(&self, n: u32) -> Option<LoudDrop> {
|
||||
Some(LoudDrop(self, n))
|
||||
}
|
||||
|
||||
fn loud_drop(&self, n: u32) -> LoudDrop {
|
||||
LoudDrop(self, n)
|
||||
}
|
||||
|
||||
fn assert_sorted(&self, expected: usize) {
|
||||
let result = self.0.borrow();
|
||||
assert_eq!(result.len(), expected);
|
||||
for i in 1..result.len() {
|
||||
assert!(
|
||||
result[i - 1] < result[i],
|
||||
"inversion at {} ({} followed by {})",
|
||||
i - 1,
|
||||
result[i - 1],
|
||||
result[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn edition_2021_around_2021() {
|
||||
let c = DropOrderCollector::default();
|
||||
let _ = edition_2021_block! {
|
||||
let a = c.loud_drop(1);
|
||||
edition_2021_block! {
|
||||
let b = c.loud_drop(0);
|
||||
c.loud_drop(2).1
|
||||
}
|
||||
};
|
||||
c.assert_sorted(3);
|
||||
}
|
||||
|
||||
fn edition_2021_around_2024() {
|
||||
let c = DropOrderCollector::default();
|
||||
let _ = edition_2021_block! {
|
||||
let a = c.loud_drop(2);
|
||||
edition_2024_block! {
|
||||
let b = c.loud_drop(1);
|
||||
c.loud_drop(0).1
|
||||
}
|
||||
};
|
||||
c.assert_sorted(3);
|
||||
}
|
||||
|
||||
fn edition_2024_around_2021() {
|
||||
let c = DropOrderCollector::default();
|
||||
let _ = edition_2024_block! {
|
||||
let a = c.loud_drop(2);
|
||||
edition_2021_block! {
|
||||
let b = c.loud_drop(0);
|
||||
c.loud_drop(1).1
|
||||
}
|
||||
};
|
||||
c.assert_sorted(3);
|
||||
}
|
||||
|
||||
fn edition_2024_around_2024() {
|
||||
let c = DropOrderCollector::default();
|
||||
let _ = edition_2024_block! {
|
||||
let a = c.loud_drop(2);
|
||||
edition_2024_block! {
|
||||
let b = c.loud_drop(1);
|
||||
c.loud_drop(0).1
|
||||
}
|
||||
};
|
||||
c.assert_sorted(3);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
edition_2021_around_2021();
|
||||
edition_2021_around_2024();
|
||||
edition_2024_around_2021();
|
||||
edition_2024_around_2024();
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fn f() -> usize {
|
||||
let c = std::cell::RefCell::new("..");
|
||||
c.borrow().len() //~ ERROR: `c` does not live long enough
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = f();
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
error[E0597]: `c` does not live long enough
|
||||
--> $DIR/feature-gate-shorter_tail_lifetimes.rs:3:5
|
||||
|
|
||||
LL | let c = std::cell::RefCell::new("..");
|
||||
| - binding `c` declared here
|
||||
LL | c.borrow().len()
|
||||
| ^---------
|
||||
| |
|
||||
| borrowed value does not live long enough
|
||||
| a temporary with access to the borrow is created here ...
|
||||
LL | }
|
||||
| -
|
||||
| |
|
||||
| `c` dropped here while still borrowed
|
||||
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, &str>`
|
||||
|
|
||||
= note: the temporary is part of an expression at the end of a block;
|
||||
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
|
||||
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
||||
|
|
||||
LL | let x = c.borrow().len(); x
|
||||
| +++++++ +++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0597`.
|
26
tests/ui/lifetimes/refcell-in-tail-expr.edition2021.stderr
Normal file
26
tests/ui/lifetimes/refcell-in-tail-expr.edition2021.stderr
Normal file
@ -0,0 +1,26 @@
|
||||
error[E0597]: `cell` does not live long enough
|
||||
--> $DIR/refcell-in-tail-expr.rs:12:27
|
||||
|
|
||||
LL | let cell = std::cell::RefCell::new(0u8);
|
||||
| ---- binding `cell` declared here
|
||||
LL |
|
||||
LL | if let Ok(mut byte) = cell.try_borrow_mut() {
|
||||
| ^^^^-----------------
|
||||
| |
|
||||
| borrowed value does not live long enough
|
||||
| a temporary with access to the borrow is created here ...
|
||||
...
|
||||
LL | }
|
||||
| -
|
||||
| |
|
||||
| `cell` dropped here while still borrowed
|
||||
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Result<RefMut<'_, u8>, BorrowMutError>`
|
||||
|
|
||||
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
||||
|
|
||||
LL | };
|
||||
| +
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0597`.
|
16
tests/ui/lifetimes/refcell-in-tail-expr.rs
Normal file
16
tests/ui/lifetimes/refcell-in-tail-expr.rs
Normal file
@ -0,0 +1,16 @@
|
||||
//@ revisions: edition2021 edition2024
|
||||
//@ [edition2021] edition: 2021
|
||||
//@ [edition2024] edition: 2024
|
||||
//@ [edition2024] compile-flags: -Zunstable-options
|
||||
//@ [edition2024] check-pass
|
||||
|
||||
#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))]
|
||||
|
||||
fn main() {
|
||||
let cell = std::cell::RefCell::new(0u8);
|
||||
|
||||
if let Ok(mut byte) = cell.try_borrow_mut() {
|
||||
//[edition2021]~^ ERROR: `cell` does not live long enough
|
||||
*byte = 1;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
error[E0597]: `c` does not live long enough
|
||||
--> $DIR/shorter-tail-expr-lifetime.rs:10:5
|
||||
|
|
||||
LL | let c = std::cell::RefCell::new("..");
|
||||
| - binding `c` declared here
|
||||
LL | c.borrow().len()
|
||||
| ^---------
|
||||
| |
|
||||
| borrowed value does not live long enough
|
||||
| a temporary with access to the borrow is created here ...
|
||||
LL | }
|
||||
| -
|
||||
| |
|
||||
| `c` dropped here while still borrowed
|
||||
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, &str>`
|
||||
|
|
||||
= note: the temporary is part of an expression at the end of a block;
|
||||
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
|
||||
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
||||
|
|
||||
LL | let x = c.borrow().len(); x
|
||||
| +++++++ +++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0597`.
|
15
tests/ui/lifetimes/shorter-tail-expr-lifetime.rs
Normal file
15
tests/ui/lifetimes/shorter-tail-expr-lifetime.rs
Normal file
@ -0,0 +1,15 @@
|
||||
//@ revisions: edition2021 edition2024
|
||||
//@ [edition2024] compile-flags: -Zunstable-options
|
||||
//@ [edition2024] edition: 2024
|
||||
//@ [edition2024] run-pass
|
||||
|
||||
#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))]
|
||||
|
||||
fn f() -> usize {
|
||||
let c = std::cell::RefCell::new("..");
|
||||
c.borrow().len() //[edition2021]~ ERROR: `c` does not live long enough
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = f();
|
||||
}
|
9
tests/ui/lifetimes/tail-expr-in-nested-expr.rs
Normal file
9
tests/ui/lifetimes/tail-expr-in-nested-expr.rs
Normal file
@ -0,0 +1,9 @@
|
||||
//@ edition: 2024
|
||||
//@ compile-flags: -Zunstable-options
|
||||
|
||||
#![feature(shorter_tail_lifetimes)]
|
||||
|
||||
fn main() {
|
||||
let _ = { String::new().as_str() }.len();
|
||||
//~^ ERROR temporary value dropped while borrowed
|
||||
}
|
15
tests/ui/lifetimes/tail-expr-in-nested-expr.stderr
Normal file
15
tests/ui/lifetimes/tail-expr-in-nested-expr.stderr
Normal file
@ -0,0 +1,15 @@
|
||||
error[E0716]: temporary value dropped while borrowed
|
||||
--> $DIR/tail-expr-in-nested-expr.rs:7:15
|
||||
|
|
||||
LL | let _ = { String::new().as_str() }.len();
|
||||
| ^^^^^^^^^^^^^---------
|
||||
| | |
|
||||
| | temporary value is freed at the end of this statement
|
||||
| creates a temporary value which is freed while still in use
|
||||
| borrow later used here
|
||||
|
|
||||
= note: consider using a `let` binding to create a longer lived value
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0716`.
|
29
tests/ui/lifetimes/tail-expr-lock-poisoning.rs
Normal file
29
tests/ui/lifetimes/tail-expr-lock-poisoning.rs
Normal file
@ -0,0 +1,29 @@
|
||||
//@ revisions: edition2021 edition2024
|
||||
//@ ignore-wasm no panic or subprocess support
|
||||
//@ [edition2024] compile-flags: -Zunstable-options
|
||||
//@ [edition2024] edition: 2024
|
||||
//@ run-pass
|
||||
#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))]
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
struct PanicOnDrop;
|
||||
impl Drop for PanicOnDrop {
|
||||
fn drop(&mut self) {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
fn f(m: &Mutex<i32>) -> i32 {
|
||||
let _x = PanicOnDrop;
|
||||
*m.lock().unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let m = Mutex::new(0);
|
||||
let _ = std::panic::catch_unwind(|| f(&m));
|
||||
#[cfg(edition2024)]
|
||||
assert!(m.lock().is_ok());
|
||||
#[cfg(edition2021)]
|
||||
assert!(m.lock().is_err());
|
||||
}
|
@ -17,14 +17,14 @@ fn foo(x: &u32) -> &u32 {
|
||||
fn baz(x: &u32) -> &&u32 {
|
||||
let x = 22;
|
||||
&&x
|
||||
//~^ ERROR cannot return value referencing local variable
|
||||
//~^ ERROR cannot return value referencing local variable `x`
|
||||
//~| ERROR cannot return reference to temporary value
|
||||
}
|
||||
|
||||
fn foobazbar<'a>(x: u32, y: &'a u32) -> &'a u32 {
|
||||
let x = 22;
|
||||
&x
|
||||
//~^ ERROR cannot return reference to local variable
|
||||
//~^ ERROR cannot return reference to local variable `x`
|
||||
}
|
||||
|
||||
fn foobar<'a>(x: &'a u32) -> &'a u32 {
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Checks existence of a note for "a caller chooses ty for ty param" upon return ty mismatch.
|
||||
// Checks existence or absence of a note for "a caller chooses ty for ty param" upon return ty
|
||||
// mismatch.
|
||||
|
||||
fn f<T>() -> (T,) {
|
||||
(0,) //~ ERROR mismatched types
|
||||
@ -14,6 +15,14 @@ fn h() -> u8 {
|
||||
0u8
|
||||
}
|
||||
|
||||
// This case was reported in <https://github.com/rust-lang/rust/issues/126547> where it doesn't
|
||||
// make sense to make the "note caller chooses ty for ty param" note if the found type contains
|
||||
// the ty param...
|
||||
fn k<T>(_t: &T) -> T {
|
||||
_t
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
fn main() {
|
||||
f::<()>();
|
||||
g::<(), ()>;
|
||||
|
@ -1,5 +1,5 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/return-ty-mismatch-note.rs:4:6
|
||||
--> $DIR/return-ty-mismatch-note.rs:5:6
|
||||
|
|
||||
LL | fn f<T>() -> (T,) {
|
||||
| - expected this type parameter
|
||||
@ -10,7 +10,7 @@ LL | (0,)
|
||||
found type `{integer}`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/return-ty-mismatch-note.rs:8:6
|
||||
--> $DIR/return-ty-mismatch-note.rs:9:6
|
||||
|
|
||||
LL | fn g<U, V>() -> (U, V) {
|
||||
| - expected this type parameter
|
||||
@ -21,7 +21,7 @@ LL | (0, "foo")
|
||||
found type `{integer}`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/return-ty-mismatch-note.rs:8:9
|
||||
--> $DIR/return-ty-mismatch-note.rs:9:9
|
||||
|
|
||||
LL | fn g<U, V>() -> (U, V) {
|
||||
| - expected this type parameter
|
||||
@ -31,6 +31,19 @@ LL | (0, "foo")
|
||||
= note: expected type parameter `V`
|
||||
found reference `&'static str`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/return-ty-mismatch-note.rs:22:5
|
||||
|
|
||||
LL | fn k<T>(_t: &T) -> T {
|
||||
| - - expected `T` because of return type
|
||||
| |
|
||||
| expected this type parameter
|
||||
LL | _t
|
||||
| ^^ expected type parameter `T`, found `&T`
|
||||
|
|
||||
= note: expected type parameter `_`
|
||||
found reference `&_`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
@ -10,7 +10,6 @@ LL | t.clone()
|
||||
|
|
||||
= note: expected type parameter `_`
|
||||
found reference `&_`
|
||||
= note: the caller chooses a type for `T` which can be different from `&T`
|
||||
note: `T` does not implement `Clone`, so `&T` was cloned instead
|
||||
--> $DIR/clone-on-unconstrained-borrowed-type-param.rs:3:5
|
||||
|
|
||||
|
@ -19,8 +19,14 @@ enum SingleUninhabited {
|
||||
Y(Uninhabited),
|
||||
}
|
||||
|
||||
enum MultipleUninhabited {
|
||||
X(u8, Uninhabited),
|
||||
Y(Uninhabited, u16),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_transmutable::<Uninhabited>();
|
||||
assert_transmutable::<SingleInhabited>();
|
||||
assert_transmutable::<SingleUninhabited>();
|
||||
assert_transmutable::<MultipleUninhabited>();
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ fn void() {
|
||||
}
|
||||
|
||||
// Non-ZST uninhabited types are, nonetheless, uninhabited.
|
||||
fn yawning_void() {
|
||||
fn yawning_void_struct() {
|
||||
enum Void {}
|
||||
|
||||
struct YawningVoid(Void, u128);
|
||||
@ -49,6 +49,28 @@ fn yawning_void() {
|
||||
assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted
|
||||
}
|
||||
|
||||
// Non-ZST uninhabited types are, nonetheless, uninhabited.
|
||||
fn yawning_void_enum() {
|
||||
enum Void {}
|
||||
|
||||
enum YawningVoid {
|
||||
A(Void, u128),
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(std::mem::size_of::<YawningVoid>() == std::mem::size_of::<u128>());
|
||||
// Just to be sure the above constant actually evaluated:
|
||||
assert!(false); //~ ERROR: evaluation of constant value failed
|
||||
};
|
||||
|
||||
// This transmutation is vacuously acceptable; since one cannot construct a
|
||||
// `Void`, unsoundness cannot directly arise from transmuting a void into
|
||||
// anything else.
|
||||
assert::is_maybe_transmutable::<YawningVoid, u128>();
|
||||
|
||||
assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted
|
||||
}
|
||||
|
||||
// References to uninhabited types are, logically, uninhabited, but for layout
|
||||
// purposes are not ZSTs, and aren't treated as uninhabited when they appear in
|
||||
// enum variants.
|
||||
|
@ -7,10 +7,18 @@ LL | assert!(false);
|
||||
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/uninhabited.rs:65:9
|
||||
--> $DIR/uninhabited.rs:63:9
|
||||
|
|
||||
LL | assert!(false);
|
||||
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:65:9
|
||||
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:63:9
|
||||
|
|
||||
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/uninhabited.rs:87:9
|
||||
|
|
||||
LL | assert!(false);
|
||||
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:87:9
|
||||
|
|
||||
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
@ -36,11 +44,33 @@ LL | | }
|
||||
LL | | }>
|
||||
| |__________^ required by this bound in `is_maybe_transmutable`
|
||||
|
||||
error[E0277]: `()` cannot be safely transmuted into `yawning_void::Void`
|
||||
error[E0277]: `()` cannot be safely transmuted into `yawning_void_struct::Void`
|
||||
--> $DIR/uninhabited.rs:49:41
|
||||
|
|
||||
LL | assert::is_maybe_transmutable::<(), Void>();
|
||||
| ^^^^ `yawning_void::Void` is uninhabited
|
||||
| ^^^^ `yawning_void_struct::Void` is uninhabited
|
||||
|
|
||||
note: required by a bound in `is_maybe_transmutable`
|
||||
--> $DIR/uninhabited.rs:10:14
|
||||
|
|
||||
LL | pub fn is_maybe_transmutable<Src, Dst>()
|
||||
| --------------------- required by a bound in this function
|
||||
LL | where
|
||||
LL | Dst: BikeshedIntrinsicFrom<Src, {
|
||||
| ______________^
|
||||
LL | | Assume {
|
||||
LL | | alignment: true,
|
||||
LL | | lifetimes: true,
|
||||
... |
|
||||
LL | | }
|
||||
LL | | }>
|
||||
| |__________^ required by this bound in `is_maybe_transmutable`
|
||||
|
||||
error[E0277]: `()` cannot be safely transmuted into `yawning_void_enum::Void`
|
||||
--> $DIR/uninhabited.rs:71:41
|
||||
|
|
||||
LL | assert::is_maybe_transmutable::<(), Void>();
|
||||
| ^^^^ `yawning_void_enum::Void` is uninhabited
|
||||
|
|
||||
note: required by a bound in `is_maybe_transmutable`
|
||||
--> $DIR/uninhabited.rs:10:14
|
||||
@ -59,7 +89,7 @@ LL | | }>
|
||||
| |__________^ required by this bound in `is_maybe_transmutable`
|
||||
|
||||
error[E0277]: `u128` cannot be safely transmuted into `DistantVoid`
|
||||
--> $DIR/uninhabited.rs:70:43
|
||||
--> $DIR/uninhabited.rs:92:43
|
||||
|
|
||||
LL | assert::is_maybe_transmutable::<u128, DistantVoid>();
|
||||
| ^^^^^^^^^^^ at least one value of `u128` isn't a bit-valid value of `DistantVoid`
|
||||
@ -80,7 +110,7 @@ LL | | }
|
||||
LL | | }>
|
||||
| |__________^ required by this bound in `is_maybe_transmutable`
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0080, E0277.
|
||||
For more information about an error, try `rustc --explain E0080`.
|
||||
|
Loading…
Reference in New Issue
Block a user