Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Rollup of 6 pull requests #127550

Closed
wants to merge 15 commits into from
Closed
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: 1 addition & 1 deletion INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ If building LLVM from source, you'll need additional tools:
[LLVM's documentation](https://llvm.org/docs/GettingStarted.html#host-c-toolchain-both-compiler-and-standard-library)
* `ninja`, or GNU `make` 3.81 or later (Ninja is recommended, especially on
Windows)
* `cmake` 3.13.4 or later
* `cmake` version listed on [LLVM's documentation](https://llvm.org/docs/GettingStarted.html#software)
* `libstdc++-static` may be required on some Linux distributions such as Fedora
and Ubuntu

Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@ lint_lintpass_by_hand = implementing `LintPass` by hand
lint_macro_expanded_macro_exports_accessed_by_absolute_paths = macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
.note = the macro is defined here

lint_macro_expr_fragment_specifier_2024_migration =
the `expr` fragment specifier will accept more expressions in the 2024 edition
.suggestion = to keep the existing behavior, use the `expr_2021` fragment specifier
lint_macro_is_private = macro `{$ident}` is private

lint_macro_rule_never_used = rule #{$n} of macro `{$name}` is never used
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ mod late;
mod let_underscore;
mod levels;
mod lints;
mod macro_expr_fragment_specifier_2024_migration;
mod map_unit_fn;
mod methods;
mod multiple_supertrait_upcastable;
Expand Down Expand Up @@ -97,6 +98,7 @@ use impl_trait_overcaptures::ImplTraitOvercaptures;
use internal::*;
use invalid_from_utf8::*;
use let_underscore::*;
use macro_expr_fragment_specifier_2024_migration::*;
use map_unit_fn::*;
use methods::*;
use multiple_supertrait_upcastable::*;
Expand Down Expand Up @@ -170,6 +172,7 @@ early_lint_methods!(
IncompleteInternalFeatures: IncompleteInternalFeatures,
RedundantSemicolons: RedundantSemicolons,
UnusedDocComment: UnusedDocComment,
Expr2024: Expr2024,
]
]
);
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,13 @@ pub struct BuiltinTypeAliasGenericBounds<'a, 'b> {
pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
}

#[derive(LintDiagnostic)]
#[diag(lint_macro_expr_fragment_specifier_2024_migration)]
pub struct MacroExprFragment2024 {
#[suggestion(code = "expr_2021", applicability = "machine-applicable")]
pub suggestion: Span,
}

pub struct BuiltinTypeAliasGenericBoundsSuggestion {
pub suggestions: Vec<(Span, String)>,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//! Migration code for the `expr_fragment_specifier_2024`
//! rule.
use tracing::debug;

use rustc_ast::token::Token;
use rustc_ast::token::TokenKind;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::tokenstream::TokenTree;
use rustc_session::declare_lint;
use rustc_session::declare_lint_pass;
use rustc_session::lint::FutureIncompatibilityReason;
use rustc_span::edition::Edition;
use rustc_span::sym;

use crate::lints::MacroExprFragment2024;
use crate::EarlyLintPass;

declare_lint! {
/// The `edition_2024_expr_fragment_specifier` lint detects the use of
/// `expr` fragments in macros during migration to the 2024 edition.
///
/// The `expr` fragment specifier will accept more expressions in the 2024
/// edition. To maintain the behavior from the 2021 edition and earlier, use
/// the `expr_2021` fragment specifier.
///
/// ### Example
///
/// ```rust,edition2021,compile_fail
/// #![deny(edition_2024_expr_fragment_specifier)]
/// macro_rules! m {
/// ($e:expr) => {
/// $e
/// }
/// }
///
/// fn main() {
/// m!(1);
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Rust [editions] allow the language to evolve without breaking backwards
/// compatibility. This lint catches code that uses [macro matcher fragment
/// specifiers] that have changed meaning in the 2024 edition. If you switch
/// to the new edition without updating the code, your macros may behave
/// differently.
///
/// In the 2024 edition, the `expr` fragment specifier `expr` will also
/// match `const { ... }` blocks. This means if a macro had a pattern that
/// matched `$e:expr` and another that matches `const { $e: expr }`, for
/// example, that under the 2024 edition the first pattern would match while
/// in the 2021 and earlier editions the second pattern would match. To keep
/// the old behavior, use the `expr_2021` fragment specifier.
///
/// This lint detects macros whose behavior might change due to the changing
/// meaning of the `expr` fragment specifier. It is "allow" by default
/// because the code is perfectly valid in older editions. The [`cargo fix`]
/// tool with the `--edition` flag will switch this lint to "warn" and
/// automatically apply the suggested fix from the compiler. This provides a
/// completely automated way to update old code for a new edition.
///
/// Using `cargo fix --edition` with this lint will ensure that your code
/// retains the same behavior. This may not be the desired, as macro authors
/// often will want their macros to use the latest grammar for matching
/// expressions. Be sure to carefully review changes introduced by this lint
/// to ensure the macros implement the desired behavior.
///
/// [editions]: https://doc.rust-lang.org/edition-guide/
/// [macro matcher fragment specifiers]: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html
/// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
pub EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
Allow,
"The `expr` fragment specifier will accept more expressions in the 2024 edition. \
To keep the existing behavior, use the `expr_2021` fragment specifier.",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
reference: "Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>",
};
}

declare_lint_pass!(Expr2024 => [EDITION_2024_EXPR_FRAGMENT_SPECIFIER,]);

impl Expr2024 {
fn check_tokens(&mut self, cx: &crate::EarlyContext<'_>, tokens: &TokenStream) {
let mut prev_colon = false;
let mut prev_identifier = false;
let mut prev_dollar = false;
for tt in tokens.trees() {
debug!(
"check_tokens: {:?} - colon {prev_dollar} - ident {prev_identifier} - colon {prev_colon}",
tt
);
match tt {
TokenTree::Token(token, _) => match token.kind {
TokenKind::Dollar => {
prev_dollar = true;
continue;
}
TokenKind::Ident(..) | TokenKind::NtIdent(..) => {
if prev_colon && prev_identifier && prev_dollar {
self.check_ident_token(cx, token);
} else if prev_dollar {
prev_identifier = true;
continue;
}
}
TokenKind::Colon => {
if prev_dollar && prev_identifier {
prev_colon = true;
continue;
}
}
_ => {}
},
TokenTree::Delimited(.., tts) => self.check_tokens(cx, tts),
}
prev_colon = false;
prev_identifier = false;
prev_dollar = false;
}
}

fn check_ident_token(&mut self, cx: &crate::EarlyContext<'_>, token: &Token) {
debug!("check_ident_token: {:?}", token);
let (sym, edition) = match token.kind {
TokenKind::Ident(sym, _) => (sym, Edition::Edition2024),
_ => return,
};

debug!("token.span.edition(): {:?}", token.span.edition());
if token.span.edition() >= edition {
return;
}

if sym != sym::expr {
return;
}

debug!("emitting lint");
cx.builder.emit_span_lint(
&EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
token.span.into(),
MacroExprFragment2024 { suggestion: token.span },
);
}
}

impl EarlyLintPass for Expr2024 {
fn check_mac_def(&mut self, cx: &crate::EarlyContext<'_>, mc: &rustc_ast::MacroDef) {
self.check_tokens(cx, &mc.body.tokens);
}
}
14 changes: 11 additions & 3 deletions compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,20 @@ enum GoalEvaluationKind {
Nested,
}

// FIXME(trait-system-refactor-initiative#117): we don't detect whether a response
// ended up pulling down any universes.
fn has_no_inference_or_external_constraints<I: Interner>(
response: ty::Canonical<I, Response<I>>,
) -> bool {
response.value.external_constraints.region_constraints.is_empty()
&& response.value.var_values.is_identity()
&& response.value.external_constraints.opaque_types.is_empty()
let ExternalConstraintsData {
ref region_constraints,
ref opaque_types,
ref normalization_nested_goals,
} = *response.value.external_constraints;
response.value.var_values.is_identity()
&& region_constraints.is_empty()
&& opaque_types.is_empty()
&& normalization_nested_goals.is_empty()
}

impl<'a, D, I> EvalCtxt<'a, D>
Expand Down
52 changes: 21 additions & 31 deletions compiler/rustc_next_trait_solver/src/solve/search_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ struct StackEntry<I: Interner> {
/// C :- D
/// D :- C
/// ```
cycle_participants: HashSet<CanonicalInput<I>>,
nested_goals: HashSet<CanonicalInput<I>>,
/// Starts out as `None` and gets set when rerunning this
/// goal in case we encounter a cycle.
provisional_result: Option<QueryResult<I>>,
Expand Down Expand Up @@ -139,18 +139,11 @@ impl<I: Interner> SearchGraph<I> {
self.mode
}

/// Pops the highest goal from the stack, lazily updating the
/// the next goal in the stack.
///
/// Directly popping from the stack instead of using this method
/// would cause us to not track overflow and recursion depth correctly.
fn pop_stack(&mut self) -> StackEntry<I> {
let elem = self.stack.pop().unwrap();
if let Some(last) = self.stack.raw.last_mut() {
last.reached_depth = last.reached_depth.max(elem.reached_depth);
last.encountered_overflow |= elem.encountered_overflow;
fn update_parent_goal(&mut self, reached_depth: StackDepth, encountered_overflow: bool) {
if let Some(parent) = self.stack.raw.last_mut() {
parent.reached_depth = parent.reached_depth.max(reached_depth);
parent.encountered_overflow |= encountered_overflow;
}
elem
}

pub(super) fn is_empty(&self) -> bool {
Expand Down Expand Up @@ -222,8 +215,8 @@ impl<I: Interner> SearchGraph<I> {
let current_cycle_root = &mut stack[current_root.as_usize()];
for entry in cycle_participants {
entry.non_root_cycle_participant = entry.non_root_cycle_participant.max(Some(head));
current_cycle_root.cycle_participants.insert(entry.input);
current_cycle_root.cycle_participants.extend(mem::take(&mut entry.cycle_participants));
current_cycle_root.nested_goals.insert(entry.input);
current_cycle_root.nested_goals.extend(mem::take(&mut entry.nested_goals));
}
}

Expand Down Expand Up @@ -342,7 +335,7 @@ impl<I: Interner> SearchGraph<I> {
non_root_cycle_participant: None,
encountered_overflow: false,
has_been_used: HasBeenUsed::empty(),
cycle_participants: Default::default(),
nested_goals: Default::default(),
provisional_result: None,
};
assert_eq!(self.stack.push(entry), depth);
Expand All @@ -364,14 +357,16 @@ impl<I: Interner> SearchGraph<I> {
}

debug!("canonical cycle overflow");
let current_entry = self.pop_stack();
let current_entry = self.stack.pop().unwrap();
debug_assert!(current_entry.has_been_used.is_empty());
let result = Self::response_no_constraints(cx, input, Certainty::overflow(false));
(current_entry, result)
});

let proof_tree = inspect.finalize_canonical_goal_evaluation(cx);

self.update_parent_goal(final_entry.reached_depth, final_entry.encountered_overflow);

// We're now done with this goal. In case this goal is involved in a larger cycle
// do not remove it from the provisional cache and update its provisional result.
// We only add the root of cycles to the global cache.
Expand All @@ -394,15 +389,15 @@ impl<I: Interner> SearchGraph<I> {
//
// We must not use the global cache entry of a root goal if a cycle
// participant is on the stack. This is necessary to prevent unstable
// results. See the comment of `StackEntry::cycle_participants` for
// results. See the comment of `StackEntry::nested_goals` for
// more details.
self.global_cache(cx).insert(
cx,
input,
proof_tree,
reached_depth,
final_entry.encountered_overflow,
final_entry.cycle_participants,
final_entry.nested_goals,
dep_node,
result,
)
Expand Down Expand Up @@ -441,14 +436,9 @@ impl<I: Interner> SearchGraph<I> {
}
}

// Update the reached depth of the current goal to make sure
// its state is the same regardless of whether we've used the
// global cache or not.
// Adjust the parent goal as if we actually computed this goal.
let reached_depth = self.stack.next_index().plus(additional_depth);
if let Some(last) = self.stack.raw.last_mut() {
last.reached_depth = last.reached_depth.max(reached_depth);
last.encountered_overflow |= encountered_overflow;
}
self.update_parent_goal(reached_depth, encountered_overflow);

Some(result)
}
Expand Down Expand Up @@ -477,7 +467,7 @@ impl<I: Interner> SearchGraph<I> {
F: FnMut(&mut Self, &mut ProofTreeBuilder<D>) -> QueryResult<I>,
{
let result = prove_goal(self, inspect);
let stack_entry = self.pop_stack();
let stack_entry = self.stack.pop().unwrap();
debug_assert_eq!(stack_entry.input, input);

// If the current goal is not the root of a cycle, we are done.
Expand Down Expand Up @@ -554,27 +544,27 @@ impl<I: Interner> SearchGraph<I> {
non_root_cycle_participant,
encountered_overflow: _,
has_been_used,
ref cycle_participants,
ref nested_goals,
provisional_result,
} = *entry;
let cache_entry = provisional_cache.get(&entry.input).unwrap();
assert_eq!(cache_entry.stack_depth, Some(depth));
if let Some(head) = non_root_cycle_participant {
assert!(head < depth);
assert!(cycle_participants.is_empty());
assert!(nested_goals.is_empty());
assert_ne!(stack[head].has_been_used, HasBeenUsed::empty());

let mut current_root = head;
while let Some(parent) = stack[current_root].non_root_cycle_participant {
current_root = parent;
}
assert!(stack[current_root].cycle_participants.contains(&input));
assert!(stack[current_root].nested_goals.contains(&input));
}

if !cycle_participants.is_empty() {
if !nested_goals.is_empty() {
assert!(provisional_result.is_some() || !has_been_used.is_empty());
for entry in stack.iter().take(depth.as_usize()) {
assert_eq!(cycle_participants.get(&entry.input), None);
assert_eq!(nested_goals.get(&entry.input), None);
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions library/core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1008,8 +1008,15 @@ impl<'a> Iterator for Source<'a> {
self.current = self.current.and_then(Error::source);
current
}

fn size_hint(&self) -> (usize, Option<usize>) {
if self.current.is_some() { (1, None) } else { (0, Some(0)) }
}
}

#[unstable(feature = "error_iter", issue = "58520")]
impl<'a> crate::iter::FusedIterator for Source<'a> {}

#[stable(feature = "error_by_ref", since = "1.51.0")]
impl<'a, T: Error + ?Sized> Error for &'a T {
#[allow(deprecated, deprecated_in_future)]
Expand Down
Loading
Loading