From 5b5638f6863803477d56e200d6a9a208015838c1 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 19 Nov 2014 10:17:40 +1100 Subject: Switch to an independent enum for `Lit*` subtokens. --- src/libsyntax/parse/parser.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src/libsyntax/parse/parser.rs') diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 40c4ac9f8c0..4edcb182e53 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1640,23 +1640,23 @@ impl<'a> Parser<'a> { /// Matches token_lit = LIT_INTEGER | ... pub fn lit_from_token(&mut self, tok: &token::Token) -> Lit_ { match *tok { - token::LitByte(i) => LitByte(parse::byte_lit(i.as_str()).val0()), - token::LitChar(i) => LitChar(parse::char_lit(i.as_str()).val0()), - token::LitInteger(s) => parse::integer_lit(s.as_str(), + token::Literal(token::Byte(i)) => LitByte(parse::byte_lit(i.as_str()).val0()), + token::Literal(token::Char(i)) => LitChar(parse::char_lit(i.as_str()).val0()), + token::Literal(token::Integer(s)) => parse::integer_lit(s.as_str(), &self.sess.span_diagnostic, self.last_span), - token::LitFloat(s) => parse::float_lit(s.as_str()), - token::LitStr(s) => { + token::Literal(token::Float(s)) => parse::float_lit(s.as_str()), + token::Literal(token::Str_(s)) => { LitStr(token::intern_and_get_ident(parse::str_lit(s.as_str()).as_slice()), ast::CookedStr) } - token::LitStrRaw(s, n) => { + token::Literal(token::StrRaw(s, n)) => { LitStr(token::intern_and_get_ident(parse::raw_str_lit(s.as_str()).as_slice()), ast::RawStr(n)) } - token::LitBinary(i) => + token::Literal(token::Binary(i)) => LitBinary(parse::binary_lit(i.as_str())), - token::LitBinaryRaw(i, _) => + token::Literal(token::BinaryRaw(i, _)) => LitBinary(Rc::new(i.as_str().as_bytes().iter().map(|&x| x).collect())), _ => { self.unexpected_last(tok); } } @@ -2424,7 +2424,7 @@ impl<'a> Parser<'a> { } } } - token::LitInteger(n) => { + token::Literal(token::Integer(n)) => { let index = n.as_str(); let dot = self.last_span.hi; hi = self.span.hi; @@ -2449,7 +2449,7 @@ impl<'a> Parser<'a> { } } } - token::LitFloat(n) => { + token::Literal(token::Float(n)) => { self.bump(); let last_span = self.last_span; let fstr = n.as_str(); @@ -5085,7 +5085,7 @@ impl<'a> Parser<'a> { self.expect(&token::Semi); (path, the_ident) }, - token::LitStr(..) | token::LitStrRaw(..) => { + token::Literal(token::Str_(..)) | token::Literal(token::StrRaw(..)) => { let path = self.parse_str(); self.expect_keyword(keywords::As); let the_ident = self.parse_ident(); @@ -5267,7 +5267,7 @@ impl<'a> Parser<'a> { /// the `extern` keyword, if one is found. fn parse_opt_abi(&mut self) -> Option { match self.token { - token::LitStr(s) | token::LitStrRaw(s, _) => { + token::Literal(token::Str_(s)) | token::Literal(token::StrRaw(s, _)) => { self.bump(); let the_string = s.as_str(); match abi::lookup(the_string) { @@ -5904,8 +5904,8 @@ impl<'a> Parser<'a> { pub fn parse_optional_str(&mut self) -> Option<(InternedString, ast::StrStyle)> { let (s, style) = match self.token { - token::LitStr(s) => (self.id_to_interned_str(s.ident()), ast::CookedStr), - token::LitStrRaw(s, n) => { + token::Literal(token::Str_(s)) => (self.id_to_interned_str(s.ident()), ast::CookedStr), + token::Literal(token::StrRaw(s, n)) => { (self.id_to_interned_str(s.ident()), ast::RawStr(n)) } _ => return None -- cgit 1.4.1-3-g733a5 From b64c7b83dd08c7c3afc643564d65975d57785172 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 8 Nov 2014 06:59:10 -0500 Subject: Refactor QPath to take an ast::TraitRef --- src/librustc/middle/resolve.rs | 63 +--------- src/librustc/middle/subst.rs | 4 + src/librustc/middle/traits/mod.rs | 5 +- src/librustc/middle/traits/select.rs | 2 +- src/librustc/middle/traits/util.rs | 3 +- src/librustc/middle/typeck/astconv.rs | 128 ++++++--------------- src/librustc/middle/typeck/collect.rs | 31 ++--- src/librustc/util/common.rs | 4 + src/libsyntax/ast.rs | 6 +- src/libsyntax/fold.rs | 22 +++- src/libsyntax/parse/parser.rs | 10 +- src/libsyntax/print/pprust.rs | 4 +- src/libsyntax/visit.rs | 4 +- .../associated-types-in-ambiguous-context.rs | 4 +- .../associated-types-in-wrong-context.rs | 4 +- ...alified-path-with-trait-with-type-parameters.rs | 18 +++ 16 files changed, 122 insertions(+), 190 deletions(-) create mode 100644 src/test/run-pass/associated-types-qualified-path-with-trait-with-type-parameters.rs (limited to 'src/libsyntax/parse/parser.rs') diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index baf53cc34ba..f369f00a14e 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -633,6 +633,7 @@ enum TraitReferenceType { TraitDerivation, // trait T : SomeTrait { ... } TraitBoundingTypeParameter, // fn f() { ... } TraitObject, // Box SomeTrait> + TraitQPath, // :: } impl NameBindings { @@ -4532,6 +4533,7 @@ impl<'a> Resolver<'a> { TraitImplementation => "implement", TraitDerivation => "derive", TraitObject => "reference", + TraitQPath => "extract an associated type from", }; let msg = format!("attempt to {} a nonexistent trait `{}`", usage_str, path_str); @@ -4969,65 +4971,8 @@ impl<'a> Resolver<'a> { } TyQPath(ref qpath) => { - self.resolve_type(&*qpath.for_type); - - let current_module = self.current_module.clone(); - let module_path: Vec<_> = - qpath.trait_name - .segments - .iter() - .map(|ps| ps.identifier.name) - .collect(); - match self.resolve_module_path( - current_module, - module_path.as_slice(), - UseLexicalScope, - qpath.trait_name.span, - PathSearch) { - Success((ref module, _)) if module.kind.get() == - TraitModuleKind => { - match self.resolve_definition_of_name_in_module( - (*module).clone(), - qpath.item_name.name, - TypeNS) { - ChildNameDefinition(def, lp) | - ImportNameDefinition(def, lp) => { - match def { - DefAssociatedTy(trait_type_id) => { - let def = DefAssociatedTy( - trait_type_id); - self.record_def(ty.id, (def, lp)); - } - _ => { - self.resolve_error( - ty.span, - "not an associated type"); - } - } - } - NoNameDefinition => { - self.resolve_error(ty.span, - "unresolved associated \ - type"); - } - } - } - Success(..) => self.resolve_error(ty.span, "not a trait"), - Indeterminate => { - self.session.span_bug(ty.span, - "indeterminate result when \ - resolving associated type") - } - Failed(error) => { - let (span, help) = match error { - Some((span, msg)) => (span, format!("; {}", msg)), - None => (ty.span, String::new()), - }; - self.resolve_error(span, - format!("unresolved trait: {}", - help).as_slice()) - } - } + self.resolve_type(&*qpath.self_type); + self.resolve_trait_reference(ty.id, &*qpath.trait_ref, TraitQPath); } TyClosure(ref c) | TyProc(ref c) => { diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index bac417c8218..b030867fc84 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -100,6 +100,10 @@ impl<'tcx> Substs<'tcx> { regions_is_noop && self.types.is_empty() } + pub fn type_for_def(&self, ty_param_def: &ty::TypeParameterDef) -> Ty<'tcx> { + *self.types.get(ty_param_def.space, ty_param_def.index) + } + pub fn has_regions_escaping_depth(&self, depth: uint) -> bool { self.types.iter().any(|&t| ty::type_escapes_depth(t, depth)) || { match self.regions { diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index c5eacf35da9..d34d413225e 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -25,6 +25,7 @@ use std::rc::Rc; use std::slice::Items; use syntax::ast; use syntax::codemap::{Span, DUMMY_SP}; +use util::common::ErrorReported; pub use self::fulfill::FulfillmentContext; pub use self::select::SelectionContext; @@ -95,10 +96,6 @@ pub enum ObligationCauseCode<'tcx> { FieldSized, } -// An error has already been reported to the user, so no need to continue checking. -#[deriving(Clone,Show)] -pub struct ErrorReported; - pub type Obligations<'tcx> = subst::VecPerParamSpace>; pub type Selection<'tcx> = Vtable<'tcx, Obligation<'tcx>>; diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index b884cb535d7..9cb7023e1b5 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -17,7 +17,6 @@ use self::Candidate::*; use self::BuiltinBoundConditions::*; use self::EvaluationResult::*; -use super::{ErrorReported}; use super::{Obligation, ObligationCause}; use super::{SelectionError, Unimplemented, Overflow, OutputTypeParameterMismatch}; @@ -38,6 +37,7 @@ use std::cell::RefCell; use std::collections::hash_map::HashMap; use std::rc::Rc; use syntax::ast; +use util::common::ErrorReported; use util::ppaux::Repr; pub struct SelectionContext<'cx, 'tcx:'cx> { diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index dfd436bdc4d..ec49d501056 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -18,9 +18,10 @@ use std::fmt; use std::rc::Rc; use syntax::ast; use syntax::codemap::Span; +use util::common::ErrorReported; use util::ppaux::Repr; -use super::{ErrorReported, Obligation, ObligationCause, VtableImpl, +use super::{Obligation, ObligationCause, VtableImpl, VtableParam, VtableParamData, VtableImplData}; /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index ea652bc7e65..637c1f58157 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -207,7 +207,6 @@ fn ast_path_substs_for_ty<'tcx,AC,RS>( decl_def_id: ast::DefId, decl_generics: &ty::Generics<'tcx>, self_ty: Option>, - associated_ty: Option>, path: &ast::Path) -> Substs<'tcx> where AC: AstConv<'tcx>, RS: RegionScope @@ -243,7 +242,7 @@ fn ast_path_substs_for_ty<'tcx,AC,RS>( }; create_substs_for_ast_path(this, rscope, path.span, decl_def_id, - decl_generics, self_ty, types, regions, associated_ty) + decl_generics, self_ty, types, regions) } fn create_substs_for_ast_path<'tcx,AC,RS>( @@ -254,8 +253,7 @@ fn create_substs_for_ast_path<'tcx,AC,RS>( decl_generics: &ty::Generics<'tcx>, self_ty: Option>, types: Vec>, - regions: Vec, - associated_ty: Option>) + regions: Vec) -> Substs<'tcx> where AC: AstConv<'tcx>, RS: RegionScope { @@ -366,9 +364,9 @@ fn create_substs_for_ast_path<'tcx,AC,RS>( substs.types.push( AssocSpace, this.associated_type_binding(span, - associated_ty, + self_ty, decl_def_id, - param.def_id)) + param.def_id)); } return substs; @@ -417,19 +415,17 @@ pub fn instantiate_poly_trait_ref<'tcx,AC,RS>( this: &AC, rscope: &RS, ast_trait_ref: &ast::PolyTraitRef, - self_ty: Option>, - associated_type: Option>) + self_ty: Option>) -> Rc> where AC: AstConv<'tcx>, RS: RegionScope { - instantiate_trait_ref(this, rscope, &ast_trait_ref.trait_ref, self_ty, associated_type) + instantiate_trait_ref(this, rscope, &ast_trait_ref.trait_ref, self_ty) } pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC, rscope: &RS, ast_trait_ref: &ast::TraitRef, - self_ty: Option>, - associated_type: Option>) + self_ty: Option>) -> Rc> where AC: AstConv<'tcx>, RS: RegionScope @@ -444,8 +440,8 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC, ast_trait_ref.path.span, ast_trait_ref.ref_id) { def::DefTrait(trait_def_id) => { - let trait_ref = Rc::new(ast_path_to_trait_ref(this, rscope, trait_def_id, self_ty, - associated_type, &ast_trait_ref.path)); + let trait_ref = Rc::new(ast_path_to_trait_ref(this, rscope, trait_def_id, + self_ty, &ast_trait_ref.path)); this.tcx().trait_refs.borrow_mut().insert(ast_trait_ref.ref_id, trait_ref.clone()); trait_ref @@ -463,7 +459,6 @@ fn ast_path_to_trait_ref<'tcx,AC,RS>( rscope: &RS, trait_def_id: ast::DefId, self_ty: Option>, - associated_type: Option>, path: &ast::Path) -> ty::TraitRef<'tcx> where AC: AstConv<'tcx>, RS: RegionScope @@ -493,8 +488,7 @@ fn ast_path_to_trait_ref<'tcx,AC,RS>( &trait_def.generics, self_ty, types, - regions, - associated_type); + regions); ty::TraitRef::new(trait_def_id, substs) } @@ -517,7 +511,6 @@ pub fn ast_path_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( did, &generics, None, - None, path); let ty = decl_ty.subst(tcx, &substs); TypeAndSubsts { substs: substs, ty: ty } @@ -558,7 +551,7 @@ pub fn ast_path_to_ty_relaxed<'tcx,AC,RS>( Substs::new(VecPerParamSpace::params_from_type(type_params), VecPerParamSpace::params_from_type(region_params)) } else { - ast_path_substs_for_ty(this, rscope, did, &generics, None, None, path) + ast_path_substs_for_ty(this, rscope, did, &generics, None, path) }; let ty = decl_ty.subst(tcx, &substs); @@ -726,7 +719,6 @@ fn mk_pointer<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( rscope, trait_def_id, None, - None, path); let empty_vec = []; let bounds = match *opt_bounds { None => empty_vec.as_slice(), @@ -750,61 +742,37 @@ fn mk_pointer<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( constr(ast_ty_to_ty(this, rscope, a_seq_ty)) } -fn associated_ty_to_ty<'tcx,AC,RS>(this: &AC, - rscope: &RS, - trait_path: &ast::Path, - for_ast_type: &ast::Ty, - trait_type_id: ast::DefId, - span: Span) - -> Ty<'tcx> - where AC: AstConv<'tcx>, RS: RegionScope +fn qpath_to_ty<'tcx,AC,RS>(this: &AC, + rscope: &RS, + ast_ty: &ast::Ty, // the TyQPath + qpath: &ast::QPath) + -> Ty<'tcx> + where AC: AstConv<'tcx>, RS: RegionScope { - debug!("associated_ty_to_ty(trait_path={}, for_ast_type={}, trait_type_id={})", - trait_path.repr(this.tcx()), - for_ast_type.repr(this.tcx()), - trait_type_id.repr(this.tcx())); - - // Find the trait that this associated type belongs to. - let trait_did = match ty::impl_or_trait_item(this.tcx(), - trait_type_id).container() { - ty::ImplContainer(_) => { - this.tcx().sess.span_bug(span, - "associated_ty_to_ty(): impl associated \ - types shouldn't go through this \ - function") - } - ty::TraitContainer(trait_id) => trait_id, - }; + debug!("qpath_to_ty(ast_ty={})", + ast_ty.repr(this.tcx())); - let for_type = ast_ty_to_ty(this, rscope, for_ast_type); - if !this.associated_types_of_trait_are_valid(for_type, trait_did) { - this.tcx().sess.span_err(span, - "this associated type is not \ - allowed in this context"); - return ty::mk_err() - } + let self_type = ast_ty_to_ty(this, rscope, &*qpath.self_type); + + debug!("qpath_to_ty: self_type={}", self_type.repr(this.tcx())); - let trait_ref = ast_path_to_trait_ref(this, + let trait_ref = instantiate_trait_ref(this, rscope, - trait_did, - None, - Some(for_type), - trait_path); - - debug!("associated_ty_to_ty(trait_ref={})", - trait_ref.repr(this.tcx())); - - let trait_def = this.get_trait_def(trait_did); - for type_parameter in trait_def.generics.types.iter() { - if type_parameter.def_id == trait_type_id { - debug!("associated_ty_to_ty(type_parameter={} substs={})", - type_parameter.repr(this.tcx()), - trait_ref.substs.repr(this.tcx())); - return *trait_ref.substs.types.get(type_parameter.space, - type_parameter.index) + &*qpath.trait_ref, + Some(self_type)); + + debug!("qpath_to_ty: trait_ref={}", trait_ref.repr(this.tcx())); + + let trait_def = this.get_trait_def(trait_ref.def_id); + + for ty_param_def in trait_def.generics.types.get_slice(AssocSpace).iter() { + if ty_param_def.name == qpath.item_name.name { + debug!("qpath_to_ty: corresponding ty_param_def={}", ty_param_def); + return trait_ref.substs.type_for_def(ty_param_def); } } - this.tcx().sess.span_bug(span, + + this.tcx().sess.span_bug(ast_ty.span, "this associated type didn't get added \ as a parameter for some reason") } @@ -931,7 +899,6 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( rscope, trait_def_id, None, - None, path); let empty_bounds: &[ast::TyParamBound] = &[]; let ast_bounds = match *bounds { @@ -996,26 +963,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( } } ast::TyQPath(ref qpath) => { - match tcx.def_map.borrow().get(&ast_ty.id) { - None => { - tcx.sess.span_bug(ast_ty.span, - "unbound qualified path") - } - Some(&def::DefAssociatedTy(trait_type_id)) => { - associated_ty_to_ty(this, - rscope, - &qpath.trait_name, - &*qpath.for_type, - trait_type_id, - ast_ty.span) - } - Some(_) => { - tcx.sess.span_err(ast_ty.span, - "this qualified path does not name \ - an associated type"); - ty::mk_err() - } - } + qpath_to_ty(this, rscope, ast_ty, &**qpath) } ast::TyFixedLengthVec(ref ty, ref e) => { match const_eval::eval_const_expr_partial(tcx, &**e) { @@ -1411,7 +1359,7 @@ fn conv_ty_poly_trait_ref<'tcx, AC, RS>( let main_trait_bound = match partitioned_bounds.trait_bounds.remove(0) { Some(trait_bound) => { - Some(instantiate_poly_trait_ref(this, rscope, trait_bound, None, None)) + Some(instantiate_poly_trait_ref(this, rscope, trait_bound, None)) } None => { this.tcx().sess.span_err( diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index b0e8b664d06..90ad0d2f3e5 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -684,7 +684,11 @@ fn find_associated_type_in_generics<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Option>, associated_type_id: ast::DefId, generics: &ty::Generics<'tcx>) - -> Ty<'tcx> { + -> Ty<'tcx> +{ + debug!("find_associated_type_in_generics(ty={}, associated_type_id={}, generics={}", + ty.repr(tcx), associated_type_id.repr(tcx), generics.repr(tcx)); + let ty = match ty { None => { tcx.sess.span_bug(span, @@ -703,20 +707,22 @@ fn find_associated_type_in_generics<'tcx>(tcx: &ty::ctxt<'tcx>, for type_parameter in generics.types.iter() { if type_parameter.def_id == associated_type_id && type_parameter.associated_with == Some(param_id) { - return ty::mk_param_from_def(tcx, type_parameter) + return ty::mk_param_from_def(tcx, type_parameter); } } - tcx.sess.span_bug(span, - "find_associated_type_in_generics(): didn't \ - find associated type anywhere in the generics \ - list") + tcx.sess.span_err( + span, + format!("no suitable bound on `{}`", + ty.user_string(tcx))[]); + ty::mk_err() } _ => { - tcx.sess.span_bug(span, - "find_associated_type_in_generics(): self type \ - is not a parameter") - + tcx.sess.span_err( + span, + "it is currently unsupported to access associated types except \ + through a type parameter; this restriction will be lifted in time"); + ty::mk_err() } } } @@ -1155,7 +1161,7 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) { for trait_ref in opt_trait_ref.iter() { astconv::instantiate_trait_ref(&icx, &ExplicitRscope, trait_ref, - Some(selfty), None); + Some(selfty)); } }, ast::ItemTrait(_, _, _, ref trait_methods) => { @@ -1627,7 +1633,7 @@ fn ty_generics_for_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, ccx, subst::AssocSpace, &associated_type.ty_param, - generics.types.len(subst::TypeSpace), + generics.types.len(subst::AssocSpace), &ast_generics.where_clause, Some(local_def(trait_id))); ccx.tcx.ty_param_defs.borrow_mut().insert(associated_type.ty_param.id, @@ -2019,7 +2025,6 @@ fn conv_param_bounds<'tcx,AC>(this: &AC, astconv::instantiate_poly_trait_ref(this, &ExplicitRscope, bound, - Some(param_ty.to_ty(this.tcx())), Some(param_ty.to_ty(this.tcx()))) }) .collect(); diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index 06154f68613..cdbe107e11c 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -20,6 +20,10 @@ use syntax::ast; use syntax::visit; use syntax::visit::Visitor; +// An error has already been reported to the user, so no need to continue checking. +#[deriving(Clone,Show)] +pub struct ErrorReported; + pub fn time(do_it: bool, what: &str, u: U, f: |U| -> T) -> T { local_data_key!(depth: uint); if !do_it { return f(u); } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 15e14902727..61e56f0cc42 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -706,11 +706,11 @@ pub enum Expr_ { /// /// as SomeTrait>::SomeAssociatedItem /// ^~~~~ ^~~~~~~~~ ^~~~~~~~~~~~~~~~~~ -/// for_type trait_name item_name +/// self_type trait_name item_name #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub struct QPath { - pub for_type: P, - pub trait_name: Path, + pub self_type: P, + pub trait_ref: P, pub item_name: Ident, } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index b3137ff5f7e..2e6ee49f0ff 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -142,6 +142,10 @@ pub trait Folder { noop_fold_ty(t, self) } + fn fold_qpath(&mut self, t: P) -> P { + noop_fold_qpath(t, self) + } + fn fold_mod(&mut self, m: Mod) -> Mod { noop_fold_mod(m, self) } @@ -435,12 +439,8 @@ pub fn noop_fold_ty(t: P, fld: &mut T) -> P { fld.fold_opt_bounds(bounds), id) } - TyQPath(ref qpath) => { - TyQPath(P(QPath { - for_type: fld.fold_ty(qpath.for_type.clone()), - trait_name: fld.fold_path(qpath.trait_name.clone()), - item_name: fld.fold_ident(qpath.item_name.clone()), - })) + TyQPath(qpath) => { + TyQPath(fld.fold_qpath(qpath)) } TyFixedLengthVec(ty, e) => { TyFixedLengthVec(fld.fold_ty(ty), fld.fold_expr(e)) @@ -456,6 +456,16 @@ pub fn noop_fold_ty(t: P, fld: &mut T) -> P { }) } +pub fn noop_fold_qpath(qpath: P, fld: &mut T) -> P { + qpath.map(|qpath| { + QPath { + self_type: fld.fold_ty(qpath.self_type), + trait_ref: qpath.trait_ref.map(|tr| fld.fold_trait_ref(tr)), + item_name: fld.fold_ident(qpath.item_name), + } + }) +} + pub fn noop_fold_foreign_mod(ForeignMod {abi, view_items, items}: ForeignMod, fld: &mut T) -> ForeignMod { ForeignMod { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 50b1a2204b0..d3ae9838c6d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1502,17 +1502,17 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(keywords::Proc) { self.parse_proc_type(Vec::new()) } else if self.token == token::Lt { - // QUALIFIED PATH + // QUALIFIED PATH `::item` self.bump(); - let for_type = self.parse_ty(true); + let self_type = self.parse_ty(true); self.expect_keyword(keywords::As); - let trait_name = self.parse_path(LifetimeAndTypesWithoutColons); + let trait_ref = self.parse_trait_ref(); self.expect(&token::Gt); self.expect(&token::ModSep); let item_name = self.parse_ident(); TyQPath(P(QPath { - for_type: for_type, - trait_name: trait_name.path, + self_type: self_type, + trait_ref: P(trait_ref), item_name: item_name, })) } else if self.token == token::ModSep || diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index e6e0c33a42d..fa6b70389b4 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -744,10 +744,10 @@ impl<'a> State<'a> { } ast::TyQPath(ref qpath) => { try!(word(&mut self.s, "<")); - try!(self.print_type(&*qpath.for_type)); + try!(self.print_type(&*qpath.self_type)); try!(space(&mut self.s)); try!(self.word_space("as")); - try!(self.print_path(&qpath.trait_name, false)); + try!(self.print_trait_ref(&*qpath.trait_ref)); try!(word(&mut self.s, ">")); try!(word(&mut self.s, "::")); try!(self.print_ident(qpath.item_name)); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index efe1e18eda9..bbbec5e0626 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -403,8 +403,8 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) { } } TyQPath(ref qpath) => { - visitor.visit_ty(&*qpath.for_type); - visitor.visit_path(&qpath.trait_name, typ.id); + visitor.visit_ty(&*qpath.self_type); + visitor.visit_trait_ref(&*qpath.trait_ref); visitor.visit_ident(typ.span, qpath.item_name); } TyFixedLengthVec(ref ty, ref expression) => { diff --git a/src/test/compile-fail/associated-types-in-ambiguous-context.rs b/src/test/compile-fail/associated-types-in-ambiguous-context.rs index a2c01fe62f6..24de1fa2f78 100644 --- a/src/test/compile-fail/associated-types-in-ambiguous-context.rs +++ b/src/test/compile-fail/associated-types-in-ambiguous-context.rs @@ -20,12 +20,12 @@ fn get(x: T, y: U) -> Get::Value {} trait Other { fn uhoh(&self, foo: U, bar: ::Value) {} - //~^ ERROR this associated type is not allowed in this context + //~^ ERROR no suitable bound on `Self` } impl Other for T { fn uhoh(&self, foo: U, bar: <(T, U) as Get>::Value) {} - //~^ ERROR this associated type is not allowed in this context + //~^ ERROR currently unsupported } trait Grab { diff --git a/src/test/compile-fail/associated-types-in-wrong-context.rs b/src/test/compile-fail/associated-types-in-wrong-context.rs index 8fbfc33896b..8cab2759ad5 100644 --- a/src/test/compile-fail/associated-types-in-wrong-context.rs +++ b/src/test/compile-fail/associated-types-in-wrong-context.rs @@ -16,7 +16,7 @@ trait Get { } fn get(x: int) -> ::Value {} -//~^ ERROR this associated type is not allowed in this context +//~^ ERROR unsupported struct Struct { x: int, @@ -24,7 +24,7 @@ struct Struct { impl Struct { fn uhoh(foo: ::Value) {} - //~^ ERROR this associated type is not allowed in this context + //~^ ERROR no suitable bound on `T` } fn main() { diff --git a/src/test/run-pass/associated-types-qualified-path-with-trait-with-type-parameters.rs b/src/test/run-pass/associated-types-qualified-path-with-trait-with-type-parameters.rs new file mode 100644 index 00000000000..1b4eb2604a8 --- /dev/null +++ b/src/test/run-pass/associated-types-qualified-path-with-trait-with-type-parameters.rs @@ -0,0 +1,18 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(associated_types)] + +trait Foo { + type Bar; + fn get_bar() -> >::Bar; +} + +fn main() { } -- cgit 1.4.1-3-g733a5 From 6679595853705ca11f64984a055be60233321a4a Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 19 Nov 2014 15:48:38 +1100 Subject: Parse and store suffixes on literals. This adds an optional suffix at the end of a literal token: `"foo"bar`. An actual use of a suffix in a expression (or other literal that the compiler reads) is rejected in the parser. This doesn't switch the handling of numbers to this system, and doesn't outlaw illegal suffixes for them yet. --- src/librustdoc/html/highlight.rs | 18 +++-- src/libsyntax/ast.rs | 2 +- src/libsyntax/diagnostics/plugin.rs | 2 +- src/libsyntax/ext/quote.rs | 33 +++++---- src/libsyntax/parse/lexer/mod.rs | 112 ++++++++++++++++++++++-------- src/libsyntax/parse/parser.rs | 109 +++++++++++++++++++++-------- src/libsyntax/parse/token.rs | 19 ++++- src/libsyntax/print/pprust.rs | 34 +++++---- src/test/compile-fail/bad-lit-suffixes.rs | 36 ++++++++++ 9 files changed, 268 insertions(+), 97 deletions(-) create mode 100644 src/test/compile-fail/bad-lit-suffixes.rs (limited to 'src/libsyntax/parse/parser.rs') diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 527ef553d99..111650f565c 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -128,13 +128,17 @@ fn doit(sess: &parse::ParseSess, mut lexer: lexer::StringReader, } } - // text literals - token::Literal(token::Byte(..)) | token::Literal(token::Char(..)) | - token::Literal(token::Binary(..)) | token::Literal(token::BinaryRaw(..)) | - token::Literal(token::Str_(..)) | token::Literal(token::StrRaw(..)) => "string", - - // number literals - token::Literal(token::Integer(..)) | token::Literal(token::Float(..)) => "number", + token::Literal(lit, _suf) => { + match lit { + // text literals + token::Byte(..) | token::Char(..) | + token::Binary(..) | token::BinaryRaw(..) | + token::Str_(..) | token::StrRaw(..) => "string", + + // number literals + token::Integer(..) | token::Float(..) => "number", + } + } // keywords are also included in the identifier set token::Ident(ident, _is_mod_sep) => { diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 2158bdb416c..7b16c087859 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -838,7 +838,7 @@ impl TokenTree { tts: vec![TtToken(sp, token::Ident(token::str_to_ident("doc"), token::Plain)), TtToken(sp, token::Eq), - TtToken(sp, token::Literal(token::Str_(name)))], + TtToken(sp, token::Literal(token::Str_(name), None))], close_span: sp, })) } diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs index b928fc778e8..281bde3129a 100644 --- a/src/libsyntax/diagnostics/plugin.rs +++ b/src/libsyntax/diagnostics/plugin.rs @@ -87,7 +87,7 @@ pub fn expand_register_diagnostic<'cx>(ecx: &'cx mut ExtCtxt, }, [ast::TtToken(_, token::Ident(ref code, _)), ast::TtToken(_, token::Comma), - ast::TtToken(_, token::Literal(token::StrRaw(description, _)))] => { + ast::TtToken(_, token::Literal(token::StrRaw(description, _), None))] => { (code, Some(description)) } _ => unreachable!() diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index ec51ce00605..eaa3632cf49 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -543,10 +543,13 @@ fn mk_delim(cx: &ExtCtxt, sp: Span, delim: token::DelimToken) -> P { #[allow(non_upper_case_globals)] fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P { macro_rules! mk_lit { - ($name: expr, $($args: expr),*) => {{ + ($name: expr, $suffix: expr, $($args: expr),*) => {{ let inner = cx.expr_call(sp, mk_token_path(cx, sp, $name), vec![$($args),*]); - - cx.expr_call(sp, mk_token_path(cx, sp, "Literal"), vec![inner]) + let suffix = match $suffix { + Some(name) => cx.expr_some(sp, mk_name(cx, sp, ast::Ident::new(name))), + None => cx.expr_none(sp) + }; + cx.expr_call(sp, mk_token_path(cx, sp, "Literal"), vec![inner, suffix]) }} } match *tok { @@ -567,32 +570,32 @@ fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P { vec![mk_delim(cx, sp, delim)]); } - token::Literal(token::Byte(i)) => { + token::Literal(token::Byte(i), suf) => { let e_byte = mk_name(cx, sp, i.ident()); - return mk_lit!("Byte", e_byte); + return mk_lit!("Byte", suf, e_byte); } - token::Literal(token::Char(i)) => { + token::Literal(token::Char(i), suf) => { let e_char = mk_name(cx, sp, i.ident()); - return mk_lit!("Char", e_char); + return mk_lit!("Char", suf, e_char); } - token::Literal(token::Integer(i)) => { + token::Literal(token::Integer(i), suf) => { let e_int = mk_name(cx, sp, i.ident()); - return mk_lit!("Integer", e_int); + return mk_lit!("Integer", suf, e_int); } - token::Literal(token::Float(fident)) => { + token::Literal(token::Float(fident), suf) => { let e_fident = mk_name(cx, sp, fident.ident()); - return mk_lit!("Float", e_fident); + return mk_lit!("Float", suf, e_fident); } - token::Literal(token::Str_(ident)) => { - return mk_lit!("Str_", mk_name(cx, sp, ident.ident())) + token::Literal(token::Str_(ident), suf) => { + return mk_lit!("Str_", suf, mk_name(cx, sp, ident.ident())) } - token::Literal(token::StrRaw(ident, n)) => { - return mk_lit!("StrRaw", mk_name(cx, sp, ident.ident()), cx.expr_uint(sp, n)) + token::Literal(token::StrRaw(ident, n), suf) => { + return mk_lit!("StrRaw", suf, mk_name(cx, sp, ident.ident()), cx.expr_uint(sp, n)) } token::Ident(ident, style) => { diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index b7598c7c428..55c4335941d 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -369,6 +369,25 @@ impl<'a> StringReader<'a> { self.nextnextch() == Some(c) } + /// Eats *, if possible. + fn scan_optional_raw_name(&mut self) -> Option { + if !ident_start(self.curr) { + return None + } + let start = self.last_pos; + while ident_continue(self.curr) { + self.bump(); + } + + self.with_str_from(start, |string| { + if string == "_" { + None + } else { + Some(token::intern(string)) + } + }) + } + /// PRECONDITION: self.curr is not whitespace /// Eats any kind of comment. fn scan_comment(&mut self) -> Option { @@ -638,7 +657,7 @@ impl<'a> StringReader<'a> { } /// Lex a LIT_INTEGER or a LIT_FLOAT - fn scan_number(&mut self, c: char) -> token::Token { + fn scan_number(&mut self, c: char) -> token::Lit { let mut num_digits; let mut base = 10; let start_bpos = self.last_pos; @@ -655,17 +674,17 @@ impl<'a> StringReader<'a> { } 'u' | 'i' => { self.scan_int_suffix(); - return token::Literal(token::Integer(self.name_from(start_bpos))); + return token::Integer(self.name_from(start_bpos)); }, 'f' => { let last_pos = self.last_pos; self.scan_float_suffix(); self.check_float_base(start_bpos, last_pos, base); - return token::Literal(token::Float(self.name_from(start_bpos))); + return token::Float(self.name_from(start_bpos)); } _ => { // just a 0 - return token::Literal(token::Integer(self.name_from(start_bpos))); + return token::Integer(self.name_from(start_bpos)); } } } else if c.is_digit_radix(10) { @@ -678,7 +697,7 @@ impl<'a> StringReader<'a> { self.err_span_(start_bpos, self.last_pos, "no valid digits found for number"); // eat any suffix self.scan_int_suffix(); - return token::Literal(token::Integer(token::intern("0"))); + return token::Integer(token::intern("0")); } // might be a float, but don't be greedy if this is actually an @@ -696,13 +715,13 @@ impl<'a> StringReader<'a> { } let last_pos = self.last_pos; self.check_float_base(start_bpos, last_pos, base); - return token::Literal(token::Float(self.name_from(start_bpos))); + return token::Float(self.name_from(start_bpos)); } else if self.curr_is('f') { // or it might be an integer literal suffixed as a float self.scan_float_suffix(); let last_pos = self.last_pos; self.check_float_base(start_bpos, last_pos, base); - return token::Literal(token::Float(self.name_from(start_bpos))); + return token::Float(self.name_from(start_bpos)); } else { // it might be a float if it has an exponent if self.curr_is('e') || self.curr_is('E') { @@ -710,11 +729,11 @@ impl<'a> StringReader<'a> { self.scan_float_suffix(); let last_pos = self.last_pos; self.check_float_base(start_bpos, last_pos, base); - return token::Literal(token::Float(self.name_from(start_bpos))); + return token::Float(self.name_from(start_bpos)); } // but we certainly have an integer! self.scan_int_suffix(); - return token::Literal(token::Integer(self.name_from(start_bpos))); + return token::Integer(self.name_from(start_bpos)); } } @@ -967,7 +986,9 @@ impl<'a> StringReader<'a> { } if is_dec_digit(c) { - return self.scan_number(c.unwrap()); + let num = self.scan_number(c.unwrap()); + let suffix = self.scan_optional_raw_name(); + return token::Literal(num, suffix) } if self.read_embedded_ident { @@ -1126,17 +1147,19 @@ impl<'a> StringReader<'a> { } let id = if valid { self.name_from(start) } else { token::intern("0") }; self.bump(); // advance curr past token - return token::Literal(token::Char(id)); + let suffix = self.scan_optional_raw_name(); + return token::Literal(token::Char(id), suffix); } 'b' => { self.bump(); - return match self.curr { + let lit = match self.curr { Some('\'') => self.scan_byte(), Some('"') => self.scan_byte_string(), Some('r') => self.scan_raw_byte_string(), _ => unreachable!() // Should have been a token::Ident above. }; - + let suffix = self.scan_optional_raw_name(); + return token::Literal(lit, suffix); } '"' => { let start_bpos = self.last_pos; @@ -1157,7 +1180,8 @@ impl<'a> StringReader<'a> { let id = if valid { self.name_from(start_bpos + BytePos(1)) } else { token::intern("??") }; self.bump(); - return token::Literal(token::Str_(id)); + let suffix = self.scan_optional_raw_name(); + return token::Literal(token::Str_(id), suffix); } 'r' => { let start_bpos = self.last_pos; @@ -1224,7 +1248,8 @@ impl<'a> StringReader<'a> { } else { token::intern("??") }; - return token::Literal(token::StrRaw(id, hash_count)); + let suffix = self.scan_optional_raw_name(); + return token::Literal(token::StrRaw(id, hash_count), suffix); } '-' => { if self.nextch_is('>') { @@ -1293,7 +1318,7 @@ impl<'a> StringReader<'a> { || (self.curr_is('#') && self.nextch_is('!') && !self.nextnextch_is('[')) } - fn scan_byte(&mut self) -> token::Token { + fn scan_byte(&mut self) -> token::Lit { self.bump(); let start = self.last_pos; @@ -1314,10 +1339,10 @@ impl<'a> StringReader<'a> { let id = if valid { self.name_from(start) } else { token::intern("??") }; self.bump(); // advance curr past token - return token::Literal(token::Byte(id)); + return token::Byte(id); } - fn scan_byte_string(&mut self) -> token::Token { + fn scan_byte_string(&mut self) -> token::Lit { self.bump(); let start = self.last_pos; let mut valid = true; @@ -1336,10 +1361,10 @@ impl<'a> StringReader<'a> { } let id = if valid { self.name_from(start) } else { token::intern("??") }; self.bump(); - return token::Literal(token::Binary(id)); + return token::Binary(id); } - fn scan_raw_byte_string(&mut self) -> token::Token { + fn scan_raw_byte_string(&mut self) -> token::Lit { let start_bpos = self.last_pos; self.bump(); let mut hash_count = 0u; @@ -1387,9 +1412,9 @@ impl<'a> StringReader<'a> { self.bump(); } self.bump(); - return token::Literal(token::BinaryRaw(self.name_from_to(content_start_bpos, - content_end_bpos), - hash_count)); + return token::BinaryRaw(self.name_from_to(content_start_bpos, + content_end_bpos), + hash_count); } } @@ -1536,17 +1561,17 @@ mod test { #[test] fn character_a() { assert_eq!(setup(&mk_sh(), "'a'".to_string()).next_token().tok, - token::Literal(token::Char(token::intern("a")))); + token::Literal(token::Char(token::intern("a")), None)); } #[test] fn character_space() { assert_eq!(setup(&mk_sh(), "' '".to_string()).next_token().tok, - token::Literal(token::Char(token::intern(" ")))); + token::Literal(token::Char(token::intern(" ")), None)); } #[test] fn character_escaped() { assert_eq!(setup(&mk_sh(), "'\\n'".to_string()).next_token().tok, - token::Literal(token::Char(token::intern("\\n")))); + token::Literal(token::Char(token::intern("\\n")), None)); } #[test] fn lifetime_name() { @@ -1558,7 +1583,38 @@ mod test { assert_eq!(setup(&mk_sh(), "r###\"\"#a\\b\x00c\"\"###".to_string()).next_token() .tok, - token::Literal(token::StrRaw(token::intern("\"#a\\b\x00c\""), 3))); + token::Literal(token::StrRaw(token::intern("\"#a\\b\x00c\""), 3), None)); + } + + #[test] fn literal_suffixes() { + macro_rules! test { + ($input: expr, $tok_type: ident, $tok_contents: expr) => {{ + assert_eq!(setup(&mk_sh(), format!("{}suffix", $input)).next_token().tok, + token::Literal(token::$tok_type(token::intern($tok_contents)), + Some(token::intern("suffix")))); + // with a whitespace separator: + assert_eq!(setup(&mk_sh(), format!("{} suffix", $input)).next_token().tok, + token::Literal(token::$tok_type(token::intern($tok_contents)), + None)); + }} + } + + test!("'a'", Char, "a"); + test!("b'a'", Byte, "a"); + test!("\"a\"", Str_, "a"); + test!("b\"a\"", Binary, "a"); + test!("1234", Integer, "1234"); + test!("0b101", Integer, "0b101"); + test!("0xABC", Integer, "0xABC"); + test!("1.0", Float, "1.0"); + test!("1.0e10", Float, "1.0e10"); + + assert_eq!(setup(&mk_sh(), "r###\"raw\"###suffix".to_string()).next_token().tok, + token::Literal(token::StrRaw(token::intern("raw"), 3), + Some(token::intern("suffix")))); + assert_eq!(setup(&mk_sh(), "br###\"raw\"###suffix".to_string()).next_token().tok, + token::Literal(token::BinaryRaw(token::intern("raw"), 3), + Some(token::intern("suffix")))); } #[test] fn line_doc_comments() { @@ -1574,7 +1630,7 @@ mod test { token::Comment => { }, _ => panic!("expected a comment!") } - assert_eq!(lexer.next_token().tok, token::Literal(token::Char(token::intern("a")))); + assert_eq!(lexer.next_token().tok, token::Literal(token::Char(token::intern("a")), None)); } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 4edcb182e53..b9e1fe07e71 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -646,6 +646,20 @@ impl<'a> Parser<'a> { } } + pub fn expect_no_suffix(&mut self, sp: Span, kind: &str, suffix: Option) { + match suffix { + None => {/* everything ok */} + Some(suf) => { + let text = suf.as_str(); + if text.is_empty() { + self.span_bug(sp, "found empty non-None literal suffix") + } + self.span_err(sp, &*format!("a {} with a suffix is illegal", kind)); + } + } + } + + /// Attempt to consume a `<`. If `<<` is seen, replace it with a single /// `<` and continue. If a `<` is not seen, return false. /// @@ -968,6 +982,9 @@ impl<'a> Parser<'a> { pub fn span_err(&mut self, sp: Span, m: &str) { self.sess.span_diagnostic.span_err(sp, m) } + pub fn span_bug(&mut self, sp: Span, m: &str) -> ! { + self.sess.span_diagnostic.span_bug(sp, m) + } pub fn abort_if_errors(&mut self) { self.sess.span_diagnostic.handler().abort_if_errors(); } @@ -1640,24 +1657,40 @@ impl<'a> Parser<'a> { /// Matches token_lit = LIT_INTEGER | ... pub fn lit_from_token(&mut self, tok: &token::Token) -> Lit_ { match *tok { - token::Literal(token::Byte(i)) => LitByte(parse::byte_lit(i.as_str()).val0()), - token::Literal(token::Char(i)) => LitChar(parse::char_lit(i.as_str()).val0()), - token::Literal(token::Integer(s)) => parse::integer_lit(s.as_str(), - &self.sess.span_diagnostic, - self.last_span), - token::Literal(token::Float(s)) => parse::float_lit(s.as_str()), - token::Literal(token::Str_(s)) => { - LitStr(token::intern_and_get_ident(parse::str_lit(s.as_str()).as_slice()), - ast::CookedStr) - } - token::Literal(token::StrRaw(s, n)) => { - LitStr(token::intern_and_get_ident(parse::raw_str_lit(s.as_str()).as_slice()), - ast::RawStr(n)) + token::Literal(lit, suf) => { + let (suffix_illegal, out) = match lit { + token::Byte(i) => (true, LitByte(parse::byte_lit(i.as_str()).val0())), + token::Char(i) => (true, LitChar(parse::char_lit(i.as_str()).val0())), + token::Integer(s) => (false, parse::integer_lit(s.as_str(), + &self.sess.span_diagnostic, + self.last_span)), + token::Float(s) => (false, parse::float_lit(s.as_str())), + token::Str_(s) => { + (true, + LitStr(token::intern_and_get_ident(parse::str_lit(s.as_str()).as_slice()), + ast::CookedStr)) + } + token::StrRaw(s, n) => { + (true, + LitStr( + token::intern_and_get_ident( + parse::raw_str_lit(s.as_str()).as_slice()), + ast::RawStr(n))) + } + token::Binary(i) => + (true, LitBinary(parse::binary_lit(i.as_str()))), + token::BinaryRaw(i, _) => + (true, + LitBinary(Rc::new(i.as_str().as_bytes().iter().map(|&x| x).collect()))), + }; + + if suffix_illegal { + let sp = self.last_span; + self.expect_no_suffix(sp, &*format!("{} literal", lit.short_name()), suf) + } + + out } - token::Literal(token::Binary(i)) => - LitBinary(parse::binary_lit(i.as_str())), - token::Literal(token::BinaryRaw(i, _)) => - LitBinary(Rc::new(i.as_str().as_bytes().iter().map(|&x| x).collect())), _ => { self.unexpected_last(tok); } } } @@ -2424,7 +2457,10 @@ impl<'a> Parser<'a> { } } } - token::Literal(token::Integer(n)) => { + token::Literal(token::Integer(n), suf) => { + let sp = self.span; + self.expect_no_suffix(sp, "tuple index", suf); + let index = n.as_str(); let dot = self.last_span.hi; hi = self.span.hi; @@ -2449,7 +2485,7 @@ impl<'a> Parser<'a> { } } } - token::Literal(token::Float(n)) => { + token::Literal(token::Float(n), _suf) => { self.bump(); let last_span = self.last_span; let fstr = n.as_str(); @@ -5085,12 +5121,17 @@ impl<'a> Parser<'a> { self.expect(&token::Semi); (path, the_ident) }, - token::Literal(token::Str_(..)) | token::Literal(token::StrRaw(..)) => { - let path = self.parse_str(); + token::Literal(token::Str_(..), suf) | token::Literal(token::StrRaw(..), suf) => { + let sp = self.span; + self.expect_no_suffix(sp, "extern crate name", suf); + // forgo the internal suffix check of `parse_str` to + // avoid repeats (this unwrap will always succeed due + // to the restriction of the `match`) + let (s, style, _) = self.parse_optional_str().unwrap(); self.expect_keyword(keywords::As); let the_ident = self.parse_ident(); self.expect(&token::Semi); - (Some(path), the_ident) + (Some((s, style)), the_ident) }, _ => { let span = self.span; @@ -5267,7 +5308,9 @@ impl<'a> Parser<'a> { /// the `extern` keyword, if one is found. fn parse_opt_abi(&mut self) -> Option { match self.token { - token::Literal(token::Str_(s)) | token::Literal(token::StrRaw(s, _)) => { + token::Literal(token::Str_(s), suf) | token::Literal(token::StrRaw(s, _), suf) => { + let sp = self.span; + self.expect_no_suffix(sp, "ABI spec", suf); self.bump(); let the_string = s.as_str(); match abi::lookup(the_string) { @@ -5902,21 +5945,27 @@ impl<'a> Parser<'a> { } pub fn parse_optional_str(&mut self) - -> Option<(InternedString, ast::StrStyle)> { - let (s, style) = match self.token { - token::Literal(token::Str_(s)) => (self.id_to_interned_str(s.ident()), ast::CookedStr), - token::Literal(token::StrRaw(s, n)) => { - (self.id_to_interned_str(s.ident()), ast::RawStr(n)) + -> Option<(InternedString, ast::StrStyle, Option)> { + let ret = match self.token { + token::Literal(token::Str_(s), suf) => { + (self.id_to_interned_str(s.ident()), ast::CookedStr, suf) + } + token::Literal(token::StrRaw(s, n), suf) => { + (self.id_to_interned_str(s.ident()), ast::RawStr(n), suf) } _ => return None }; self.bump(); - Some((s, style)) + Some(ret) } pub fn parse_str(&mut self) -> (InternedString, StrStyle) { match self.parse_optional_str() { - Some(s) => { s } + Some((s, style, suf)) => { + let sp = self.last_span; + self.expect_no_suffix(sp, "str literal", suf); + (s, style) + } _ => self.fatal("expected string literal") } } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index bfa6ca798b2..4272b57a4dc 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -72,6 +72,19 @@ pub enum Lit { BinaryRaw(ast::Name, uint), /* raw binary str delimited by n hash symbols */ } +impl Lit { + pub fn short_name(&self) -> &'static str { + match *self { + Byte(_) => "byte", + Char(_) => "char", + Integer(_) => "integer", + Float(_) => "float", + Str_(_) | StrRaw(..) => "str", + Binary(_) | BinaryRaw(..) => "binary str" + } + } +} + #[allow(non_camel_case_types)] #[deriving(Clone, Encodable, Decodable, PartialEq, Eq, Hash, Show)] pub enum Token { @@ -111,7 +124,7 @@ pub enum Token { CloseDelim(DelimToken), /* Literals */ - Literal(Lit), + Literal(Lit, Option), /* Name components */ Ident(ast::Ident, IdentStyle), @@ -151,7 +164,7 @@ impl Token { Ident(_, _) => true, Underscore => true, Tilde => true, - Literal(_) => true, + Literal(_, _) => true, Pound => true, At => true, Not => true, @@ -172,7 +185,7 @@ impl Token { /// Returns `true` if the token is any literal pub fn is_lit(&self) -> bool { match *self { - Literal(_) => true, + Literal(_, _) => true, _ => false, } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 7997c1ba4ef..642ffa3745d 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -236,18 +236,28 @@ pub fn token_to_string(tok: &Token) -> String { token::Question => "?".into_string(), /* Literals */ - token::Literal(token::Byte(b)) => format!("b'{}'", b.as_str()), - token::Literal(token::Char(c)) => format!("'{}'", c.as_str()), - token::Literal(token::Float(c)) => c.as_str().into_string(), - token::Literal(token::Integer(c)) => c.as_str().into_string(), - token::Literal(token::Str_(s)) => format!("\"{}\"", s.as_str()), - token::Literal(token::StrRaw(s, n)) => format!("r{delim}\"{string}\"{delim}", - delim="#".repeat(n), - string=s.as_str()), - token::Literal(token::Binary(v)) => format!("b\"{}\"", v.as_str()), - token::Literal(token::BinaryRaw(s, n)) => format!("br{delim}\"{string}\"{delim}", - delim="#".repeat(n), - string=s.as_str()), + token::Literal(lit, suf) => { + let mut out = match lit { + token::Byte(b) => format!("b'{}'", b.as_str()), + token::Char(c) => format!("'{}'", c.as_str()), + token::Float(c) => c.as_str().into_string(), + token::Integer(c) => c.as_str().into_string(), + token::Str_(s) => format!("\"{}\"", s.as_str()), + token::StrRaw(s, n) => format!("r{delim}\"{string}\"{delim}", + delim="#".repeat(n), + string=s.as_str()), + token::Binary(v) => format!("b\"{}\"", v.as_str()), + token::BinaryRaw(s, n) => format!("br{delim}\"{string}\"{delim}", + delim="#".repeat(n), + string=s.as_str()), + }; + + if let Some(s) = suf { + out.push_str(s.as_str()) + } + + out + } /* Name components */ token::Ident(s, _) => token::get_ident(s).get().into_string(), diff --git a/src/test/compile-fail/bad-lit-suffixes.rs b/src/test/compile-fail/bad-lit-suffixes.rs new file mode 100644 index 00000000000..e48bb807488 --- /dev/null +++ b/src/test/compile-fail/bad-lit-suffixes.rs @@ -0,0 +1,36 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +extern crate + "foo"suffix //~ ERROR extern crate name with a suffix is illegal + as foo; + +extern + "C"suffix //~ ERROR ABI spec with a suffix is illegal + fn foo() {} + +extern + "C"suffix //~ ERROR ABI spec with a suffix is illegal +{} + +fn main() { + ""suffix; //~ ERROR str literal with a suffix is illegal + b""suffix; //~ ERROR binary str literal with a suffix is illegal + r#""#suffix; //~ ERROR str literal with a suffix is illegal + br#""#suffix; //~ ERROR binary str literal with a suffix is illegal + 'a'suffix; //~ ERROR char literal with a suffix is illegal + b'a'suffix; //~ ERROR byte literal with a suffix is illegal + + 1234suffix; + 0b101suffix; + 1.0suffix; + 1.0e10suffix; +} -- cgit 1.4.1-3-g733a5 From 606a309d4aeb09ba88a0962c633a5b3fd4b300f6 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 19 Nov 2014 20:22:54 +1100 Subject: Switch numeric suffix parsing to use the new system. This moves errors and all handling of numeric suffixes into the parser rather than the lexer. --- src/libsyntax/parse/lexer/mod.rs | 74 +-------------- src/libsyntax/parse/mod.rs | 145 ++++++++++++++++-------------- src/libsyntax/parse/parser.rs | 25 ++++-- src/test/compile-fail/bad-lit-suffixes.rs | 13 ++- 4 files changed, 108 insertions(+), 149 deletions(-) (limited to 'src/libsyntax/parse/parser.rs') diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 55c4335941d..fbca4868255 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -672,16 +672,6 @@ impl<'a> StringReader<'a> { '0'...'9' | '_' | '.' => { num_digits = self.scan_digits(10) + 1; } - 'u' | 'i' => { - self.scan_int_suffix(); - return token::Integer(self.name_from(start_bpos)); - }, - 'f' => { - let last_pos = self.last_pos; - self.scan_float_suffix(); - self.check_float_base(start_bpos, last_pos, base); - return token::Float(self.name_from(start_bpos)); - } _ => { // just a 0 return token::Integer(self.name_from(start_bpos)); @@ -695,8 +685,6 @@ impl<'a> StringReader<'a> { if num_digits == 0 { self.err_span_(start_bpos, self.last_pos, "no valid digits found for number"); - // eat any suffix - self.scan_int_suffix(); return token::Integer(token::intern("0")); } @@ -711,28 +699,19 @@ impl<'a> StringReader<'a> { if self.curr.unwrap_or('\0').is_digit_radix(10) { self.scan_digits(10); self.scan_float_exponent(); - self.scan_float_suffix(); } let last_pos = self.last_pos; self.check_float_base(start_bpos, last_pos, base); return token::Float(self.name_from(start_bpos)); - } else if self.curr_is('f') { - // or it might be an integer literal suffixed as a float - self.scan_float_suffix(); - let last_pos = self.last_pos; - self.check_float_base(start_bpos, last_pos, base); - return token::Float(self.name_from(start_bpos)); } else { // it might be a float if it has an exponent if self.curr_is('e') || self.curr_is('E') { self.scan_float_exponent(); - self.scan_float_suffix(); let last_pos = self.last_pos; self.check_float_base(start_bpos, last_pos, base); return token::Float(self.name_from(start_bpos)); } // but we certainly have an integer! - self.scan_int_suffix(); return token::Integer(self.name_from(start_bpos)); } } @@ -869,55 +848,6 @@ impl<'a> StringReader<'a> { true } - /// Scan over an int literal suffix. - fn scan_int_suffix(&mut self) { - match self.curr { - Some('i') | Some('u') => { - self.bump(); - - if self.curr_is('8') { - self.bump(); - } else if self.curr_is('1') { - if !self.nextch_is('6') { - self.err_span_(self.last_pos, self.pos, - "illegal int suffix"); - } else { - self.bump(); self.bump(); - } - } else if self.curr_is('3') { - if !self.nextch_is('2') { - self.err_span_(self.last_pos, self.pos, - "illegal int suffix"); - } else { - self.bump(); self.bump(); - } - } else if self.curr_is('6') { - if !self.nextch_is('4') { - self.err_span_(self.last_pos, self.pos, - "illegal int suffix"); - } else { - self.bump(); self.bump(); - } - } - }, - _ => { } - } - } - - /// Scan over a float literal suffix - fn scan_float_suffix(&mut self) { - if self.curr_is('f') { - if (self.nextch_is('3') && self.nextnextch_is('2')) - || (self.nextch_is('6') && self.nextnextch_is('4')) { - self.bump(); - self.bump(); - self.bump(); - } else { - self.err_span_(self.last_pos, self.pos, "illegal float suffix"); - } - } - } - /// Scan over a float exponent. fn scan_float_exponent(&mut self) { if self.curr_is('e') || self.curr_is('E') { @@ -988,6 +918,7 @@ impl<'a> StringReader<'a> { if is_dec_digit(c) { let num = self.scan_number(c.unwrap()); let suffix = self.scan_optional_raw_name(); + debug!("next_token_inner: scanned number {}, {}", num, suffix); return token::Literal(num, suffix) } @@ -1609,6 +1540,9 @@ mod test { test!("1.0", Float, "1.0"); test!("1.0e10", Float, "1.0e10"); + assert_eq!(setup(&mk_sh(), "2u".to_string()).next_token().tok, + token::Literal(token::Integer(token::intern("2")), + Some(token::intern("u")))); assert_eq!(setup(&mk_sh(), "r###\"raw\"###suffix".to_string()).next_token().tok, token::Literal(token::StrRaw(token::intern("raw"), 3), Some(token::intern("suffix")))); diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 2810db4eadd..d111108269d 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -511,28 +511,41 @@ pub fn raw_str_lit(lit: &str) -> String { res } -pub fn float_lit(s: &str) -> ast::Lit_ { - debug!("float_lit: {}", s); - // FIXME #2252: bounds checking float literals is defered until trans - let s2 = s.chars().filter(|&c| c != '_').collect::(); - let s = s2.as_slice(); - - let mut ty = None; - - if s.ends_with("f32") { - ty = Some(ast::TyF32); - } else if s.ends_with("f64") { - ty = Some(ast::TyF64); - } +// check if `s` looks like i32 or u1234 etc. +fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { + s.len() > 1 && + first_chars.contains(&s.char_at(0)) && + s.slice_from(1).chars().all(|c| '0' <= c && c <= '9') +} +fn filtered_float_lit(data: token::InternedString, suffix: Option<&str>, + sd: &SpanHandler, sp: Span) -> ast::Lit_ { + debug!("filtered_float_lit: {}, {}", data, suffix); + match suffix { + Some("f32") => ast::LitFloat(data, ast::TyF32), + Some("f64") => ast::LitFloat(data, ast::TyF64), + Some(suf) => { + if suf.len() >= 2 && looks_like_width_suffix(&['f'], suf) { + // if it looks like a width, lets try to be helpful. + sd.span_err(sp, &*format!("illegal width `{}` for float literal, \ + valid widths are 32 and 64", suf.slice_from(1))); + } else { + sd.span_err(sp, &*format!("illegal suffix `{}` for float literal, \ + valid suffixes are `f32` and `f64`", suf)); + } - match ty { - Some(t) => { - ast::LitFloat(token::intern_and_get_ident(s.slice_to(s.len() - t.suffix_len())), t) - }, - None => ast::LitFloatUnsuffixed(token::intern_and_get_ident(s)) + ast::LitFloatUnsuffixed(data) + } + None => ast::LitFloatUnsuffixed(data) } } +pub fn float_lit(s: &str, suffix: Option<&str>, sd: &SpanHandler, sp: Span) -> ast::Lit_ { + debug!("float_lit: {}, {}", s, suffix); + // FIXME #2252: bounds checking float literals is defered until trans + let s = s.chars().filter(|&c| c != '_').collect::(); + let data = token::intern_and_get_ident(&*s); + filtered_float_lit(data, suffix, sd, sp) +} /// Parse a string representing a byte literal into its final form. Similar to `char_lit` pub fn byte_lit(lit: &str) -> (u8, uint) { @@ -626,24 +639,19 @@ pub fn binary_lit(lit: &str) -> Rc> { Rc::new(res) } -pub fn integer_lit(s: &str, sd: &SpanHandler, sp: Span) -> ast::Lit_ { +pub fn integer_lit(s: &str, suffix: Option<&str>, sd: &SpanHandler, sp: Span) -> ast::Lit_ { // s can only be ascii, byte indexing is fine let s2 = s.chars().filter(|&c| c != '_').collect::(); let mut s = s2.as_slice(); - debug!("parse_integer_lit: {}", s); - - if s.len() == 1 { - let n = (s.char_at(0)).to_digit(10).unwrap(); - return ast::LitInt(n as u64, ast::UnsuffixedIntLit(ast::Sign::new(n))); - } + debug!("integer_lit: {}, {}", s, suffix); let mut base = 10; let orig = s; let mut ty = ast::UnsuffixedIntLit(ast::Plus); - if s.char_at(0) == '0' { + if s.char_at(0) == '0' && s.len() > 1 { match s.char_at(1) { 'x' => base = 16, 'o' => base = 8, @@ -652,57 +660,56 @@ pub fn integer_lit(s: &str, sd: &SpanHandler, sp: Span) -> ast::Lit_ { } } + // 1f64 and 2f32 etc. are valid float literals. + match suffix { + Some(suf) if looks_like_width_suffix(&['f'], suf) => { + match base { + 16u => sd.span_err(sp, "hexadecimal float literal is not supported"), + 8u => sd.span_err(sp, "octal float literal is not supported"), + 2u => sd.span_err(sp, "binary float literal is not supported"), + _ => () + } + let ident = token::intern_and_get_ident(&*s); + return filtered_float_lit(ident, suffix, sd, sp) + } + _ => {} + } + if base != 10 { s = s.slice_from(2); } - let last = s.len() - 1; - match s.char_at(last) { - 'i' => ty = ast::SignedIntLit(ast::TyI, ast::Plus), - 'u' => ty = ast::UnsignedIntLit(ast::TyU), - '8' => { - if s.len() > 2 { - match s.char_at(last - 1) { - 'i' => ty = ast::SignedIntLit(ast::TyI8, ast::Plus), - 'u' => ty = ast::UnsignedIntLit(ast::TyU8), - _ => { } - } - } - }, - '6' => { - if s.len() > 3 && s.char_at(last - 1) == '1' { - match s.char_at(last - 2) { - 'i' => ty = ast::SignedIntLit(ast::TyI16, ast::Plus), - 'u' => ty = ast::UnsignedIntLit(ast::TyU16), - _ => { } - } - } - }, - '2' => { - if s.len() > 3 && s.char_at(last - 1) == '3' { - match s.char_at(last - 2) { - 'i' => ty = ast::SignedIntLit(ast::TyI32, ast::Plus), - 'u' => ty = ast::UnsignedIntLit(ast::TyU32), - _ => { } - } - } - }, - '4' => { - if s.len() > 3 && s.char_at(last - 1) == '6' { - match s.char_at(last - 2) { - 'i' => ty = ast::SignedIntLit(ast::TyI64, ast::Plus), - 'u' => ty = ast::UnsignedIntLit(ast::TyU64), - _ => { } + if let Some(suf) = suffix { + if suf.is_empty() { sd.span_bug(sp, "found empty literal suffix in Some")} + ty = match suf { + "i" => ast::SignedIntLit(ast::TyI, ast::Plus), + "i8" => ast::SignedIntLit(ast::TyI8, ast::Plus), + "i16" => ast::SignedIntLit(ast::TyI16, ast::Plus), + "i32" => ast::SignedIntLit(ast::TyI32, ast::Plus), + "i64" => ast::SignedIntLit(ast::TyI64, ast::Plus), + "u" => ast::UnsignedIntLit(ast::TyU), + "u8" => ast::UnsignedIntLit(ast::TyU8), + "u16" => ast::UnsignedIntLit(ast::TyU16), + "u32" => ast::UnsignedIntLit(ast::TyU32), + "u64" => ast::UnsignedIntLit(ast::TyU64), + _ => { + // i and u look like widths, so lets + // give an error message along those lines + if looks_like_width_suffix(&['i', 'u'], suf) { + sd.span_err(sp, &*format!("illegal width `{}` for integer literal; \ + valid widths are 8, 16, 32 and 64", + suf.slice_from(1))); + } else { + sd.span_err(sp, &*format!("illegal suffix `{}` for numeric literal", suf)); } + + ty } - }, - _ => { } + } } - debug!("The suffix is {}, base {}, the new string is {}, the original \ - string was {}", ty, base, s, orig); - - s = s.slice_to(s.len() - ty.suffix_len()); + debug!("integer_lit: the type is {}, base {}, the new string is {}, the original \ + string was {}, the original suffix was {}", ty, base, s, orig, suffix); let res: u64 = match ::std::num::from_str_radix(s, base) { Some(r) => r, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b9e1fe07e71..85364b8f65f 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -652,9 +652,9 @@ impl<'a> Parser<'a> { Some(suf) => { let text = suf.as_str(); if text.is_empty() { - self.span_bug(sp, "found empty non-None literal suffix") + self.span_bug(sp, "found empty literal suffix in Some") } - self.span_err(sp, &*format!("a {} with a suffix is illegal", kind)); + self.span_err(sp, &*format!("{} with a suffix is illegal", kind)); } } } @@ -1661,10 +1661,23 @@ impl<'a> Parser<'a> { let (suffix_illegal, out) = match lit { token::Byte(i) => (true, LitByte(parse::byte_lit(i.as_str()).val0())), token::Char(i) => (true, LitChar(parse::char_lit(i.as_str()).val0())), - token::Integer(s) => (false, parse::integer_lit(s.as_str(), - &self.sess.span_diagnostic, - self.last_span)), - token::Float(s) => (false, parse::float_lit(s.as_str())), + + // there are some valid suffixes for integer and + // float literals, so all the handling is done + // internally. + token::Integer(s) => { + (false, parse::integer_lit(s.as_str(), + suf.as_ref().map(|s| s.as_str()), + &self.sess.span_diagnostic, + self.last_span)) + } + token::Float(s) => { + (false, parse::float_lit(s.as_str(), + suf.as_ref().map(|s| s.as_str()), + &self.sess.span_diagnostic, + self.last_span)) + } + token::Str_(s) => { (true, LitStr(token::intern_and_get_ident(parse::str_lit(s.as_str()).as_slice()), diff --git a/src/test/compile-fail/bad-lit-suffixes.rs b/src/test/compile-fail/bad-lit-suffixes.rs index e48bb807488..e142365a8ca 100644 --- a/src/test/compile-fail/bad-lit-suffixes.rs +++ b/src/test/compile-fail/bad-lit-suffixes.rs @@ -29,8 +29,13 @@ fn main() { 'a'suffix; //~ ERROR char literal with a suffix is illegal b'a'suffix; //~ ERROR byte literal with a suffix is illegal - 1234suffix; - 0b101suffix; - 1.0suffix; - 1.0e10suffix; + 1234u1024; //~ ERROR illegal width `1024` for integer literal + 1234i1024; //~ ERROR illegal width `1024` for integer literal + 1234f1024; //~ ERROR illegal width `1024` for float literal + 1234.5f1024; //~ ERROR illegal width `1024` for float literal + + 1234suffix; //~ ERROR illegal suffix `suffix` for numeric literal + 0b101suffix; //~ ERROR illegal suffix `suffix` for numeric literal + 1.0suffix; //~ ERROR illegal suffix `suffix` for numeric literal + 1.0e10suffix; //~ ERROR illegal suffix `suffix` for numeric literal } -- cgit 1.4.1-3-g733a5