use rustc_ast::attr;
use rustc_ast::ptr::P as AstP;
use rustc_ast::*;
-use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::struct_span_err;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP};
-use rustc_target::asm;
-use std::collections::hash_map::Entry;
-use std::fmt::Write;
impl<'hir> LoweringContext<'_, 'hir> {
fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
let e = e.as_ref().map(|x| self.lower_expr(x));
hir::ExprKind::Ret(e)
}
- ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm),
+ ExprKind::InlineAsm(ref asm) => {
+ hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
+ }
ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm),
ExprKind::Struct(ref se) => {
let rest = match &se.rest {
hir::ExprKind::Struct(
self.arena.alloc(self.lower_qpath(
e.id,
- &None,
+ &se.qself,
&se.path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
let mut generic_args = vec![];
for (idx, arg) in args.into_iter().enumerate() {
if legacy_args_idx.contains(&idx) {
- let parent_def_id = self.current_hir_id_owner.last().unwrap().0;
+ let parent_def_id = self.current_hir_id_owner.0;
let node_id = self.resolver.next_node_id();
// Add a definition for the in-band const def.
)
}
- /// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_ok(<expr>) }`,
- /// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_ok(()) }`
+ /// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_output(<expr>) }`,
+ /// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_output(()) }`
/// and save the block id to use it as a break target for desugaring of the `?` operator.
fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind<'hir> {
self.with_catch_scope(body.id, |this| {
let ok_wrapped_span =
this.mark_span_with_reason(DesugaringKind::TryBlock, tail_expr.span, None);
- // `::std::ops::Try::from_ok($tail_expr)`
+ // `::std::ops::Try::from_output($tail_expr)`
block.expr = Some(this.wrap_in_try_constructor(
- hir::LangItem::TryFromOk,
+ hir::LangItem::TryTraitFromOutput,
try_span,
tail_expr,
ok_wrapped_span,
/// It is not a complete check, but just tries to reject most paths early
/// if they are not tuple structs.
/// Type checking will take care of the full validation later.
- fn extract_tuple_struct_path<'a>(&mut self, expr: &'a Expr) -> Option<&'a Path> {
- // For tuple struct destructuring, it must be a non-qualified path (like in patterns).
- if let ExprKind::Path(None, path) = &expr.kind {
- // Does the path resolves to something disallowed in a tuple struct/variant pattern?
+ fn extract_tuple_struct_path<'a>(
+ &mut self,
+ expr: &'a Expr,
+ ) -> Option<(&'a Option<QSelf>, &'a Path)> {
+ if let ExprKind::Path(qself, path) = &expr.kind {
+ // Does the path resolve to something disallowed in a tuple struct/variant pattern?
if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
if partial_res.unresolved_segments() == 0
&& !partial_res.base_res().expected_in_tuple_struct_pat()
return None;
}
}
- return Some(path);
+ return Some((qself, path));
}
None
}
}
// Tuple structs.
ExprKind::Call(callee, args) => {
- if let Some(path) = self.extract_tuple_struct_path(callee) {
+ if let Some((qself, path)) = self.extract_tuple_struct_path(callee) {
let (pats, rest) = self.destructure_sequence(
args,
"tuple struct or variant",
);
let qpath = self.lower_qpath(
callee.id,
- &None,
+ qself,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
}));
let qpath = self.lower_qpath(
lhs.id,
- &None,
+ &se.qself,
&se.path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
(Some(..), Some(..), HalfOpen) => hir::LangItem::Range,
(None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
(Some(..), Some(..), Closed) => unreachable!(),
- (_, None, Closed) => {
- self.diagnostic().span_fatal(span, "inclusive range with no end").raise()
- }
+ (_, None, Closed) => self.diagnostic().span_fatal(span, "inclusive range with no end"),
};
let fields = self.arena.alloc_from_iter(
result
}
- fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> {
- // Rustdoc needs to support asm! from foriegn architectures: don't try
- // lowering the register contraints in this case.
- let asm_arch = if self.sess.opts.actually_rustdoc { None } else { self.sess.asm_arch };
- if asm_arch.is_none() && !self.sess.opts.actually_rustdoc {
- struct_span_err!(self.sess, sp, E0472, "asm! is unsupported on this target").emit();
- }
- if asm.options.contains(InlineAsmOptions::ATT_SYNTAX)
- && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
- && !self.sess.opts.actually_rustdoc
- {
- self.sess
- .struct_span_err(sp, "the `att_syntax` option is only supported on x86")
- .emit();
- }
-
- // Lower operands to HIR. We use dummy register classes if an error
- // occurs during lowering because we still need to be able to produce a
- // valid HIR.
- let sess = self.sess;
- let operands: Vec<_> = asm
- .operands
- .iter()
- .map(|(op, op_sp)| {
- let lower_reg = |reg| match reg {
- InlineAsmRegOrRegClass::Reg(s) => {
- asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
- asm::InlineAsmReg::parse(
- asm_arch,
- |feature| sess.target_features.contains(&Symbol::intern(feature)),
- &sess.target,
- s,
- )
- .unwrap_or_else(|e| {
- let msg = format!("invalid register `{}`: {}", s.as_str(), e);
- sess.struct_span_err(*op_sp, &msg).emit();
- asm::InlineAsmReg::Err
- })
- } else {
- asm::InlineAsmReg::Err
- })
- }
- InlineAsmRegOrRegClass::RegClass(s) => {
- asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
- asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| {
- let msg = format!("invalid register class `{}`: {}", s.as_str(), e);
- sess.struct_span_err(*op_sp, &msg).emit();
- asm::InlineAsmRegClass::Err
- })
- } else {
- asm::InlineAsmRegClass::Err
- })
- }
- };
-
- let op = match *op {
- InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In {
- reg: lower_reg(reg),
- expr: self.lower_expr_mut(expr),
- },
- InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out {
- reg: lower_reg(reg),
- late,
- expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
- },
- InlineAsmOperand::InOut { reg, late, ref expr } => {
- hir::InlineAsmOperand::InOut {
- reg: lower_reg(reg),
- late,
- expr: self.lower_expr_mut(expr),
- }
- }
- InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => {
- hir::InlineAsmOperand::SplitInOut {
- reg: lower_reg(reg),
- late,
- in_expr: self.lower_expr_mut(in_expr),
- out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
- }
- }
- InlineAsmOperand::Const { ref anon_const } => hir::InlineAsmOperand::Const {
- anon_const: self.lower_anon_const(anon_const),
- },
- InlineAsmOperand::Sym { ref expr } => {
- hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) }
- }
- };
- (op, *op_sp)
- })
- .collect();
-
- // Validate template modifiers against the register classes for the operands
- for p in &asm.template {
- if let InlineAsmTemplatePiece::Placeholder {
- operand_idx,
- modifier: Some(modifier),
- span: placeholder_span,
- } = *p
- {
- let op_sp = asm.operands[operand_idx].1;
- match &operands[operand_idx].0 {
- hir::InlineAsmOperand::In { reg, .. }
- | hir::InlineAsmOperand::Out { reg, .. }
- | hir::InlineAsmOperand::InOut { reg, .. }
- | hir::InlineAsmOperand::SplitInOut { reg, .. } => {
- let class = reg.reg_class();
- if class == asm::InlineAsmRegClass::Err {
- continue;
- }
- let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
- if !valid_modifiers.contains(&modifier) {
- let mut err = sess.struct_span_err(
- placeholder_span,
- "invalid asm template modifier for this register class",
- );
- err.span_label(placeholder_span, "template modifier");
- err.span_label(op_sp, "argument");
- if !valid_modifiers.is_empty() {
- let mut mods = format!("`{}`", valid_modifiers[0]);
- for m in &valid_modifiers[1..] {
- let _ = write!(mods, ", `{}`", m);
- }
- err.note(&format!(
- "the `{}` register class supports \
- the following template modifiers: {}",
- class.name(),
- mods
- ));
- } else {
- err.note(&format!(
- "the `{}` register class does not support template modifiers",
- class.name()
- ));
- }
- err.emit();
- }
- }
- hir::InlineAsmOperand::Const { .. } => {
- let mut err = sess.struct_span_err(
- placeholder_span,
- "asm template modifiers are not allowed for `const` arguments",
- );
- err.span_label(placeholder_span, "template modifier");
- err.span_label(op_sp, "argument");
- err.emit();
- }
- hir::InlineAsmOperand::Sym { .. } => {
- let mut err = sess.struct_span_err(
- placeholder_span,
- "asm template modifiers are not allowed for `sym` arguments",
- );
- err.span_label(placeholder_span, "template modifier");
- err.span_label(op_sp, "argument");
- err.emit();
- }
- }
- }
- }
-
- let mut used_input_regs = FxHashMap::default();
- let mut used_output_regs = FxHashMap::default();
- let mut required_features: Vec<&str> = vec![];
- for (idx, &(ref op, op_sp)) in operands.iter().enumerate() {
- if let Some(reg) = op.reg() {
- // Make sure we don't accidentally carry features from the
- // previous iteration.
- required_features.clear();
-
- let reg_class = reg.reg_class();
- if reg_class == asm::InlineAsmRegClass::Err {
- continue;
- }
-
- // We ignore target feature requirements for clobbers: if the
- // feature is disabled then the compiler doesn't care what we
- // do with the registers.
- //
- // Note that this is only possible for explicit register
- // operands, which cannot be used in the asm string.
- let is_clobber = matches!(
- op,
- hir::InlineAsmOperand::Out {
- reg: asm::InlineAsmRegOrRegClass::Reg(_),
- late: _,
- expr: None
- }
- );
-
- if !is_clobber {
- // Validate register classes against currently enabled target
- // features. We check that at least one type is available for
- // the current target.
- for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
- if let Some(feature) = feature {
- if self.sess.target_features.contains(&Symbol::intern(feature)) {
- required_features.clear();
- break;
- } else {
- required_features.push(feature);
- }
- } else {
- required_features.clear();
- break;
- }
- }
- // We are sorting primitive strs here and can use unstable sort here
- required_features.sort_unstable();
- required_features.dedup();
- match &required_features[..] {
- [] => {}
- [feature] => {
- let msg = format!(
- "register class `{}` requires the `{}` target feature",
- reg_class.name(),
- feature
- );
- sess.struct_span_err(op_sp, &msg).emit();
- }
- features => {
- let msg = format!(
- "register class `{}` requires at least one target feature: {}",
- reg_class.name(),
- features.join(", ")
- );
- sess.struct_span_err(op_sp, &msg).emit();
- }
- }
- }
-
- // Check for conflicts between explicit register operands.
- if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
- let (input, output) = match op {
- hir::InlineAsmOperand::In { .. } => (true, false),
- // Late output do not conflict with inputs, but normal outputs do
- hir::InlineAsmOperand::Out { late, .. } => (!late, true),
- hir::InlineAsmOperand::InOut { .. }
- | hir::InlineAsmOperand::SplitInOut { .. } => (true, true),
- hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::Sym { .. } => {
- unreachable!()
- }
- };
-
- // Flag to output the error only once per operand
- let mut skip = false;
- reg.overlapping_regs(|r| {
- let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
- input| {
- match used_regs.entry(r) {
- Entry::Occupied(o) => {
- if skip {
- return;
- }
- skip = true;
-
- let idx2 = *o.get();
- let &(ref op2, op_sp2) = &operands[idx2];
- let reg2 = match op2.reg() {
- Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r,
- _ => unreachable!(),
- };
-
- let msg = format!(
- "register `{}` conflicts with register `{}`",
- reg.name(),
- reg2.name()
- );
- let mut err = sess.struct_span_err(op_sp, &msg);
- err.span_label(op_sp, &format!("register `{}`", reg.name()));
- err.span_label(op_sp2, &format!("register `{}`", reg2.name()));
-
- match (op, op2) {
- (
- hir::InlineAsmOperand::In { .. },
- hir::InlineAsmOperand::Out { late, .. },
- )
- | (
- hir::InlineAsmOperand::Out { late, .. },
- hir::InlineAsmOperand::In { .. },
- ) => {
- assert!(!*late);
- let out_op_sp = if input { op_sp2 } else { op_sp };
- let msg = "use `lateout` instead of \
- `out` to avoid conflict";
- err.span_help(out_op_sp, msg);
- }
- _ => {}
- }
-
- err.emit();
- }
- Entry::Vacant(v) => {
- v.insert(idx);
- }
- }
- };
- if input {
- check(&mut used_input_regs, true);
- }
- if output {
- check(&mut used_output_regs, false);
- }
- });
- }
- }
- }
-
- let operands = self.arena.alloc_from_iter(operands);
- let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
- let line_spans = self.arena.alloc_slice(&asm.line_spans[..]);
- let hir_asm = hir::InlineAsm { template, operands, options: asm.options, line_spans };
- hir::ExprKind::InlineAsm(self.arena.alloc(hir_asm))
- }
-
fn lower_expr_llvm_asm(&mut self, asm: &LlvmInlineAsm) -> hir::ExprKind<'hir> {
let inner = hir::LlvmInlineAsmInner {
inputs: asm.inputs.iter().map(|&(c, _)| c).collect(),
self.allow_try_trait.clone(),
);
- // `Try::into_result(<expr>)`
+ // `Try::branch(<expr>)`
let scrutinee = {
// expand <expr>
let sub_expr = self.lower_expr_mut(sub_expr);
self.expr_call_lang_item_fn(
unstable_span,
- hir::LangItem::TryIntoResult,
+ hir::LangItem::TryTraitBranch,
arena_vec![self; sub_expr],
)
};
};
let attrs = vec![attr];
- // `Ok(val) => #[allow(unreachable_code)] val,`
- let ok_arm = {
+ // `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,`
+ let continue_arm = {
let val_ident = Ident::with_dummy_span(sym::val);
let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
let val_expr = self.arena.alloc(self.expr_ident_with_attrs(
val_pat_nid,
ThinVec::from(attrs.clone()),
));
- let ok_pat = self.pat_ok(span, val_pat);
- self.arm(ok_pat, val_expr)
+ let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
+ self.arm(continue_pat, val_expr)
};
- // `Err(err) => #[allow(unreachable_code)]
- // return Try::from_error(From::from(err)),`
- let err_arm = {
- let err_ident = Ident::with_dummy_span(sym::err);
- let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident);
- let from_expr = {
- let err_expr = self.expr_ident_mut(try_span, err_ident, err_local_nid);
- self.expr_call_lang_item_fn(
- try_span,
- hir::LangItem::FromFrom,
- arena_vec![self; err_expr],
- )
- };
- let from_err_expr = self.wrap_in_try_constructor(
- hir::LangItem::TryFromError,
- unstable_span,
- from_expr,
+ // `ControlFlow::Break(residual) =>
+ // #[allow(unreachable_code)]
+ // return Try::from_residual(residual),`
+ let break_arm = {
+ let residual_ident = Ident::with_dummy_span(sym::residual);
+ let (residual_local, residual_local_nid) = self.pat_ident(try_span, residual_ident);
+ let residual_expr = self.expr_ident_mut(try_span, residual_ident, residual_local_nid);
+ let from_residual_expr = self.wrap_in_try_constructor(
+ hir::LangItem::TryTraitFromResidual,
+ try_span,
+ self.arena.alloc(residual_expr),
unstable_span,
);
let thin_attrs = ThinVec::from(attrs);
try_span,
hir::ExprKind::Break(
hir::Destination { label: None, target_id },
- Some(from_err_expr),
+ Some(from_residual_expr),
),
thin_attrs,
))
} else {
self.arena.alloc(self.expr(
try_span,
- hir::ExprKind::Ret(Some(from_err_expr)),
+ hir::ExprKind::Ret(Some(from_residual_expr)),
thin_attrs,
))
};
- let err_pat = self.pat_err(try_span, err_local);
- self.arm(err_pat, ret_expr)
+ let break_pat = self.pat_cf_break(try_span, residual_local);
+ self.arm(break_pat, ret_expr)
};
hir::ExprKind::Match(
scrutinee,
- arena_vec![self; err_arm, ok_arm],
+ arena_vec![self; break_arm, continue_arm],
hir::MatchSource::TryDesugar,
)
}