Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ parse_default_not_followed_by_item = `default` is not followed by an item
.label = the `default` qualifier
.note = only `fn`, `const`, `type`, or `impl` items may be prefixed by `default`
parse_delegation_non_trait_impl_reuse = only trait impls can be reused
parse_do_catch_syntax_removed = found removed `do catch` syntax
.note = following RFC #2388, the new non-placeholder syntax is `try`
.suggestion = replace with the new syntax
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3671,3 +3671,10 @@ pub(crate) struct VarargsWithoutPattern {
#[suggestion(code = "_: ...", applicability = "machine-applicable")]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_delegation_non_trait_impl_reuse)]
pub(crate) struct ImplReuseInherentImpl {
#[primary_span]
pub span: Span,
}
144 changes: 111 additions & 33 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ impl<'a> Parser<'a> {
}
}

enum ReuseKind {
Path,
Impl,
}

impl<'a> Parser<'a> {
pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Box<Item>>> {
let fn_parse_mode =
Expand Down Expand Up @@ -249,9 +254,9 @@ impl<'a> Parser<'a> {
} else if self.check_keyword_case(exp!(Trait), case) || self.check_trait_front_matter() {
// TRAIT ITEM
self.parse_item_trait(attrs, lo)?
} else if self.check_impl_frontmatter() {
} else if self.check_impl_frontmatter(0) {
// IMPL ITEM
self.parse_item_impl(attrs, def_())?
self.parse_item_impl(attrs, def_(), false)?
} else if let Const::Yes(const_span) = self.parse_constness(case) {
// CONST ITEM
self.recover_const_mut(const_span);
Expand All @@ -265,8 +270,8 @@ impl<'a> Parser<'a> {
rhs,
define_opaque: None,
}))
} else if self.is_reuse_path_item() {
self.parse_item_delegation()?
} else if let Some(kind) = self.is_reuse_item() {
self.parse_item_delegation(attrs, def_(), kind)?
} else if self.check_keyword_case(exp!(Mod), case)
|| self.check_keyword_case(exp!(Unsafe), case) && self.is_keyword_ahead(1, &[kw::Mod])
{
Expand Down Expand Up @@ -367,16 +372,25 @@ impl<'a> Parser<'a> {
/// When parsing a statement, would the start of a path be an item?
pub(super) fn is_path_start_item(&mut self) -> bool {
self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }`
|| self.is_reuse_path_item()
|| self.is_reuse_item().is_some() // yes: `reuse impl Trait for Struct { self.0 }`, yes: `reuse some_path::foo;`
|| self.check_trait_front_matter() // no: `auto::b`, yes: `auto trait X { .. }`
|| self.is_async_fn() // no(2015): `async::b`, yes: `async fn`
|| matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac`
}

fn is_reuse_path_item(&mut self) -> bool {
fn is_reuse_item(&mut self) -> Option<ReuseKind> {
if !self.token.is_keyword(kw::Reuse) {
return None;
}

// no: `reuse ::path` for compatibility reasons with macro invocations
self.token.is_keyword(kw::Reuse)
&& self.look_ahead(1, |t| t.is_path_start() && *t != token::PathSep)
if self.look_ahead(1, |t| t.is_path_start() && *t != token::PathSep) {
Some(ReuseKind::Path)
} else if self.check_impl_frontmatter(1) {
Some(ReuseKind::Impl)
} else {
None
}
}

/// Are we sure this could not possibly be a macro invocation?
Expand Down Expand Up @@ -560,6 +574,7 @@ impl<'a> Parser<'a> {
&mut self,
attrs: &mut AttrVec,
defaultness: Defaultness,
is_reuse: bool,
) -> PResult<'a, ItemKind> {
let mut constness = self.parse_constness(Case::Sensitive);
let safety = self.parse_safety(Case::Sensitive);
Expand Down Expand Up @@ -628,7 +643,11 @@ impl<'a> Parser<'a> {

generics.where_clause = self.parse_where_clause()?;

let impl_items = self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?;
let impl_items = if is_reuse {
Default::default()
} else {
self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?
};

let (of_trait, self_ty) = match ty_second {
Some(ty_second) => {
Expand Down Expand Up @@ -699,10 +718,76 @@ impl<'a> Parser<'a> {
Ok(ItemKind::Impl(Impl { generics, of_trait, self_ty, items: impl_items, constness }))
}

fn parse_item_delegation(&mut self) -> PResult<'a, ItemKind> {
fn parse_item_delegation(
&mut self,
attrs: &mut AttrVec,
defaultness: Defaultness,
kind: ReuseKind,
) -> PResult<'a, ItemKind> {
let span = self.token.span;
self.expect_keyword(exp!(Reuse))?;

let item_kind = match kind {
ReuseKind::Path => self.parse_path_like_delegation(),
ReuseKind::Impl => self.parse_impl_delegation(span, attrs, defaultness),
}?;

self.psess.gated_spans.gate(sym::fn_delegation, span.to(self.prev_token.span));

Ok(item_kind)
}

fn parse_delegation_body(&mut self) -> PResult<'a, Option<Box<Block>>> {
Ok(if self.check(exp!(OpenBrace)) {
Some(self.parse_block()?)
} else {
self.expect(exp!(Semi))?;
None
})
}

fn parse_impl_delegation(
&mut self,
span: Span,
attrs: &mut AttrVec,
defaultness: Defaultness,
) -> PResult<'a, ItemKind> {
let mut impl_item = self.parse_item_impl(attrs, defaultness, true)?;
let ItemKind::Impl(Impl { items, of_trait, .. }) = &mut impl_item else { unreachable!() };

let until_expr_span = span.to(self.prev_token.span);

let Some(of_trait) = of_trait else {
return Err(self
.dcx()
.create_err(errors::ImplReuseInherentImpl { span: until_expr_span }));
};

let body = self.parse_delegation_body()?;
let whole_reuse_span = span.to(self.prev_token.span);

items.push(Box::new(AssocItem {
id: DUMMY_NODE_ID,
attrs: Default::default(),
span: whole_reuse_span,
tokens: None,
vis: Visibility {
kind: VisibilityKind::Inherited,
span: whole_reuse_span,
tokens: None,
},
kind: AssocItemKind::DelegationMac(Box::new(DelegationMac {
qself: None,
prefix: of_trait.trait_ref.path.clone(),
suffixes: None,
body,
})),
}));

Ok(impl_item)
}

fn parse_path_like_delegation(&mut self) -> PResult<'a, ItemKind> {
let (qself, path) = if self.eat_lt() {
let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
(Some(qself), path)
Expand All @@ -713,43 +798,35 @@ impl<'a> Parser<'a> {
let rename = |this: &mut Self| {
Ok(if this.eat_keyword(exp!(As)) { Some(this.parse_ident()?) } else { None })
};
let body = |this: &mut Self| {
Ok(if this.check(exp!(OpenBrace)) {
Some(this.parse_block()?)
} else {
this.expect(exp!(Semi))?;
None
})
};

let item_kind = if self.eat_path_sep() {
Ok(if self.eat_path_sep() {
let suffixes = if self.eat(exp!(Star)) {
None
} else {
let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?));
Some(self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), parse_suffix)?.0)
};
let deleg = DelegationMac { qself, prefix: path, suffixes, body: body(self)? };
ItemKind::DelegationMac(Box::new(deleg))

ItemKind::DelegationMac(Box::new(DelegationMac {
qself,
prefix: path,
suffixes,
body: self.parse_delegation_body()?,
}))
} else {
let rename = rename(self)?;
let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident);
let deleg = Delegation {

ItemKind::Delegation(Box::new(Delegation {
id: DUMMY_NODE_ID,
qself,
path,
ident,
rename,
body: body(self)?,
body: self.parse_delegation_body()?,
from_glob: false,
};
ItemKind::Delegation(Box::new(deleg))
};

let span = span.to(self.prev_token.span);
self.psess.gated_spans.gate(sym::fn_delegation, span);

Ok(item_kind)
}))
})
}

fn parse_item_list<T>(
Expand Down Expand Up @@ -2594,7 +2671,7 @@ impl<'a> Parser<'a> {
Ok(body)
}

fn check_impl_frontmatter(&mut self) -> bool {
fn check_impl_frontmatter(&mut self, look_ahead: usize) -> bool {
const ALL_QUALS: &[Symbol] = &[kw::Const, kw::Unsafe];
// In contrast to the loop below, this call inserts `impl` into the
// list of expected tokens shown in diagnostics.
Expand All @@ -2603,7 +2680,7 @@ impl<'a> Parser<'a> {
}
let mut i = 0;
while i < ALL_QUALS.len() {
let action = self.look_ahead(i, |token| {
let action = self.look_ahead(i + look_ahead, |token| {
if token.is_keyword(kw::Impl) {
return Some(true);
}
Expand All @@ -2618,6 +2695,7 @@ impl<'a> Parser<'a> {
}
i += 1;
}

self.is_keyword_ahead(i, &[kw::Impl])
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_target/src/target_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ impl Stability {
/// the feature gate to actually be enabled when using a nightly compiler.)
///
/// Before calling this, ensure the feature is even permitted for this use:
/// - for `#[target_feature]`/`-Ctarget-feature`, check `allow_toggle()`
/// - for `cfg(target_feature)`, check `in_cfg`
/// - for `#[target_feature]`/`-Ctarget-feature`, check `toggle_allowed()`
/// - for `cfg(target_feature)`, check `in_cfg()`
pub fn requires_nightly(&self) -> Option<Symbol> {
match *self {
Stability::Unstable(nightly_feature) => Some(nightly_feature),
Expand Down
19 changes: 19 additions & 0 deletions library/coretests/tests/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,25 @@ fn test_fmt_debug_of_mut_reference() {
assert_eq!(format!("{:?}", &mut x), "0");
}

#[test]
fn test_fmt_pointer() {
use std::rc::Rc;
use std::sync::Arc;
let p: *const u8 = std::ptr::null();
let rc = Rc::new(1usize);
let arc = Arc::new(1usize);
let b = Box::new("hi");

let _ = format!("{rc:p}{arc:p}{b:p}");

if cfg!(target_pointer_width = "32") {
assert_eq!(format!("{:#p}", p), "0x00000000");
} else {
assert_eq!(format!("{:#p}", p), "0x0000000000000000");
}
assert_eq!(format!("{:p}", p), "0x0");
}

#[test]
fn test_default_write_impls() {
use core::fmt::Write;
Expand Down
4 changes: 2 additions & 2 deletions library/std/src/sys/pal/hermit/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,9 @@ pub struct SystemTime(Timespec);
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());

impl SystemTime {
pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };
pub const MAX: SystemTime = SystemTime(Timespec::MAX);

pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };
pub const MIN: SystemTime = SystemTime(Timespec::MIN);

pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime {
SystemTime(Timespec::new(tv_sec, tv_nsec))
Expand Down
2 changes: 0 additions & 2 deletions library/std/src/sys/pal/vexos/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
pub mod os;
#[path = "../unsupported/pipe.rs"]
pub mod pipe;
pub mod time;

#[expect(dead_code)]
Expand Down
4 changes: 3 additions & 1 deletion src/tools/compiletest/src/directives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ mod directive_names;
mod file;
mod handlers;
mod line;
mod line_number;
pub(crate) use line_number::LineNumber;
mod needs;
#[cfg(test)]
mod tests;
Expand Down Expand Up @@ -591,7 +593,7 @@ fn iter_directives(
];
// Process the extra implied directives, with a dummy line number of 0.
for directive_str in extra_directives {
let directive_line = line_directive(testfile, 0, directive_str)
let directive_line = line_directive(testfile, LineNumber::ZERO, directive_str)
.unwrap_or_else(|| panic!("bad extra-directive line: {directive_str:?}"));
it(&directive_line);
}
Expand Down
3 changes: 2 additions & 1 deletion src/tools/compiletest/src/directives/file.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use camino::Utf8Path;

use crate::directives::LineNumber;
use crate::directives::line::{DirectiveLine, line_directive};

pub(crate) struct FileDirectives<'a> {
Expand All @@ -11,7 +12,7 @@ impl<'a> FileDirectives<'a> {
pub(crate) fn from_file_contents(path: &'a Utf8Path, file_contents: &'a str) -> Self {
let mut lines = vec![];

for (line_number, ln) in (1..).zip(file_contents.lines()) {
for (line_number, ln) in LineNumber::enumerate().zip(file_contents.lines()) {
let ln = ln.trim();

if let Some(directive_line) = line_directive(path, line_number, ln) {
Expand Down
6 changes: 4 additions & 2 deletions src/tools/compiletest/src/directives/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ use std::fmt;

use camino::Utf8Path;

use crate::directives::LineNumber;

const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@";

/// If the given line begins with the appropriate comment prefix for a directive,
/// returns a struct containing various parts of the directive.
pub(crate) fn line_directive<'a>(
file_path: &'a Utf8Path,
line_number: usize,
line_number: LineNumber,
original_line: &'a str,
) -> Option<DirectiveLine<'a>> {
// Ignore lines that don't start with the comment prefix.
Expand Down Expand Up @@ -60,7 +62,7 @@ pub(crate) struct DirectiveLine<'a> {
/// Mostly used for diagnostics, but some directives (e.g. `//@ pp-exact`)
/// also use it to compute a value based on the filename.
pub(crate) file_path: &'a Utf8Path,
pub(crate) line_number: usize,
pub(crate) line_number: LineNumber,

/// Some test directives start with a revision name in square brackets
/// (e.g. `[foo]`), and only apply to that revision of the test.
Expand Down
Loading
Loading