Auto merge of #32267 - durka:inclusive-range-error, r=nrc
melt the ICE when lowering an impossible range Emit a fatal error instead of panicking when HIR lowering encounters a range with no `end` point. This involved adding a method to wire up `LoweringContext::span_fatal`. Fixes #32245 (cc @nodakai). r? @nrc
This commit is contained in:
commit
44a77f6769
6 changed files with 88 additions and 32 deletions
|
@ -348,6 +348,10 @@ impl NodeIdAssigner for Session {
|
||||||
fn peek_node_id(&self) -> NodeId {
|
fn peek_node_id(&self) -> NodeId {
|
||||||
self.next_node_id.get().checked_add(1).unwrap()
|
self.next_node_id.get().checked_add(1).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn diagnostic(&self) -> &errors::Handler {
|
||||||
|
self.diagnostic()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_msg_into_multilines(msg: &str) -> Option<String> {
|
fn split_msg_into_multilines(msg: &str) -> Option<String> {
|
||||||
|
|
|
@ -68,6 +68,7 @@ use std::collections::HashMap;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use syntax::ast::*;
|
use syntax::ast::*;
|
||||||
use syntax::attr::{ThinAttributes, ThinAttributesExt};
|
use syntax::attr::{ThinAttributes, ThinAttributesExt};
|
||||||
|
use syntax::errors::Handler;
|
||||||
use syntax::ext::mtwt;
|
use syntax::ext::mtwt;
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
use syntax::codemap::{respan, Spanned, Span};
|
use syntax::codemap::{respan, Spanned, Span};
|
||||||
|
@ -140,6 +141,11 @@ impl<'a, 'hir> LoweringContext<'a> {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Panics if this LoweringContext's NodeIdAssigner is not able to emit diagnostics.
|
||||||
|
fn diagnostic(&self) -> &Handler {
|
||||||
|
self.id_assigner.diagnostic()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility fn for setting and unsetting the cached id.
|
// Utility fn for setting and unsetting the cached id.
|
||||||
|
@ -1289,7 +1295,8 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
|
||||||
make_struct(lctx, e, &["RangeInclusive", "NonEmpty"],
|
make_struct(lctx, e, &["RangeInclusive", "NonEmpty"],
|
||||||
&[("start", e1), ("end", e2)]),
|
&[("start", e1), ("end", e2)]),
|
||||||
|
|
||||||
_ => panic!("impossible range in AST"),
|
_ => panic!(lctx.diagnostic().span_fatal(e.span,
|
||||||
|
"inclusive range with no end"))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub use self::PathParameters::*;
|
||||||
use attr::ThinAttributes;
|
use attr::ThinAttributes;
|
||||||
use codemap::{Span, Spanned, DUMMY_SP, ExpnId};
|
use codemap::{Span, Spanned, DUMMY_SP, ExpnId};
|
||||||
use abi::Abi;
|
use abi::Abi;
|
||||||
|
use errors;
|
||||||
use ext::base;
|
use ext::base;
|
||||||
use ext::tt::macro_parser;
|
use ext::tt::macro_parser;
|
||||||
use parse::token::InternedString;
|
use parse::token::InternedString;
|
||||||
|
@ -344,6 +345,10 @@ pub const DUMMY_NODE_ID: NodeId = !0;
|
||||||
pub trait NodeIdAssigner {
|
pub trait NodeIdAssigner {
|
||||||
fn next_node_id(&self) -> NodeId;
|
fn next_node_id(&self) -> NodeId;
|
||||||
fn peek_node_id(&self) -> NodeId;
|
fn peek_node_id(&self) -> NodeId;
|
||||||
|
|
||||||
|
fn diagnostic(&self) -> &errors::Handler {
|
||||||
|
panic!("this ID assigner cannot emit diagnostics")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The AST represents all type param bounds as types.
|
/// The AST represents all type param bounds as types.
|
||||||
|
|
|
@ -2072,8 +2072,15 @@ impl<'a> Parser<'a> {
|
||||||
start: Option<P<Expr>>,
|
start: Option<P<Expr>>,
|
||||||
end: Option<P<Expr>>,
|
end: Option<P<Expr>>,
|
||||||
limits: RangeLimits)
|
limits: RangeLimits)
|
||||||
-> ast::ExprKind {
|
-> PResult<'a, ast::ExprKind> {
|
||||||
ExprKind::Range(start, end, limits)
|
if end.is_none() && limits == RangeLimits::Closed {
|
||||||
|
Err(self.span_fatal_help(self.span,
|
||||||
|
"inclusive range with no end",
|
||||||
|
"inclusive ranges must be bounded at the end \
|
||||||
|
(`...b` or `a...b`)"))
|
||||||
|
} else {
|
||||||
|
Ok(ExprKind::Range(start, end, limits))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mk_field(&mut self, expr: P<Expr>, ident: ast::SpannedIdent) -> ast::ExprKind {
|
pub fn mk_field(&mut self, expr: P<Expr>, ident: ast::SpannedIdent) -> ast::ExprKind {
|
||||||
|
@ -2999,12 +3006,12 @@ impl<'a> Parser<'a> {
|
||||||
lhs = self.mk_expr(lhs_span.lo, rhs.span.hi,
|
lhs = self.mk_expr(lhs_span.lo, rhs.span.hi,
|
||||||
ExprKind::Type(lhs, rhs), None);
|
ExprKind::Type(lhs, rhs), None);
|
||||||
continue
|
continue
|
||||||
} else if op == AssocOp::DotDot {
|
} else if op == AssocOp::DotDot || op == AssocOp::DotDotDot {
|
||||||
// If we didn’t have to handle `x..`, it would be pretty easy to generalise
|
// If we didn’t have to handle `x..`/`x...`, it would be pretty easy to
|
||||||
// it to the Fixity::None code.
|
// generalise it to the Fixity::None code.
|
||||||
//
|
//
|
||||||
// We have 2 alternatives here: `x..y` and `x..` The other two variants are
|
// We have 2 alternatives here: `x..y`/`x...y` and `x..`/`x...` The other
|
||||||
// handled with `parse_prefix_range_expr` call above.
|
// two variants are handled with `parse_prefix_range_expr` call above.
|
||||||
let rhs = if self.is_at_start_of_range_notation_rhs() {
|
let rhs = if self.is_at_start_of_range_notation_rhs() {
|
||||||
let rhs = self.parse_assoc_expr_with(op.precedence() + 1,
|
let rhs = self.parse_assoc_expr_with(op.precedence() + 1,
|
||||||
LhsExpr::NotYetParsed);
|
LhsExpr::NotYetParsed);
|
||||||
|
@ -3023,7 +3030,13 @@ impl<'a> Parser<'a> {
|
||||||
} else {
|
} else {
|
||||||
cur_op_span
|
cur_op_span
|
||||||
});
|
});
|
||||||
let r = self.mk_range(Some(lhs), rhs, RangeLimits::HalfOpen);
|
let limits = if op == AssocOp::DotDot {
|
||||||
|
RangeLimits::HalfOpen
|
||||||
|
} else {
|
||||||
|
RangeLimits::Closed
|
||||||
|
};
|
||||||
|
|
||||||
|
let r = try!(self.mk_range(Some(lhs), rhs, limits));
|
||||||
lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, None);
|
lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, None);
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -3041,8 +3054,8 @@ impl<'a> Parser<'a> {
|
||||||
this.parse_assoc_expr_with(op.precedence() + 1,
|
this.parse_assoc_expr_with(op.precedence() + 1,
|
||||||
LhsExpr::NotYetParsed)
|
LhsExpr::NotYetParsed)
|
||||||
}),
|
}),
|
||||||
// the only operator handled here is `...` (the other non-associative operators are
|
// We currently have no non-associative operators that are not handled above by
|
||||||
// special-cased above)
|
// the special cases. The code is here only for future convenience.
|
||||||
Fixity::None => self.with_res(
|
Fixity::None => self.with_res(
|
||||||
restrictions - Restrictions::RESTRICTION_STMT_EXPR,
|
restrictions - Restrictions::RESTRICTION_STMT_EXPR,
|
||||||
|this| {
|
|this| {
|
||||||
|
@ -3083,13 +3096,8 @@ impl<'a> Parser<'a> {
|
||||||
let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs);
|
let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs);
|
||||||
self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr, None)
|
self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr, None)
|
||||||
}
|
}
|
||||||
AssocOp::DotDotDot => {
|
AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotDot => {
|
||||||
let (lhs_span, rhs_span) = (lhs.span, rhs.span);
|
self.bug("As, Colon, DotDot or DotDotDot branch reached")
|
||||||
let r = self.mk_range(Some(lhs), Some(rhs), RangeLimits::Closed);
|
|
||||||
self.mk_expr(lhs_span.lo, rhs_span.hi, r, None)
|
|
||||||
}
|
|
||||||
AssocOp::As | AssocOp::Colon | AssocOp::DotDot => {
|
|
||||||
self.bug("As, Colon or DotDot branch reached")
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3141,13 +3149,15 @@ impl<'a> Parser<'a> {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let r = self.mk_range(None,
|
let limits = if tok == token::DotDot {
|
||||||
opt_end,
|
|
||||||
if tok == token::DotDot {
|
|
||||||
RangeLimits::HalfOpen
|
RangeLimits::HalfOpen
|
||||||
} else {
|
} else {
|
||||||
RangeLimits::Closed
|
RangeLimits::Closed
|
||||||
});
|
};
|
||||||
|
|
||||||
|
let r = try!(self.mk_range(None,
|
||||||
|
opt_end,
|
||||||
|
limits));
|
||||||
Ok(self.mk_expr(lo, hi, r, attrs))
|
Ok(self.mk_expr(lo, hi, r, attrs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
29
src/test/compile-fail/impossible_range.rs
Normal file
29
src/test/compile-fail/impossible_range.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Make sure that invalid ranges generate an error during HIR lowering, not an ICE
|
||||||
|
|
||||||
|
#![feature(inclusive_range_syntax)]
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
..;
|
||||||
|
0..;
|
||||||
|
..1;
|
||||||
|
0..1;
|
||||||
|
|
||||||
|
...; //~ERROR inclusive range with no end
|
||||||
|
//~^HELP bounded at the end
|
||||||
|
0...; //~ERROR inclusive range with no end
|
||||||
|
//~^HELP bounded at the end
|
||||||
|
...1;
|
||||||
|
0...1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#![feature(inclusive_range_syntax, inclusive_range)]
|
#![feature(inclusive_range_syntax, inclusive_range)]
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
for _ in 1... {}
|
for _ in 1... {} //~ERROR inclusive range with no end
|
||||||
} //~ ERROR expected one of
|
//~^HELP bounded at the end
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue