From 4d77945aaa44c6029cc514d8e69c9cfb3416c6a2 Mon Sep 17 00:00:00 2001 From: Dmitry Promsky Date: Mon, 24 Mar 2014 22:32:07 +0400 Subject: [PATCH] rustc: fixed regionck for owned trait casts. Regionck didn't do any checks for casts/coercions to an owned trait, which resulted in lifetimes of the source pointer to be ignored in the result of such cast. This fix constraints all regions of the source type of the cast/coercion to be superregions of each region of the target type (if target trait definition has some lifetime params), or of 'static lifetime (if there're no lifetime params in target trait's definition). Closes #5723 Closes #9745 Closes #11971 --- src/librustc/middle/typeck/check/regionck.rs | 85 +++++++++++++------ src/libsyntax/ext/base.rs | 12 +-- src/libsyntax/ext/tt/macro_rules.rs | 4 +- src/libsyntax/parse/lexer.rs | 6 +- src/libsyntax/parse/parser.rs | 4 +- .../compile-fail/owned-ptr-static-bound.rs | 16 ++-- .../regionck-uniq-trait-cast-fail.rs | 22 +++++ .../regionck-uniq-trait-coerce-fail.rs | 27 ++++++ .../run-pass/regionck-uniq-trait-cast-pass.rs | 22 +++++ .../regionck-uniq-trait-coerce-pass.rs | 28 ++++++ 10 files changed, 181 insertions(+), 45 deletions(-) create mode 100644 src/test/compile-fail/regionck-uniq-trait-cast-fail.rs create mode 100644 src/test/compile-fail/regionck-uniq-trait-coerce-fail.rs create mode 100644 src/test/run-pass/regionck-uniq-trait-cast-pass.rs create mode 100644 src/test/run-pass/regionck-uniq-trait-coerce-pass.rs diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index c8613fd70652e..7de8921ae2265 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -419,24 +419,9 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { infer::AutoBorrow(expr.span)); } } - ty::AutoObject(ast::BorrowedSigil, Some(trait_region), _, _, _, _) => { - // Determine if we are casting `expr` to an trait - // instance. If so, we have to be sure that the type of - // the source obeys the trait's region bound. - // - // Note: there is a subtle point here concerning type - // parameters. It is possible that the type of `source` - // contains type parameters, which in turn may contain - // regions that are not visible to us (only the caller - // knows about them). The kind checker is ultimately - // responsible for guaranteeing region safety in that - // particular case. There is an extensive comment on the - // function check_cast_for_escaping_regions() in kind.rs - // explaining how it goes about doing that. - - let source_ty = rcx.fcx.expr_ty(expr); - constrain_regions_in_type(rcx, trait_region, - infer::RelateObjectBound(expr.span), source_ty); + ty::AutoObject(_, maybe_region, _, _, _, ref substs) => { + let source_ty = rcx.resolve_node_type(expr.id); + constrain_trait_cast(rcx, expr, maybe_region, substs, source_ty); } _ => {} } @@ -540,13 +525,15 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { // explaining how it goes about doing that. let target_ty = rcx.resolve_node_type(expr.id); match ty::get(target_ty).sty { - ty::ty_trait(~ty::TyTrait { store: ty::RegionTraitStore(trait_region), .. }) => { + ty::ty_trait(~ty::TyTrait { store: trait_store, substs: ref substs, .. }) => { let source_ty = rcx.resolve_expr_type_adjusted(source); - constrain_regions_in_type( - rcx, - trait_region, - infer::RelateObjectBound(expr.span), - source_ty); + + let trait_region = match trait_store { + ty::RegionTraitStore(r) => Some(r), + ty::UniqTraitStore => None + }; + + constrain_trait_cast(rcx, expr, trait_region, substs, source_ty); } _ => () } @@ -933,6 +920,56 @@ fn constrain_index(rcx: &mut Rcx, } } +fn constrain_trait_cast(rcx: &mut Rcx, + expr: &ast::Expr, + trait_region: Option, + trait_substs: &ty::substs, + source_ty: ty::t) { + // If we are casting `source` to a trait + // instance, we have to be sure that the type of + // the source obeys the trait's region bound. + // + // Note: there is a subtle point here concerning type + // parameters. It is possible that the type of `source` + // contains type parameters, which in turn may contain + // regions that are not visible to us (only the caller + // knows about them). The kind checker is ultimately + // responsible for guaranteeing region safety in that + // particular case. There is an extensive comment on the + // function check_cast_for_escaping_regions() in kind.rs + // explaining how it goes about doing that. + + debug!("constrain_trait_cast(expr={:?}, trait_region={:?}, trait_substs={:?}, source_ty={:?}", + expr, trait_region, trait_substs, ty::get(source_ty)); + + let mut regions = Vec::new(); + + match trait_substs.regions { + ty::NonerasedRegions(ref regs) => { + for r in regs.iter() { + regions.push(*r); + } + } + ty::ErasedRegions => () + } + + match trait_region { + Some(region) => { + regions.push(region); + } + None => { + // casting to owned trait with no lifetime params in trait def + if regions.is_empty() { + regions.push(ty::ReStatic); + } + } + } + + for r in regions.iter() { + constrain_regions_in_type(rcx, *r, infer::RelateObjectBound(expr.span), source_ty); + } +} + fn constrain_regions_in_type_of_node( rcx: &mut Rcx, id: ast::NodeId, diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 7ff7792313251..2818192be0ed3 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -98,20 +98,20 @@ pub type IdentMacroExpanderFn = pub type MacroCrateRegistrationFun = fn(|ast::Name, SyntaxExtension|); -pub trait AnyMacro { +pub trait AnyMacro<'a> { fn make_expr(&self) -> @ast::Expr; fn make_items(&self) -> SmallVector<@ast::Item>; fn make_stmt(&self) -> @ast::Stmt; } -pub enum MacResult { +pub enum MacResult<'a> { MRExpr(@ast::Expr), MRItem(@ast::Item), - MRAny(~AnyMacro:), + MRAny(~AnyMacro<'a>:), MRDef(MacroDef), } -impl MacResult { +impl<'a> MacResult<'a> { /// Create an empty expression MacResult; useful for satisfying /// type signatures after emitting a non-fatal error (which stop /// compilation well before the validity (or otherwise)) of the @@ -123,7 +123,7 @@ impl MacResult { span: sp, } } - pub fn dummy_expr(sp: codemap::Span) -> MacResult { + pub fn dummy_expr(sp: codemap::Span) -> MacResult<'a> { MRExpr(MacResult::raw_dummy_expr(sp)) } pub fn dummy_any(sp: codemap::Span) -> MacResult { @@ -133,7 +133,7 @@ impl MacResult { struct DummyMacResult { sp: codemap::Span } -impl AnyMacro for DummyMacResult { +impl<'a> AnyMacro<'a> for DummyMacResult { fn make_expr(&self) -> @ast::Expr { MacResult::raw_dummy_expr(self.sp) } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index d4a883a63ebbf..edfc17a1fc5a3 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -57,7 +57,7 @@ impl<'a> ParserAnyMacro<'a> { } } -impl<'a> AnyMacro for ParserAnyMacro<'a> { +impl<'a> AnyMacro<'a> for ParserAnyMacro<'a> { fn make_expr(&self) -> @ast::Expr { let ret = self.parser.borrow_mut().parse_expr(); self.ensure_complete_parse(true); @@ -106,7 +106,7 @@ impl MacroExpander for MacroRulesMacroExpander { } // Given `lhses` and `rhses`, this is the new macro we create -fn generic_extension(cx: &ExtCtxt, +fn generic_extension<'a>(cx: &'a ExtCtxt, sp: Span, name: Ident, arg: &[ast::TokenTree], diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs index 23d7cc0af97e4..697dff10a1c99 100644 --- a/src/libsyntax/parse/lexer.rs +++ b/src/libsyntax/parse/lexer.rs @@ -24,7 +24,7 @@ use std::str; pub use ext::tt::transcribe::{TtReader, new_tt_reader}; -pub trait Reader { +pub trait Reader<'a> { fn is_eof(&self) -> bool; fn next_token(&mut self) -> TokenAndSpan; fn fatal(&self, ~str) -> !; @@ -89,7 +89,7 @@ pub fn new_low_level_string_reader<'a>(span_diagnostic: &'a SpanHandler, r } -impl<'a> Reader for StringReader<'a> { +impl<'a> Reader<'a> for StringReader<'a> { fn is_eof(&self) -> bool { is_eof(self) } // return the next token. EFFECT: advances the string_reader. fn next_token(&mut self) -> TokenAndSpan { @@ -113,7 +113,7 @@ impl<'a> Reader for StringReader<'a> { } } -impl<'a> Reader for TtReader<'a> { +impl<'a> Reader<'a> for TtReader<'a> { fn is_eof(&self) -> bool { self.cur_tok == token::EOF } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 62ce0f1e11399..106c3639ba22f 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -272,7 +272,7 @@ struct ParsedItemsAndViewItems { /* ident is handled by common.rs */ -pub fn Parser<'a>(sess: &'a ParseSess, cfg: ast::CrateConfig, mut rdr: ~Reader:) +pub fn Parser<'a>(sess: &'a ParseSess, cfg: ast::CrateConfig, mut rdr: ~Reader<'a>:) -> Parser<'a> { let tok0 = rdr.next_token(); let span = tok0.sp; @@ -324,7 +324,7 @@ pub struct Parser<'a> { pub tokens_consumed: uint, pub restriction: restriction, pub quote_depth: uint, // not (yet) related to the quasiquoter - pub reader: ~Reader:, + pub reader: ~Reader<'a>:, pub interner: Rc, /// The set of seen errors about obsolete syntax. Used to suppress /// extra detail when the same error is seen twice diff --git a/src/test/compile-fail/owned-ptr-static-bound.rs b/src/test/compile-fail/owned-ptr-static-bound.rs index 508633d294146..7cc2193bf52a8 100644 --- a/src/test/compile-fail/owned-ptr-static-bound.rs +++ b/src/test/compile-fail/owned-ptr-static-bound.rs @@ -11,19 +11,19 @@ trait A {} struct B<'a, T>(&'a A); -trait X {} -impl<'a, T> X for B<'a, T> {} +trait X<'a> {} +impl<'a, T> X<'a> for B<'a, T> {} -fn f<'a, T, U>(v: ~A) -> ~X: { - ~B(v) as ~X: //~ ERROR value may contain references; add `'static` bound to `T` +fn f<'a, T, U>(v: &'a A) -> ~X<'a>: { + ~B(v) as ~X<'a>: //~ ERROR value may contain references; add `'static` bound to `T` } -fn g<'a, T, U>(v: ~A) -> ~X: { - ~B(v) as ~X: //~ ERROR value may contain references; add `'static` bound to `U` +fn g<'a, T, U>(v: &'a A) -> ~X<'a>: { + ~B(v) as ~X<'a>: //~ ERROR value may contain references; add `'static` bound to `U` } -fn h<'a, T: 'static>(v: ~A) -> ~X: { - ~B(v) as ~X: // ok +fn h<'a, T: 'static>(v: &'a A) -> ~X<'a>: { + ~B(v) as ~X<'a>: // ok } fn main() {} diff --git a/src/test/compile-fail/regionck-uniq-trait-cast-fail.rs b/src/test/compile-fail/regionck-uniq-trait-cast-fail.rs new file mode 100644 index 0000000000000..c60fe98a6b3cf --- /dev/null +++ b/src/test/compile-fail/regionck-uniq-trait-cast-fail.rs @@ -0,0 +1,22 @@ +// 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. + +// Test that lifetimes can't escape through owned trait casts + +trait X {} +impl<'a> X for &'a X {} + +fn foo(x: &X) -> ~X: { + ~x as ~X: + //~^ ERROR lifetime of the source pointer does not outlive lifetime bound of the object type +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/compile-fail/regionck-uniq-trait-coerce-fail.rs b/src/test/compile-fail/regionck-uniq-trait-coerce-fail.rs new file mode 100644 index 0000000000000..43acd475e4602 --- /dev/null +++ b/src/test/compile-fail/regionck-uniq-trait-coerce-fail.rs @@ -0,0 +1,27 @@ +// 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. + +// Test that lifetimes can't escape through owned trait coercions + +trait A {} +impl<'a> A for &'a A {} + +struct B; +impl A for B {} +impl<'a> A for &'a B {} + +fn main() { + let _tmp = { + let bb = B; + let pb = ~&bb; //~ ERROR `bb` does not live long enough + let aa: ~A: = pb; + aa + }; +} diff --git a/src/test/run-pass/regionck-uniq-trait-cast-pass.rs b/src/test/run-pass/regionck-uniq-trait-cast-pass.rs new file mode 100644 index 0000000000000..05856b48bb487 --- /dev/null +++ b/src/test/run-pass/regionck-uniq-trait-cast-pass.rs @@ -0,0 +1,22 @@ +// 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. + +// Test that trait lifetime params aren't constrained to static lifetime +// when casting something to owned trait + +trait X<'a> {} +impl<'a> X<'a> for &'a X<'a> {} + +fn foo<'a>(x: &'a X<'a>) -> ~X<'a>: { + ~x as ~X<'a>: +} + +pub fn main() { +} \ No newline at end of file diff --git a/src/test/run-pass/regionck-uniq-trait-coerce-pass.rs b/src/test/run-pass/regionck-uniq-trait-coerce-pass.rs new file mode 100644 index 0000000000000..cf9ae1d9f6d7b --- /dev/null +++ b/src/test/run-pass/regionck-uniq-trait-coerce-pass.rs @@ -0,0 +1,28 @@ +// 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. + +// Test that trait lifetime params aren't constrained to static lifetime +// when coercing something to owned trait + +trait A<'a> {} +impl<'a> A<'a> for &'a A<'a> {} + +struct B; +impl<'a> A<'a> for B {} +impl<'a> A<'a> for &'a B {} + +pub fn main() { + let bb = B; + let _tmp = { + let pb = ~&bb; + let aa: ~A: = pb; + aa + }; +}