Skip to content

Commit

Permalink
Auto merge of rust-lang#127549 - jhpratt:rollup-o1mbmhr, r=jhpratt
Browse files Browse the repository at this point in the history
Rollup of 8 pull requests

Successful merges:

 - rust-lang#124211 (Bump `elided_lifetimes_in_associated_constant` to deny)
 - rust-lang#125627 (migration lint for `expr2024` for the edition 2024)
 - rust-lang#127091 (impl FusedIterator and a size hint for the error sources iter)
 - rust-lang#127461 (Fixup failing fuchsia tests)
 - rust-lang#127484 (`#[doc(alias)]`'s doc: say that ASCII spaces are allowed)
 - rust-lang#127508 (small search graph refactor)
 - rust-lang#127521 (Remove spastorino from SMIR)
 - rust-lang#127532 (documentation: update cmake version)

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Jul 10, 2024
2 parents 7caf672 + 08cb462 commit 649feb9
Show file tree
Hide file tree
Showing 22 changed files with 324 additions and 90 deletions.
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);
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4620,7 +4620,7 @@ declare_lint! {
/// [against]: https://github.com/rust-lang/rust/issues/38831
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
Warn,
Deny,
"elided lifetimes cannot be used in associated constants in impls",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
Expand Down
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
Loading

0 comments on commit 649feb9

Please sign in to comment.