Skip to content
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
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2350,6 +2350,7 @@ unsafe extern "C" {
pub(crate) fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString);

pub(crate) fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool;
pub(crate) fn LLVMRustTargetHasMnemonic(T: &TargetMachine, s: *const c_char) -> bool;

pub(crate) fn LLVMRustPrintTargetCPUs(TM: &TargetMachine, OutStr: &RustString);
pub(crate) fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t;
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,10 @@ pub(crate) fn print(req: &PrintRequest, out: &mut String, sess: &Session) {
match req.kind {
PrintKind::TargetCPUs => print_target_cpus(sess, tm.raw(), out),
PrintKind::TargetFeatures => print_target_features(sess, tm.raw(), out),
PrintKind::BackendHasMnemonic => {
let mnemonic = req.arg.as_deref().expect("BackendHasMnemonic requires arg");
print_target_has_mnemonic(tm.raw(), mnemonic, out)
}
_ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
}
}
Expand Down Expand Up @@ -738,3 +742,10 @@ pub(crate) fn tune_cpu(sess: &Session) -> Option<&str> {
let name = sess.opts.unstable_opts.tune_cpu.as_ref()?;
Some(handle_native(name))
}

fn print_target_has_mnemonic(tm: &llvm::TargetMachine, mnemonic: &str, out: &mut String) {
use std::fmt::Write;
let cstr = SmallCStr::new(mnemonic);
let has_mnemonic = unsafe { llvm::LLVMRustTargetHasMnemonic(tm, cstr.as_ptr()) };
writeln!(out, "{}", has_mnemonic).unwrap();
}
3 changes: 3 additions & 0 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,9 @@ fn print_crate_info(
let calling_conventions = rustc_abi::all_names();
println_info!("{}", calling_conventions.join("\n"));
}
BackendHasMnemonic => {
codegen_backend.print(req, &mut crate_info, sess);
}
BackendHasZstd => {
let has_zstd: bool = codegen_backend.has_zstd();
println_info!("{has_zstd}");
Expand Down
20 changes: 20 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "llvm/IR/Verifier.h"
#include "llvm/IRPrinter/IRPrintingPasses.h"
#include "llvm/LTO/LTO.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Object/ObjectFile.h"
Expand Down Expand Up @@ -94,6 +95,25 @@ extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM,
return MCInfo->checkFeatures(std::string("+") + Feature);
}

/// Check whether the target has a specific assembly mnemonic like `ret` or
/// `nop`.
/// This should be fast enough but if its not we have to look into another
/// method of checking.
extern "C" bool LLVMRustTargetHasMnemonic(LLVMTargetMachineRef TM,
const char *Mnemonic) {
TargetMachine *Target = unwrap(TM);
const MCInstrInfo *MII = Target->getMCInstrInfo();
StringRef MnemonicRef(Mnemonic);

for (unsigned i = 0; i < MII->getNumOpcodes(); i++) {
StringRef Name = MII->getName(i);
if (Name.equals_insensitive(MnemonicRef)) {
return true;
}
}
return false;
}

enum class LLVMRustCodeModel {
Tiny,
Small,
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1416,12 +1416,12 @@ impl<'tcx> TyCtxt<'tcx> {
self,
stable_crate_id: StableCrateId,
) -> Result<TyCtxtFeed<'tcx, CrateNum>, CrateNum> {
if let Some(&existing) = self.untracked().stable_crate_ids.read().get(&stable_crate_id) {
let mut lock = self.untracked().stable_crate_ids.write();
if let Some(&existing) = lock.get(&stable_crate_id) {
return Err(existing);
}

let num = CrateNum::new(self.untracked().stable_crate_ids.read().len());
self.untracked().stable_crate_ids.write().insert(stable_crate_id, num);
let num = CrateNum::new(lock.len());
lock.insert(stable_crate_id, num);
Ok(TyCtxtFeed { key: num, tcx: self })
}

Expand Down
32 changes: 17 additions & 15 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -924,22 +924,24 @@ pub(crate) struct IrrefutableLetPatternsIfLetGuard {
}

#[derive(Diagnostic)]
#[diag(
"irrefutable `let...else` {$count ->
[one] pattern
*[other] patterns
}"
)]
#[note(
"{$count ->
[one] this pattern always matches, so the else clause is unreachable
*[other] these patterns always match, so the else clause is unreachable
}"
)]
#[diag("unreachable `else` clause")]
#[note("this pattern always matches, so the else clause is unreachable")]
pub(crate) struct IrrefutableLetPatternsLetElse {
pub(crate) count: usize,
#[help("remove this `else` block")]
pub(crate) else_span: Option<Span>,
#[subdiagnostic]
pub(crate) be_replaced: Option<LetElseReplacementSuggestion>,
}

#[derive(Subdiagnostic, Debug)]
#[suggestion(
"consider using `let {$lhs} = {$rhs}` to match on a specific variant",
code = "let {lhs} = {rhs}",
applicability = "machine-applicable"
)]
pub(crate) struct LetElseReplacementSuggestion {
#[primary_span]
pub(crate) span: Span,
pub(crate) lhs: String,
pub(crate) rhs: String,
}

#[derive(Diagnostic)]
Expand Down
71 changes: 64 additions & 7 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
let Ok(()) = self.visit_land(ex, &mut chain_refutabilities) else { return };
// Lint only single irrefutable let binding.
if let [Some((_, Irrefutable))] = chain_refutabilities[..] {
self.lint_single_let(ex.span, None);
self.lint_single_let(ex.span, None, None);
}
return;
}
Expand Down Expand Up @@ -438,7 +438,45 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
if let LetSource::PlainLet = self.let_source {
self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span));
} else if let Ok(Irrefutable) = self.is_let_irrefutable(pat, scrut) {
self.lint_single_let(span, else_span);
if span.from_expansion() {
self.lint_single_let(span, None, None);
return;
}
let let_else_span = self.check_irrefutable_option_some(pat, scrut, span);

let sm = self.tcx.sess.source_map();
let next_token_start = sm.span_extend_while_whitespace(span.clone()).hi();
let line_span = sm.span_extend_to_line(span.clone()).with_lo(next_token_start);
let else_keyword_span = sm.span_until_whitespace(line_span);
self.lint_single_let(span, Some(else_keyword_span), let_else_span);
}
}

/// Check case `let x = Some(y);`, user likely intended to destructure `Option`
fn check_irrefutable_option_some(
&self,
pat: &'p Pat<'tcx>,
initializer: Option<&Expr<'tcx>>,
span: Span,
) -> Option<LetElseReplacementSuggestion> {
if let sm = self.tcx.sess.source_map()
&& let Some(initializer) = initializer
&& let Some(s_ty) = initializer.ty.ty_adt_def()
&& self.tcx.is_diagnostic_item(rustc_span::sym::Option, s_ty.did())
&& let ExprKind::Scope { value, .. } = initializer.kind
&& let initializer_expr = &self.thir[value]
&& let ExprKind::Adt(box AdtExpr { fields, .. }) = &initializer_expr.kind
&& let Some(field) = fields.first()
&& let inner = &self.thir[field.expr]
&& let Some(inner_ty) = inner.ty.ty_adt_def()
&& self.tcx.is_diagnostic_item(rustc_span::sym::Option, inner_ty.did())
&& let Ok(rhs) = sm.span_to_snippet(inner.span)
&& let Ok(lhs) = sm.span_to_snippet(pat.span)
{
let lhs = format!("Some({})", lhs);
Some(LetElseReplacementSuggestion { span, lhs, rhs })
} else {
None
}
}

Expand Down Expand Up @@ -559,14 +597,20 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
}

#[instrument(level = "trace", skip(self))]
fn lint_single_let(&mut self, let_span: Span, else_span: Option<Span>) {
fn lint_single_let(
&mut self,
let_span: Span,
else_keyword_span: Option<Span>,
let_else_span: Option<LetElseReplacementSuggestion>,
) {
report_irrefutable_let_patterns(
self.tcx,
self.hir_source,
self.let_source,
1,
let_span,
else_span,
else_keyword_span,
let_else_span,
);
}

Expand Down Expand Up @@ -862,7 +906,8 @@ fn report_irrefutable_let_patterns(
source: LetSource,
count: usize,
span: Span,
else_span: Option<Span>,
else_keyword_span: Option<Span>,
let_else_span: Option<LetElseReplacementSuggestion>,
) {
macro_rules! emit_diag {
($lint:tt) => {{
Expand All @@ -875,11 +920,23 @@ fn report_irrefutable_let_patterns(
LetSource::IfLet | LetSource::ElseIfLet => emit_diag!(IrrefutableLetPatternsIfLet),
LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard),
LetSource::LetElse => {
let spans = match else_keyword_span {
Some(else_keyword_span) => {
let mut spans = MultiSpan::from_span(else_keyword_span);
spans.push_span_label(
span,
msg!("assigning to binding pattern will always succeed"),
);
spans
}
None => span.into(),
};

tcx.emit_node_span_lint(
IRREFUTABLE_LET_PATTERNS,
id,
span,
IrrefutableLetPatternsLetElse { count, else_span },
spans,
IrrefutableLetPatternsLetElse { be_replaced: let_else_span },
);
}
LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet),
Expand Down
37 changes: 31 additions & 6 deletions compiler/rustc_session/src/config/print_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ use crate::macros::AllVariants;
pub struct PrintRequest {
pub kind: PrintKind,
pub out: OutFileName,
pub arg: Option<String>,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(AllVariants)]
pub enum PrintKind {
// tidy-alphabetical-start
AllTargetSpecsJson,
BackendHasMnemonic,
BackendHasZstd,
CallingConventions,
Cfg,
Expand Down Expand Up @@ -55,6 +57,7 @@ impl PrintKind {
match self {
// tidy-alphabetical-start
AllTargetSpecsJson => "all-target-specs-json",
BackendHasMnemonic => "backend-has-mnemonic",
BackendHasZstd => "backend-has-zstd",
CallingConventions => "calling-conventions",
Cfg => "cfg",
Expand Down Expand Up @@ -108,7 +111,8 @@ impl PrintKind {

// Unstable values:
AllTargetSpecsJson => false,
BackendHasZstd => false, // (perma-unstable, for use by compiletest)
BackendHasMnemonic => false, // (perma-unstable, for use by compiletest)
BackendHasZstd => false, // (perma-unstable, for use by compiletest)
CheckCfg => false,
CrateRootLintLevels => false,
SupportedCrateTypes => false,
Expand Down Expand Up @@ -145,11 +149,19 @@ pub(crate) fn collect_print_requests(
) -> Vec<PrintRequest> {
let mut prints = Vec::<PrintRequest>::new();
if cg.target_cpu.as_deref() == Some("help") {
prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout });
prints.push(PrintRequest {
kind: PrintKind::TargetCPUs,
out: OutFileName::Stdout,
arg: None,
});
cg.target_cpu = None;
};
if cg.target_feature == "help" {
prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout });
prints.push(PrintRequest {
kind: PrintKind::TargetFeatures,
out: OutFileName::Stdout,
arg: None,
});
cg.target_feature = String::new();
}

Expand All @@ -162,9 +174,22 @@ pub(crate) fn collect_print_requests(
prints.extend(matches.opt_strs("print").into_iter().map(|req| {
let (req, out) = split_out_file_name(&req);

let kind = if let Some(print_kind) = PrintKind::from_str(req) {
let (kind, arg) = if let Some(mnemonic) = req.strip_prefix("backend-has-mnemonic") {
check_print_request_stability(early_dcx, unstable_opts, PrintKind::BackendHasMnemonic);
// BackendHasMnemonic requires a mnemonic argument
if let Some(mnemonic) = mnemonic.strip_prefix(':')
&& !mnemonic.is_empty()
{
(PrintKind::BackendHasMnemonic, Some(mnemonic.to_string()))
} else {
early_dcx.early_fatal(
"expected mnemonic name after `--print=backend-has-mnemonic:`, \
for example: `--print=backend-has-mnemonic:RET`",
);
}
} else if let Some(print_kind) = PrintKind::from_str(req) {
check_print_request_stability(early_dcx, unstable_opts, print_kind);
print_kind
(print_kind, None)
} else {
let is_nightly = nightly_options::match_is_nightly_build(matches);
emit_unknown_print_request_help(early_dcx, req, is_nightly)
Expand All @@ -180,7 +205,7 @@ pub(crate) fn collect_print_requests(
}
}

PrintRequest { kind, out }
PrintRequest { kind, out, arg }
}));

prints
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_trait_selection/src/traits/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,11 +554,7 @@ fn evaluate_host_effect_for_fn_goal<'tcx>(
// but they don't really need to right now.
ty::CoroutineClosure(_, _) => return Err(EvaluationFailure::NoSolution),

ty::Closure(def, args) => {
// For now we limit ourselves to closures without binders. The next solver can handle them.
args.as_closure().sig().no_bound_vars().ok_or(EvaluationFailure::NoSolution)?;
(def, args)
}
ty::Closure(def, args) => (def, args),

// Everything else needs explicit impls or cannot have an impl
_ => return Err(EvaluationFailure::NoSolution),
Expand Down
11 changes: 11 additions & 0 deletions src/doc/rustc-dev-guide/src/tests/compiletest.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,17 @@ See also the [codegen tests](#codegen-tests) for a similar set of tests.
If you need to work with `#![no_std]` cross-compiling tests, consult the
[`minicore` test auxiliary](./minicore.md) chapter.

#### Conditional assembly tests based on instruction support

Tests that depend on specific assembly instructions being available can use the
`//@ needs-asm-mnemonic: <MNEMONIC>` directive. This will skip the test if the
target backend does not support the specified instruction mnemonic.

For example, a test that requires the `RET` instruction:
```rust,ignore
//@ needs-asm-mnemonic: RET
```

[`tests/assembly-llvm`]: https://github.com/rust-lang/rust/tree/HEAD/tests/assembly-llvm


Expand Down
3 changes: 3 additions & 0 deletions src/doc/rustc-dev-guide/src/tests/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ The following directives will check rustc build settings and target settings:
For tests that cross-compile to explicit targets
via `--target`, use `needs-llvm-components` instead to ensure the appropriate
backend is available.
- `needs-asm-mnemonic: <MNEMONIC>` — ignores if the target backend does not
support the specified assembly mnemonic (e.g., `RET`, `NOP`).
Only supported with the LLVM backend.
- `needs-profiler-runtime` — ignores the test if the profiler runtime was not
enabled for the target (`build.profiler = true` in `bootstrap.toml`)
- `needs-sanitizer-support` — ignores if the sanitizer support was not enabled
Expand Down
Loading
Loading