From 1b0f0ad28070c072f68ea0ab10bbae61b52706a8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 17 Mar 2015 10:23:13 -0400 Subject: [PATCH 1/2] Extract out `mts` into `combine` using `tys_with_variance` --- src/librustc/middle/infer/bivariate.rs | 10 --------- src/librustc/middle/infer/combine.rs | 19 +++++++++++++++- src/librustc/middle/infer/equate.rs | 9 -------- src/librustc/middle/infer/glb.rs | 30 -------------------------- src/librustc/middle/infer/lub.rs | 26 ---------------------- src/librustc/middle/infer/sub.rs | 24 --------------------- 6 files changed, 18 insertions(+), 100 deletions(-) diff --git a/src/librustc/middle/infer/bivariate.rs b/src/librustc/middle/infer/bivariate.rs index 93c80fb754f71..1916d39b49828 100644 --- a/src/librustc/middle/infer/bivariate.rs +++ b/src/librustc/middle/infer/bivariate.rs @@ -74,16 +74,6 @@ impl<'f, 'tcx> Combine<'tcx> for Bivariate<'f, 'tcx> { Ok(a) } - fn mts(&self, a: &ty::mt<'tcx>, b: &ty::mt<'tcx>) -> cres<'tcx, ty::mt<'tcx>> { - debug!("mts({} <: {})", - a.repr(self.fields.infcx.tcx), - b.repr(self.fields.infcx.tcx)); - - if a.mutbl != b.mutbl { return Err(ty::terr_mutability); } - let t = try!(self.tys(a.ty, b.ty)); - Ok(ty::mt { mutbl: a.mutbl, ty: t }) - } - fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> { if a != b { Err(ty::terr_unsafety_mismatch(expected_found(self, a, b))) diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs index 438b61f918a1e..9fdfdaccf4eaf 100644 --- a/src/librustc/middle/infer/combine.rs +++ b/src/librustc/middle/infer/combine.rs @@ -74,7 +74,24 @@ pub trait Combine<'tcx> : Sized { fn lub<'a>(&'a self) -> Lub<'a, 'tcx> { Lub(self.fields().clone()) } fn glb<'a>(&'a self) -> Glb<'a, 'tcx> { Glb(self.fields().clone()) } - fn mts(&self, a: &ty::mt<'tcx>, b: &ty::mt<'tcx>) -> cres<'tcx, ty::mt<'tcx>>; + fn mts(&self, a: &ty::mt<'tcx>, b: &ty::mt<'tcx>) -> cres<'tcx, ty::mt<'tcx>> { + debug!("{}.mts({}, {})", + self.tag(), + a.repr(self.tcx()), + b.repr(self.tcx())); + + if a.mutbl != b.mutbl { + Err(ty::terr_mutability) + } else { + let mutbl = a.mutbl; + let variance = match mutbl { + ast::MutImmutable => ty::Covariant, + ast::MutMutable => ty::Invariant, + }; + let ty = try!(self.tys_with_variance(variance, a.ty, b.ty)); + Ok(ty::mt {ty: ty, mutbl: mutbl}) + } + } fn tys_with_variance(&self, variance: ty::Variance, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>>; diff --git a/src/librustc/middle/infer/equate.rs b/src/librustc/middle/infer/equate.rs index 2f9f8532b6c52..e9ffe368f4220 100644 --- a/src/librustc/middle/infer/equate.rs +++ b/src/librustc/middle/infer/equate.rs @@ -54,15 +54,6 @@ impl<'f, 'tcx> Combine<'tcx> for Equate<'f, 'tcx> { Ok(a) } - fn mts(&self, a: &ty::mt<'tcx>, b: &ty::mt<'tcx>) -> cres<'tcx, ty::mt<'tcx>> { - debug!("mts({} <: {})", - a.repr(self.fields.infcx.tcx), - b.repr(self.fields.infcx.tcx)); - - if a.mutbl != b.mutbl { return Err(ty::terr_mutability); } - let t = try!(self.tys(a.ty, b.ty)); - Ok(ty::mt { mutbl: a.mutbl, ty: t }) - } fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> { if a != b { diff --git a/src/librustc/middle/infer/glb.rs b/src/librustc/middle/infer/glb.rs index ba073999ed127..43d64643fe2ee 100644 --- a/src/librustc/middle/infer/glb.rs +++ b/src/librustc/middle/infer/glb.rs @@ -55,36 +55,6 @@ impl<'f, 'tcx> Combine<'tcx> for Glb<'f, 'tcx> { } } - fn mts(&self, a: &ty::mt<'tcx>, b: &ty::mt<'tcx>) -> cres<'tcx, ty::mt<'tcx>> { - let tcx = self.fields.infcx.tcx; - - debug!("{}.mts({}, {})", - self.tag(), - mt_to_string(tcx, a), - mt_to_string(tcx, b)); - - match (a.mutbl, b.mutbl) { - // If one side or both is mut, then the GLB must use - // the precise type from the mut side. - (MutMutable, MutMutable) => { - let t = try!(self.equate().tys(a.ty, b.ty)); - Ok(ty::mt {ty: t, mutbl: MutMutable}) - } - - // If one side or both is immutable, we can use the GLB of - // both sides but mutbl must be `MutImmutable`. - (MutImmutable, MutImmutable) => { - let t = try!(self.tys(a.ty, b.ty)); - Ok(ty::mt {ty: t, mutbl: MutImmutable}) - } - - // There is no mutual subtype of these combinations. - (MutMutable, MutImmutable) | - (MutImmutable, MutMutable) => { - Err(ty::terr_mutability) - } - } - } fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> { match (a, b) { diff --git a/src/librustc/middle/infer/lub.rs b/src/librustc/middle/infer/lub.rs index 37eace0b15b6d..95d661b1add8c 100644 --- a/src/librustc/middle/infer/lub.rs +++ b/src/librustc/middle/infer/lub.rs @@ -55,32 +55,6 @@ impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> { } } - fn mts(&self, a: &ty::mt<'tcx>, b: &ty::mt<'tcx>) -> cres<'tcx, ty::mt<'tcx>> { - let tcx = self.tcx(); - - debug!("{}.mts({}, {})", - self.tag(), - mt_to_string(tcx, a), - mt_to_string(tcx, b)); - - if a.mutbl != b.mutbl { - return Err(ty::terr_mutability) - } - - let m = a.mutbl; - match m { - MutImmutable => { - let t = try!(self.tys(a.ty, b.ty)); - Ok(ty::mt {ty: t, mutbl: m}) - } - - MutMutable => { - let t = try!(self.equate().tys(a.ty, b.ty)); - Ok(ty::mt {ty: t, mutbl: m}) - } - } - } - fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> { match (a, b) { (Unsafety::Unsafe, _) | (_, Unsafety::Unsafe) => Ok(Unsafety::Unsafe), diff --git a/src/librustc/middle/infer/sub.rs b/src/librustc/middle/infer/sub.rs index 8ab063686d4bc..067973fb80f68 100644 --- a/src/librustc/middle/infer/sub.rs +++ b/src/librustc/middle/infer/sub.rs @@ -66,30 +66,6 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> { Ok(a) } - fn mts(&self, a: &ty::mt<'tcx>, b: &ty::mt<'tcx>) -> cres<'tcx, ty::mt<'tcx>> { - debug!("mts({} <: {})", - a.repr(self.tcx()), - b.repr(self.tcx())); - - if a.mutbl != b.mutbl { - return Err(ty::terr_mutability); - } - - match b.mutbl { - MutMutable => { - // If supertype is mut, subtype must match exactly - // (i.e., invariant if mut): - try!(self.equate().tys(a.ty, b.ty)); - } - MutImmutable => { - // Otherwise we can be covariant: - try!(self.tys(a.ty, b.ty)); - } - } - - Ok(*a) // return is meaningless in sub, just return *a - } - fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> { self.lub().unsafeties(a, b).compare(b, || { ty::terr_unsafety_mismatch(expected_found(self, a, b)) From 0947f4076ddbf5c0db63d60c19f28b6b79023638 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 17 Mar 2015 15:22:11 -0400 Subject: [PATCH 2/2] Move unsafety out of the subtyping relation and into coercion. --- src/librustc/middle/astencode.rs | 11 +++++- src/librustc/middle/expr_use_visitor.rs | 3 +- src/librustc/middle/infer/bivariate.rs | 10 ------ src/librustc/middle/infer/combine.rs | 8 ++++- src/librustc/middle/infer/equate.rs | 11 ------ src/librustc/middle/infer/glb.rs | 10 ------ src/librustc/middle/infer/lub.rs | 9 ----- src/librustc/middle/infer/sub.rs | 11 +----- src/librustc/middle/mem_categorization.rs | 3 +- src/librustc/middle/ty.rs | 28 +++++++++++++++ src/librustc_trans/trans/consts.rs | 3 ++ src/librustc_trans/trans/expr.rs | 5 ++- src/librustc_typeck/check/coercion.rs | 40 ++++++++++++++++++++++ src/librustc_typeck/check/mod.rs | 4 +-- src/librustc_typeck/check/writeback.rs | 4 +++ src/test/compile-fail/unsafe-subtyping.rs | 21 ++++++++++++ src/test/compile-fail/unsafe-trait-impl.rs | 22 ++++++++++++ src/test/compile-fail/variadic-ffi.rs | 4 +-- src/test/run-pass/unsafe-coercion.rs | 25 ++++++++++++++ 19 files changed, 173 insertions(+), 59 deletions(-) create mode 100644 src/test/compile-fail/unsafe-subtyping.rs create mode 100644 src/test/compile-fail/unsafe-trait-impl.rs create mode 100644 src/test/run-pass/unsafe-coercion.rs diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index f705cc8ed99fe..f7210728bb480 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -993,8 +993,14 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> { }) } + ty::AdjustUnsafeFnPointer => { + this.emit_enum_variant("AdjustUnsafeFnPointer", 2, 0, |_| { + Ok(()) + }) + } + ty::AdjustDerefRef(ref auto_deref_ref) => { - this.emit_enum_variant("AdjustDerefRef", 2, 2, |this| { + this.emit_enum_variant("AdjustDerefRef", 3, 2, |this| { this.emit_enum_variant_arg(0, |this| Ok(this.emit_auto_deref_ref(ecx, auto_deref_ref))) }) @@ -1619,6 +1625,9 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { ty::AdjustReifyFnPointer(def_id) } 2 => { + ty::AdjustUnsafeFnPointer + } + 3 => { let auto_deref_ref: ty::AutoDerefRef = this.read_enum_variant_arg(0, |this| Ok(this.read_auto_deref_ref(dcx))).unwrap(); diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index c0d51f5675ca2..6d2392054f9d5 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -790,7 +790,8 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { None => { } Some(adjustment) => { match *adjustment { - ty::AdjustReifyFnPointer(..) => { + ty::AdjustReifyFnPointer(..) | + ty::AdjustUnsafeFnPointer(..) => { // Creating a closure/fn-pointer consumes the // input and stores it into the resulting // rvalue. diff --git a/src/librustc/middle/infer/bivariate.rs b/src/librustc/middle/infer/bivariate.rs index 1916d39b49828..cedb30eebfd78 100644 --- a/src/librustc/middle/infer/bivariate.rs +++ b/src/librustc/middle/infer/bivariate.rs @@ -33,8 +33,6 @@ use middle::infer::{cres}; use middle::infer::type_variable::{BiTo}; use util::ppaux::{Repr}; -use syntax::ast::{Unsafety}; - pub struct Bivariate<'f, 'tcx: 'f> { fields: CombineFields<'f, 'tcx> } @@ -74,14 +72,6 @@ impl<'f, 'tcx> Combine<'tcx> for Bivariate<'f, 'tcx> { Ok(a) } - fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> { - if a != b { - Err(ty::terr_unsafety_mismatch(expected_found(self, a, b))) - } else { - Ok(a) - } - } - fn builtin_bounds(&self, a: BuiltinBounds, b: BuiltinBounds) diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs index 9fdfdaccf4eaf..94c9699e30cc4 100644 --- a/src/librustc/middle/infer/combine.rs +++ b/src/librustc/middle/infer/combine.rs @@ -263,7 +263,13 @@ pub trait Combine<'tcx> : Sized { self.tys_with_variance(ty::Contravariant, a, b).and_then(|t| Ok(t)) } - fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety>; + fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> { + if a != b { + Err(ty::terr_unsafety_mismatch(expected_found(self, a, b))) + } else { + Ok(a) + } + } fn abi(&self, a: abi::Abi, b: abi::Abi) -> cres<'tcx, abi::Abi> { if a == b { diff --git a/src/librustc/middle/infer/equate.rs b/src/librustc/middle/infer/equate.rs index e9ffe368f4220..c2b73bca8584e 100644 --- a/src/librustc/middle/infer/equate.rs +++ b/src/librustc/middle/infer/equate.rs @@ -16,8 +16,6 @@ use middle::infer::{Subtype}; use middle::infer::type_variable::{EqTo}; use util::ppaux::{Repr}; -use syntax::ast::Unsafety; - pub struct Equate<'f, 'tcx: 'f> { fields: CombineFields<'f, 'tcx> } @@ -54,15 +52,6 @@ impl<'f, 'tcx> Combine<'tcx> for Equate<'f, 'tcx> { Ok(a) } - - fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> { - if a != b { - Err(ty::terr_unsafety_mismatch(expected_found(self, a, b))) - } else { - Ok(a) - } - } - fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> { debug!("{}.tys({}, {})", self.tag(), a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx)); diff --git a/src/librustc/middle/infer/glb.rs b/src/librustc/middle/infer/glb.rs index 43d64643fe2ee..e17155a2ae69b 100644 --- a/src/librustc/middle/infer/glb.rs +++ b/src/librustc/middle/infer/glb.rs @@ -15,8 +15,6 @@ use super::{cres}; use super::Subtype; use middle::ty::{self, Ty}; -use syntax::ast::{MutImmutable, MutMutable, Unsafety}; -use util::ppaux::mt_to_string; use util::ppaux::Repr; /// "Greatest lower bound" (common subtype) @@ -55,14 +53,6 @@ impl<'f, 'tcx> Combine<'tcx> for Glb<'f, 'tcx> { } } - - fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> { - match (a, b) { - (Unsafety::Normal, _) | (_, Unsafety::Normal) => Ok(Unsafety::Normal), - (Unsafety::Unsafe, Unsafety::Unsafe) => Ok(Unsafety::Unsafe) - } - } - fn regions(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ty::Region> { debug!("{}.regions({}, {})", self.tag(), diff --git a/src/librustc/middle/infer/lub.rs b/src/librustc/middle/infer/lub.rs index 95d661b1add8c..be814b2acc10a 100644 --- a/src/librustc/middle/infer/lub.rs +++ b/src/librustc/middle/infer/lub.rs @@ -15,8 +15,6 @@ use super::{cres}; use super::{Subtype}; use middle::ty::{self, Ty}; -use syntax::ast::{MutMutable, MutImmutable, Unsafety}; -use util::ppaux::mt_to_string; use util::ppaux::Repr; /// "Least upper bound" (common supertype) @@ -55,13 +53,6 @@ impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> { } } - fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> { - match (a, b) { - (Unsafety::Unsafe, _) | (_, Unsafety::Unsafe) => Ok(Unsafety::Unsafe), - (Unsafety::Normal, Unsafety::Normal) => Ok(Unsafety::Normal), - } - } - fn regions(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ty::Region> { debug!("{}.regions({}, {})", self.tag(), diff --git a/src/librustc/middle/infer/sub.rs b/src/librustc/middle/infer/sub.rs index 067973fb80f68..423fb86dc5c85 100644 --- a/src/librustc/middle/infer/sub.rs +++ b/src/librustc/middle/infer/sub.rs @@ -9,7 +9,7 @@ // except according to those terms. use super::combine::*; -use super::{cres, CresCompare}; +use super::{cres}; use super::higher_ranked::HigherRankedRelations; use super::{Subtype}; use super::type_variable::{SubtypeOf, SupertypeOf}; @@ -18,9 +18,6 @@ use middle::ty::{self, Ty}; use middle::ty::TyVar; use util::ppaux::{Repr}; -use syntax::ast::{MutImmutable, MutMutable, Unsafety}; - - /// "Greatest lower bound" (common subtype) pub struct Sub<'f, 'tcx: 'f> { fields: CombineFields<'f, 'tcx> @@ -66,12 +63,6 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> { Ok(a) } - fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> { - self.lub().unsafeties(a, b).compare(b, || { - ty::terr_unsafety_mismatch(expected_found(self, a, b)) - }) - } - fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> { debug!("{}.tys({}, {})", self.tag(), a.repr(self.tcx()), b.repr(self.tcx())); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index a57ea3759de9b..5237a86ebb630 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -428,7 +428,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { Some(adjustment) => { match *adjustment { - ty::AdjustReifyFnPointer(..) => { + ty::AdjustReifyFnPointer(..) | + ty::AdjustUnsafeFnPointer(..) => { debug!("cat_expr(AdjustReifyFnPointer): {}", expr.repr(self.tcx())); // Convert a bare fn to a closure by adding NULL env. diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 8cc188e5df5b0..35a6be3761431 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -282,6 +282,7 @@ pub enum Variance { #[derive(Clone, Debug)] pub enum AutoAdjustment<'tcx> { AdjustReifyFnPointer(ast::DefId), // go from a fn-item type to a fn-pointer type + AdjustUnsafeFnPointer, // go from a safe fn pointer to an unsafe fn pointer AdjustDerefRef(AutoDerefRef<'tcx>) } @@ -2637,6 +2638,17 @@ impl<'tcx> ctxt<'tcx> { substs } + /// Create an unsafe fn ty based on a safe fn ty. + pub fn safe_to_unsafe_fn_ty(&self, bare_fn: &BareFnTy<'tcx>) -> Ty<'tcx> { + assert_eq!(bare_fn.unsafety, ast::Unsafety::Normal); + let unsafe_fn_ty_a = self.mk_bare_fn(ty::BareFnTy { + unsafety: ast::Unsafety::Unsafe, + abi: bare_fn.abi, + sig: bare_fn.sig.clone() + }); + ty::mk_bare_fn(self, None, unsafe_fn_ty_a) + } + pub fn mk_bare_fn(&self, bare_fn: BareFnTy<'tcx>) -> &'tcx BareFnTy<'tcx> { if let Some(bare_fn) = self.bare_fn_interner.borrow().get(&bare_fn) { return *bare_fn; @@ -4526,6 +4538,18 @@ pub fn adjust_ty<'tcx, F>(cx: &ctxt<'tcx>, } } + AdjustUnsafeFnPointer => { + match unadjusted_ty.sty { + ty::ty_bare_fn(None, b) => cx.safe_to_unsafe_fn_ty(b), + ref b => { + cx.sess.bug( + &format!("AdjustReifyFnPointer adjustment on non-fn-item: \ + {:?}", + b)); + } + } + } + AdjustDerefRef(ref adj) => { let mut adjusted_ty = unadjusted_ty; @@ -6695,6 +6719,7 @@ impl<'tcx> AutoAdjustment<'tcx> { pub fn is_identity(&self) -> bool { match *self { AdjustReifyFnPointer(..) => false, + AdjustUnsafeFnPointer(..) => false, AdjustDerefRef(ref r) => r.is_identity(), } } @@ -6844,6 +6869,9 @@ impl<'tcx> Repr<'tcx> for AutoAdjustment<'tcx> { AdjustReifyFnPointer(def_id) => { format!("AdjustReifyFnPointer({})", def_id.repr(tcx)) } + AdjustUnsafeFnPointer => { + format!("AdjustUnsafeFnPointer") + } AdjustDerefRef(ref data) => { data.repr(tcx) } diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 6ff230b7065da..4da295f7c33b7 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -253,6 +253,9 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // FIXME(#19925) once fn item types are // zero-sized, we'll need to do something here } + Some(ty::AdjustUnsafeFnPointer) => { + // purely a type-level thing + } Some(ty::AdjustDerefRef(adj)) => { let mut ty = ety; // Save the last autoderef in case we can avoid it. diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index da2b5a43b48cc..36cf0317b4455 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -72,7 +72,7 @@ use trans::monomorphize; use trans::tvec; use trans::type_of; use middle::ty::{struct_fields, tup_fields}; -use middle::ty::{AdjustDerefRef, AdjustReifyFnPointer, AutoUnsafe}; +use middle::ty::{AdjustDerefRef, AdjustReifyFnPointer, AdjustUnsafeFnPointer, AutoUnsafe}; use middle::ty::{AutoPtr}; use middle::ty::{self, Ty}; use middle::ty::MethodCall; @@ -396,6 +396,9 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // FIXME(#19925) once fn item types are // zero-sized, we'll need to do something here } + AdjustUnsafeFnPointer => { + // purely a type-level thing + } AdjustDerefRef(ref adj) => { let (autoderefs, use_autoref) = match adj.autoref { // Extracting a value from a box counts as a deref, but if we are diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 4e42ec61011ba..a82ebcd9175fa 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -143,6 +143,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // require double indirection). self.coerce_from_fn_item(a, a_def_id, a_f, b) } + ty::ty_bare_fn(None, a_f) => { + // We permit coercion of fn pointers to drop the + // unsafe qualifier. + self.coerce_from_fn_pointer(a, a_f, b) + } _ => { // Otherwise, just use subtyping rules. self.subtype(a, b) @@ -411,6 +416,41 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ) } + fn coerce_from_fn_pointer(&self, + a: Ty<'tcx>, + fn_ty_a: &'tcx ty::BareFnTy<'tcx>, + b: Ty<'tcx>) + -> CoerceResult<'tcx> + { + /*! + * Attempts to coerce from the type of a Rust function item + * into a closure or a `proc`. + */ + + self.unpack_actual_value(b, |b| { + debug!("coerce_from_fn_pointer(a={}, b={})", + a.repr(self.tcx()), b.repr(self.tcx())); + + match b.sty { + ty::ty_bare_fn(None, fn_ty_b) => { + match (fn_ty_a.unsafety, fn_ty_b.unsafety) { + (ast::Unsafety::Normal, ast::Unsafety::Unsafe) => { + let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a); + try!(self.subtype(unsafe_a, b)); + Ok(Some(ty::AdjustUnsafeFnPointer)) + } + _ => { + self.subtype(a, b) + } + } + } + _ => { + return self.subtype(a, b) + } + } + }) + } + fn coerce_from_fn_item(&self, a: Ty<'tcx>, fn_def_id_a: ast::DefId, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 31bee612b78d4..5677993f3e700 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1532,8 +1532,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, adj: &ty::AutoAdjustment<'tcx>) { match *adj { - ty::AdjustReifyFnPointer(..) => { - } + ty::AdjustReifyFnPointer(..) => { } + ty::AdjustUnsafeFnPointer => { } ty::AdjustDerefRef(ref d_r) => { match d_r.autoref { Some(ref a_r) => { diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index fca40df7aaa6a..2537f9362bf31 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -265,6 +265,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { ty::AdjustReifyFnPointer(def_id) } + ty::AdjustUnsafeFnPointer => { + ty::AdjustUnsafeFnPointer + } + ty::AdjustDerefRef(adj) => { for autoderef in 0..adj.autoderefs { let method_call = MethodCall::autoderef(id, autoderef); diff --git a/src/test/compile-fail/unsafe-subtyping.rs b/src/test/compile-fail/unsafe-subtyping.rs new file mode 100644 index 0000000000000..f486763681967 --- /dev/null +++ b/src/test/compile-fail/unsafe-subtyping.rs @@ -0,0 +1,21 @@ +// Copyright 2013-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. + +// Check that safe fns are not a subtype of unsafe fns. + +fn foo(x: Option) -> Option { + x //~ ERROR mismatched types +} + +fn bar(x: fn(i32)) -> unsafe fn(i32) { + x // OK, coercion! +} + +fn main() { } diff --git a/src/test/compile-fail/unsafe-trait-impl.rs b/src/test/compile-fail/unsafe-trait-impl.rs new file mode 100644 index 0000000000000..71da2f7633f3c --- /dev/null +++ b/src/test/compile-fail/unsafe-trait-impl.rs @@ -0,0 +1,22 @@ +// Copyright 2013-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. + +// Check that safe fns are not a subtype of unsafe fns. + +trait Foo { + unsafe fn len(&self) -> u32; +} + +impl Foo for u32 { + fn len(&self) -> u32 { *self } + //~^ ERROR incompatible type for trait: expected unsafe fn, found normal fn +} + +fn main() { } diff --git a/src/test/compile-fail/variadic-ffi.rs b/src/test/compile-fail/variadic-ffi.rs index 86271f670ce6f..2a62ac2ac303a 100644 --- a/src/test/compile-fail/variadic-ffi.rs +++ b/src/test/compile-fail/variadic-ffi.rs @@ -30,9 +30,9 @@ fn main() { //~| expected non-variadic fn //~| found variadic function - let y: unsafe extern "C" fn(f: isize, x: u8, ...) = bar; + let y: extern "C" fn(f: isize, x: u8, ...) = bar; //~^ ERROR: mismatched types - //~| expected `unsafe extern "C" fn(isize, u8, ...)` + //~| expected `extern "C" fn(isize, u8, ...)` //~| found `extern "C" fn(isize, u8) {bar}` //~| expected variadic fn //~| found non-variadic function diff --git a/src/test/run-pass/unsafe-coercion.rs b/src/test/run-pass/unsafe-coercion.rs new file mode 100644 index 0000000000000..06980e162c83b --- /dev/null +++ b/src/test/run-pass/unsafe-coercion.rs @@ -0,0 +1,25 @@ +// Copyright 2013-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. + +// Check that safe fns are not a subtype of unsafe fns. + +fn foo(x: i32) -> i32 { + x * 22 +} + +fn bar(x: fn(i32) -> i32) -> unsafe fn(i32) -> i32 { + x // OK, coercion! +} + +fn main() { + let f = bar(foo); + let x = unsafe { f(2) }; + assert_eq!(x, 44); +}