Correct handling of if/match, and make explicit computation of

common supertypes.

This was breaking with the change to regions because of the
(now incorrect) assumpton that our inference code makes,
which is that if a <: b succeeds, there is no need to compute
the LUB/GLB.
This commit is contained in:
Niko Matsakis 2013-06-05 21:04:07 -04:00
parent 9e6d5e152e
commit 42344af713
12 changed files with 265 additions and 120 deletions

View File

@ -30,8 +30,8 @@ pub fn check_match(fcx: @mut FnCtxt,
arms: &[ast::arm]) {
let tcx = fcx.ccx.tcx;
let pattern_ty = fcx.infcx().next_ty_var();
check_expr_has_type(fcx, discrim, pattern_ty);
let discrim_ty = fcx.infcx().next_ty_var();
check_expr_has_type(fcx, discrim, discrim_ty);
// Typecheck the patterns first, so that we get types for all the
// bindings.
@ -41,13 +41,20 @@ pub fn check_match(fcx: @mut FnCtxt,
map: pat_id_map(tcx.def_map, arm.pats[0]),
};
for arm.pats.iter().advance |p| { check_pat(&pcx, *p, pattern_ty);}
for arm.pats.iter().advance |p| { check_pat(&pcx, *p, discrim_ty);}
}
// The result of the match is the common supertype of all the
// arms. Start out the value as bottom, since it's the, well,
// bottom the type lattice, and we'll be moving up the lattice as
// we process each arm. (Note that any match with 0 arms is matching
// on any empty type and is therefore unreachable; should the flow
// of execution reach it, we will fail, so bottom is an appropriate
// type in that case)
let mut result_ty = ty::mk_bot();
// Now typecheck the blocks.
let mut result_ty = fcx.infcx().next_ty_var();
let mut arm_non_bot = false;
let mut saw_err = false;
let mut saw_err = ty::type_is_error(discrim_ty);
for arms.iter().advance |arm| {
let mut guard_err = false;
let mut guard_bot = false;
@ -74,18 +81,22 @@ pub fn check_match(fcx: @mut FnCtxt,
else if guard_bot {
fcx.write_bot(arm.body.node.id);
}
else if !ty::type_is_bot(bty) {
arm_non_bot = true; // If the match *may* evaluate to a non-_|_
// expr, the whole thing is non-_|_
}
demand::suptype(fcx, arm.body.span, result_ty, bty);
result_ty =
infer::common_supertype(
fcx.infcx(),
infer::MatchExpression(expr.span),
true, // result_ty is "expected" here
result_ty,
bty);
}
if saw_err {
result_ty = ty::mk_err();
}
else if !arm_non_bot {
} else if ty::type_is_bot(discrim_ty) {
result_ty = ty::mk_bot();
}
fcx.write_ty(expr.id, result_ty);
}
@ -647,3 +658,4 @@ pub fn check_pointer_pat(pcx: &pat_ctxt,
#[deriving(Eq)]
enum PointerKind { Managed, Send, Borrowed }

View File

@ -848,7 +848,7 @@ impl FnCtxt {
pub fn mk_subty(&self,
a_is_expected: bool,
origin: infer::SubtypeOrigin,
origin: infer::TypeOrigin,
sub: ty::t,
sup: ty::t)
-> Result<(), ty::type_err> {
@ -886,7 +886,7 @@ impl FnCtxt {
pub fn mk_eqty(&self,
a_is_expected: bool,
origin: infer::SubtypeOrigin,
origin: infer::TypeOrigin,
sub: ty::t,
sup: ty::t)
-> Result<(), ty::type_err> {
@ -1436,27 +1436,42 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
// A generic function for checking the then and else in an if
// or if-check
fn check_then_else(fcx: @mut FnCtxt,
thn: &ast::blk,
elsopt: Option<@ast::expr>,
cond_expr: @ast::expr,
then_blk: &ast::blk,
opt_else_expr: Option<@ast::expr>,
id: ast::node_id,
_sp: span) {
let if_t =
match elsopt {
Some(els) => {
let if_t = fcx.infcx().next_ty_var();
check_block(fcx, thn);
let thn_t = fcx.node_ty(thn.node.id);
demand::suptype(fcx, thn.span, if_t, thn_t);
check_expr_has_type(fcx, els, if_t);
if_t
}
None => {
check_block_no_value(fcx, thn);
ty::mk_nil()
}
};
sp: span,
expected: Option<ty::t>) {
check_expr_has_type(fcx, cond_expr, ty::mk_bool());
fcx.write_ty(id, if_t);
let branches_ty = match opt_else_expr {
Some(else_expr) => {
check_block_with_expected(fcx, then_blk, expected);
let then_ty = fcx.node_ty(then_blk.node.id);
check_expr_with_opt_hint(fcx, else_expr, expected);
let else_ty = fcx.expr_ty(else_expr);
infer::common_supertype(fcx.infcx(),
infer::IfExpression(sp),
true,
then_ty,
else_ty)
}
None => {
check_block_no_value(fcx, then_blk);
ty::mk_nil()
}
};
let cond_ty = fcx.expr_ty(cond_expr);
let if_ty = if ty::type_is_error(cond_ty) {
ty::mk_err()
} else if ty::type_is_bot(cond_ty) {
ty::mk_bot()
} else {
branches_ty
};
fcx.write_ty(id, if_ty);
}
fn lookup_op_method(fcx: @mut FnCtxt,
@ -2501,25 +2516,9 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
fcx.write_nil(id);
}
}
ast::expr_if(cond, ref thn, elsopt) => {
check_expr_has_type(fcx, cond, ty::mk_bool());
check_then_else(fcx, thn, elsopt, id, expr.span);
let cond_ty = fcx.expr_ty(cond);
let then_ty = fcx.node_ty(thn.node.id);
let else_is_bot = elsopt.map_default(false, |els| {
ty::type_is_bot(fcx.expr_ty(*els))});
if ty::type_is_error(cond_ty) || ty::type_is_error(then_ty) {
fcx.write_error(id);
}
else if elsopt.map_default(false, |els| {
ty::type_is_error(fcx.expr_ty(*els)) }) {
fcx.write_error(id);
}
else if ty::type_is_bot(cond_ty) ||
(ty::type_is_bot(then_ty) && else_is_bot) {
fcx.write_bot(id);
}
// Other cases were handled by check_then_else
ast::expr_if(cond, ref then_blk, opt_else_expr) => {
check_then_else(fcx, cond, then_blk, opt_else_expr,
id, expr.span, expected);
}
ast::expr_while(cond, ref body) => {
check_expr_has_type(fcx, cond, ty::mk_bool());
@ -2547,30 +2546,6 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
}
ast::expr_match(discrim, ref arms) => {
_match::check_match(fcx, expr, discrim, *arms);
let discrim_ty = fcx.expr_ty(discrim);
let arm_tys = arms.map(|a| fcx.node_ty(a.body.node.id));
if ty::type_is_error(discrim_ty) ||
arm_tys.iter().any_(|t| ty::type_is_error(*t)) {
fcx.write_error(id);
}
// keep in mind that `all` returns true in the empty vec case,
// which is what we want
else if ty::type_is_bot(discrim_ty) ||
arm_tys.iter().all(|t| ty::type_is_bot(*t)) {
fcx.write_bot(id);
}
else {
// Find the first non-_|_ arm.
// We know there's at least one because we already checked
// for n=0 as well as all arms being _|_ in the previous
// `if`.
for arm_tys.iter().advance |arm_ty| {
if !ty::type_is_bot(*arm_ty) {
fcx.write_ty(id, *arm_ty);
break;
}
}
}
}
ast::expr_fn_block(ref decl, ref body) => {
check_expr_fn(fcx, expr, None,

View File

@ -65,7 +65,7 @@ use middle::typeck::infer::sub::Sub;
use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::unify::{InferCtxtMethods};
use middle::typeck::infer::{InferCtxt, cres, ures};
use middle::typeck::infer::{SubtypeOrigin, SubtypeTrace};
use middle::typeck::infer::{TypeOrigin, TypeTrace};
use util::common::indent;
use std::result::{iter_vec2, map_vec2};
@ -80,7 +80,7 @@ pub trait Combine {
fn infcx(&self) -> @mut InferCtxt;
fn tag(&self) -> ~str;
fn a_is_expected(&self) -> bool;
fn trace(&self) -> SubtypeTrace;
fn trace(&self) -> TypeTrace;
fn sub(&self) -> Sub;
fn lub(&self) -> Lub;
@ -122,7 +122,7 @@ pub trait Combine {
pub struct CombineFields {
infcx: @mut InferCtxt,
a_is_expected: bool,
trace: SubtypeTrace,
trace: TypeTrace,
}
pub fn expected_found<C:Combine,T>(

View File

@ -22,7 +22,7 @@ rise to a patricular error.
The basis of the system are the "origin" types. An "origin" is the
reason that a constraint or inference variable arose. There are
different "origin" enums for different kinds of constraints/variables
(e.g., `SubtypeOrigin`, `RegionVariableOrigin`). An origin always has
(e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has
a span, but also more information so that we can generate a meaningful
error message.
@ -40,7 +40,7 @@ store and later report what gave rise to the conflicting constraints.
# Subtype Trace
Determing whether `T1 <: T2` often involves a number of subtypes and
subconstraints along the way. A "SubtypeTrace" is an extended version
subconstraints along the way. A "TypeTrace" is an extended version
of an origin that traces the types and other values that were being
compared. It is not necessarily comprehensive (in fact, at the time of
this writing it only tracks the root values being compared) but I'd
@ -64,8 +64,8 @@ use middle::ty;
use middle::ty::Region;
use middle::typeck::infer;
use middle::typeck::infer::InferCtxt;
use middle::typeck::infer::SubtypeTrace;
use middle::typeck::infer::SubtypeOrigin;
use middle::typeck::infer::TypeTrace;
use middle::typeck::infer::TypeOrigin;
use middle::typeck::infer::SubregionOrigin;
use middle::typeck::infer::RegionVariableOrigin;
use middle::typeck::infer::Types;
@ -108,8 +108,8 @@ impl InferCtxt {
}
}
fn report_and_explain_type_error(@mut self,
trace: SubtypeTrace,
pub fn report_and_explain_type_error(@mut self,
trace: TypeTrace,
terr: &ty::type_err) {
let tcx = self.tcx;
@ -125,7 +125,9 @@ impl InferCtxt {
infer::MethodCompatCheck(_) => "method not compatible with trait",
infer::ExprAssignable(_) => "mismatched types",
infer::RelateTraitRefs(_) => "mismatched traits",
infer::RelateSelfType(_) => "mismatched types"
infer::RelateSelfType(_) => "mismatched types",
infer::MatchExpression(_) => "match arms have incompatible types",
infer::IfExpression(_) => "if and else have incompatible types",
};
self.tcx.sess.span_err(
@ -179,7 +181,7 @@ impl InferCtxt {
sup: Region) {
match origin {
infer::Subtype(trace) => {
let terr = ty::terr_regions_does_not_outlive(sub, sup);
let terr = ty::terr_regions_does_not_outlive(sup, sub);
self.report_and_explain_type_error(trace, &terr);
}
infer::Reborrow(span) => {

View File

@ -18,7 +18,7 @@ use middle::typeck::infer::lub::Lub;
use middle::typeck::infer::sub::Sub;
use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::{cres, InferCtxt};
use middle::typeck::infer::{SubtypeTrace, Subtype};
use middle::typeck::infer::{TypeTrace, Subtype};
use middle::typeck::infer::fold_regions_in_sig;
use middle::typeck::isr_alist;
use syntax::ast;
@ -38,7 +38,7 @@ impl Combine for Glb {
fn infcx(&self) -> @mut InferCtxt { self.infcx }
fn tag(&self) -> ~str { ~"glb" }
fn a_is_expected(&self) -> bool { self.a_is_expected }
fn trace(&self) -> SubtypeTrace { self.trace }
fn trace(&self) -> TypeTrace { self.trace }
fn sub(&self) -> Sub { Sub(**self) }
fn lub(&self) -> Lub { Lub(**self) }

View File

@ -19,7 +19,7 @@ use middle::typeck::infer::sub::Sub;
use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::{cres, InferCtxt};
use middle::typeck::infer::fold_regions_in_sig;
use middle::typeck::infer::{SubtypeTrace, Subtype};
use middle::typeck::infer::{TypeTrace, Subtype};
use middle::typeck::isr_alist;
use util::common::indent;
use util::ppaux::mt_to_str;
@ -45,7 +45,7 @@ impl Combine for Lub {
fn infcx(&self) -> @mut InferCtxt { self.infcx }
fn tag(&self) -> ~str { ~"lub" }
fn a_is_expected(&self) -> bool { self.a_is_expected }
fn trace(&self) -> SubtypeTrace { self.trace }
fn trace(&self) -> TypeTrace { self.trace }
fn sub(&self) -> Sub { Sub(**self) }
fn lub(&self) -> Lub { Lub(**self) }

View File

@ -260,6 +260,7 @@ use middle::typeck::infer::combine::{Combine, CombineFields, eq_tys};
use middle::typeck::infer::region_inference::{RegionVarBindings};
use middle::typeck::infer::resolve::{resolver};
use middle::typeck::infer::sub::Sub;
use middle::typeck::infer::lub::Lub;
use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::unify::{ValsAndBindings, Root};
use middle::typeck::isr_alist;
@ -324,7 +325,7 @@ pub struct InferCtxt {
/// Why did we require that the two types be related?
///
/// See `error_reporting.rs` for more details
pub enum SubtypeOrigin {
pub enum TypeOrigin {
// Not yet categorized in a better way
Misc(span),
@ -339,6 +340,12 @@ pub enum SubtypeOrigin {
// Relating trait refs when resolving vtables
RelateSelfType(span),
// Computing common supertype in a match expression
MatchExpression(span),
// Computing common supertype in an if expression
IfExpression(span),
}
/// See `error_reporting.rs` for more details
@ -351,8 +358,8 @@ pub enum ValuePairs {
/// encounter an error or subtyping constraint.
///
/// See `error_reporting.rs` for more details.
pub struct SubtypeTrace {
origin: SubtypeOrigin,
pub struct TypeTrace {
origin: TypeOrigin,
values: ValuePairs,
}
@ -361,7 +368,7 @@ pub struct SubtypeTrace {
/// See `error_reporting.rs` for more details
pub enum SubregionOrigin {
// Arose from a subtyping relation
Subtype(SubtypeTrace),
Subtype(TypeTrace),
// Invocation of closure must be within its lifetime
InvokeClosure(span),
@ -425,7 +432,7 @@ pub enum RegionVariableOrigin {
Autoref(span),
// Regions created as part of an automatic coercion
Coercion(SubtypeTrace),
Coercion(TypeTrace),
// Region variables created for bound regions
// in a function or method that is called
@ -487,16 +494,47 @@ pub fn new_infer_ctxt(tcx: ty::ctxt) -> @mut InferCtxt {
}
}
pub fn common_supertype(cx: @mut InferCtxt,
origin: TypeOrigin,
a_is_expected: bool,
a: ty::t,
b: ty::t)
-> ty::t {
/*!
* Computes the least upper-bound of `a` and `b`. If this is
* not possible, reports an error and returns ty::err.
*/
debug!("common_supertype(%s, %s)", a.inf_str(cx), b.inf_str(cx));
let trace = TypeTrace {
origin: origin,
values: Types(expected_found(a_is_expected, a, b))
};
let result = do cx.commit {
cx.lub(a_is_expected, trace).tys(a, b)
};
match result {
Ok(t) => t,
Err(ref err) => {
cx.report_and_explain_type_error(trace, err);
ty::mk_err()
}
}
}
pub fn mk_subty(cx: @mut InferCtxt,
a_is_expected: bool,
origin: SubtypeOrigin,
origin: TypeOrigin,
a: ty::t,
b: ty::t)
-> ures {
debug!("mk_subty(%s <: %s)", a.inf_str(cx), b.inf_str(cx));
do indent {
do cx.commit {
let trace = SubtypeTrace {
let trace = TypeTrace {
origin: origin,
values: Types(expected_found(a_is_expected, a, b))
};
@ -509,7 +547,7 @@ pub fn can_mk_subty(cx: @mut InferCtxt, a: ty::t, b: ty::t) -> ures {
debug!("can_mk_subty(%s <: %s)", a.inf_str(cx), b.inf_str(cx));
do indent {
do cx.probe {
let trace = SubtypeTrace {
let trace = TypeTrace {
origin: Misc(codemap::dummy_sp()),
values: Types(expected_found(true, a, b))
};
@ -531,14 +569,14 @@ pub fn mk_subr(cx: @mut InferCtxt,
pub fn mk_eqty(cx: @mut InferCtxt,
a_is_expected: bool,
origin: SubtypeOrigin,
origin: TypeOrigin,
a: ty::t,
b: ty::t)
-> ures {
debug!("mk_eqty(%s <: %s)", a.inf_str(cx), b.inf_str(cx));
do indent {
do cx.commit {
let trace = SubtypeTrace {
let trace = TypeTrace {
origin: origin,
values: Types(expected_found(a_is_expected, a, b))
};
@ -550,7 +588,7 @@ pub fn mk_eqty(cx: @mut InferCtxt,
pub fn mk_sub_trait_refs(cx: @mut InferCtxt,
a_is_expected: bool,
origin: SubtypeOrigin,
origin: TypeOrigin,
a: @ty::TraitRef,
b: @ty::TraitRef)
-> ures
@ -559,7 +597,7 @@ pub fn mk_sub_trait_refs(cx: @mut InferCtxt,
a.inf_str(cx), b.inf_str(cx));
do indent {
do cx.commit {
let trace = SubtypeTrace {
let trace = TypeTrace {
origin: origin,
values: TraitRefs(expected_found(a_is_expected, a, b))
};
@ -581,14 +619,14 @@ fn expected_found<T>(a_is_expected: bool,
pub fn mk_coercety(cx: @mut InferCtxt,
a_is_expected: bool,
origin: SubtypeOrigin,
origin: TypeOrigin,
a: ty::t,
b: ty::t)
-> CoerceResult {
debug!("mk_coercety(%s -> %s)", a.inf_str(cx), b.inf_str(cx));
do indent {
do cx.commit {
let trace = SubtypeTrace {
let trace = TypeTrace {
origin: origin,
values: Types(expected_found(a_is_expected, a, b))
};
@ -601,7 +639,7 @@ pub fn can_mk_coercety(cx: @mut InferCtxt, a: ty::t, b: ty::t) -> ures {
debug!("can_mk_coercety(%s -> %s)", a.inf_str(cx), b.inf_str(cx));
do indent {
do cx.probe {
let trace = SubtypeTrace {
let trace = TypeTrace {
origin: Misc(codemap::dummy_sp()),
values: Types(expected_found(true, a, b))
};
@ -690,17 +728,21 @@ struct Snapshot {
impl InferCtxt {
pub fn combine_fields(@mut self,
a_is_expected: bool,
trace: SubtypeTrace)
trace: TypeTrace)
-> CombineFields {
CombineFields {infcx: self,
a_is_expected: a_is_expected,
trace: trace}
}
pub fn sub(@mut self, a_is_expected: bool, trace: SubtypeTrace) -> Sub {
pub fn sub(@mut self, a_is_expected: bool, trace: TypeTrace) -> Sub {
Sub(self.combine_fields(a_is_expected, trace))
}
pub fn lub(@mut self, a_is_expected: bool, trace: TypeTrace) -> Lub {
Lub(self.combine_fields(a_is_expected, trace))
}
pub fn in_snapshot(&self) -> bool {
self.region_vars.in_snapshot()
}
@ -946,7 +988,7 @@ impl InferCtxt {
}
pub fn replace_bound_regions_with_fresh_regions(&mut self,
trace: SubtypeTrace,
trace: TypeTrace,
fsig: &ty::FnSig)
-> (ty::FnSig, isr_alist) {
let(isr, _, fn_sig) =
@ -972,19 +1014,19 @@ pub fn fold_regions_in_sig(
}
}
impl SubtypeTrace {
impl TypeTrace {
pub fn span(&self) -> span {
self.origin.span()
}
}
impl Repr for SubtypeTrace {
impl Repr for TypeTrace {
fn repr(&self, tcx: ty::ctxt) -> ~str {
fmt!("SubtypeTrace(%s)", self.origin.repr(tcx))
fmt!("TypeTrace(%s)", self.origin.repr(tcx))
}
}
impl SubtypeOrigin {
impl TypeOrigin {
pub fn span(&self) -> span {
match *self {
MethodCompatCheck(span) => span,
@ -992,11 +1034,13 @@ impl SubtypeOrigin {
Misc(span) => span,
RelateTraitRefs(span) => span,
RelateSelfType(span) => span,
MatchExpression(span) => span,
IfExpression(span) => span,
}
}
}
impl Repr for SubtypeOrigin {
impl Repr for TypeOrigin {
fn repr(&self, tcx: ty::ctxt) -> ~str {
match *self {
MethodCompatCheck(a) => fmt!("MethodCompatCheck(%s)", a.repr(tcx)),
@ -1004,6 +1048,8 @@ impl Repr for SubtypeOrigin {
Misc(a) => fmt!("Misc(%s)", a.repr(tcx)),
RelateTraitRefs(a) => fmt!("RelateTraitRefs(%s)", a.repr(tcx)),
RelateSelfType(a) => fmt!("RelateSelfType(%s)", a.repr(tcx)),
MatchExpression(a) => fmt!("MatchExpression(%s)", a.repr(tcx)),
IfExpression(a) => fmt!("IfExpression(%s)", a.repr(tcx)),
}
}
}

View File

@ -1528,6 +1528,8 @@ impl RegionVarBindings {
loop;
}
debug!("ConcreteFailure: !(sub <= sup): sub=%?, sup=%?",
sub, sup);
errors.push(ConcreteFailure(origin, sub, sup));
}
}

View File

@ -20,7 +20,7 @@ use middle::typeck::infer::InferCtxt;
use middle::typeck::infer::lattice::CombineFieldsLatticeMethods;
use middle::typeck::infer::lub::Lub;
use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::{SubtypeTrace, Subtype};
use middle::typeck::infer::{TypeTrace, Subtype};
use util::common::{indent, indenter};
use util::ppaux::bound_region_to_str;
@ -37,7 +37,7 @@ impl Combine for Sub {
fn infcx(&self) -> @mut InferCtxt { self.infcx }
fn tag(&self) -> ~str { ~"sub" }
fn a_is_expected(&self) -> bool { self.a_is_expected }
fn trace(&self) -> SubtypeTrace { self.trace }
fn trace(&self) -> TypeTrace { self.trace }
fn sub(&self) -> Sub { Sub(**self) }
fn lub(&self) -> Lub { Lub(**self) }

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:mismatched types
fn main() { let x = if true { 10i } else { 10u }; }
fn main() {
let x = if true { 10i } else { 10u };
//~^ ERROR if and else have incompatible types: expected `int` but found `uint`
}

View File

@ -0,0 +1,52 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we correctly consider the type of `match` to be the LUB
// of the various arms, particularly in the case where regions are
// involved.
pub fn opt_str0<'a>(maybestr: &'a Option<~str>) -> &'a str {
if maybestr.is_none() {
"(none)"
} else {
let s: &'a str = *maybestr.get_ref();
s
}
}
pub fn opt_str1<'a>(maybestr: &'a Option<~str>) -> &'a str {
if maybestr.is_some() {
let s: &'a str = *maybestr.get_ref();
s
} else {
"(none)"
}
}
pub fn opt_str2<'a>(maybestr: &'a Option<~str>) -> &'static str {
if maybestr.is_none() { //~ ERROR mismatched types
"(none)"
} else {
let s: &'a str = *maybestr.get_ref();
s
}
}
pub fn opt_str3<'a>(maybestr: &'a Option<~str>) -> &'static str {
if maybestr.is_some() { //~ ERROR mismatched types
let s: &'a str = *maybestr.get_ref();
s
} else {
"(none)"
}
}
fn main() {}

View File

@ -0,0 +1,55 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we correctly consider the type of `match` to be the LUB
// of the various arms, particularly in the case where regions are
// involved.
pub fn opt_str0<'a>(maybestr: &'a Option<~str>) -> &'a str {
match *maybestr {
Some(ref s) => {
let s: &'a str = *s;
s
}
None => "(none)",
}
}
pub fn opt_str1<'a>(maybestr: &'a Option<~str>) -> &'a str {
match *maybestr {
None => "(none)",
Some(ref s) => {
let s: &'a str = *s;
s
}
}
}
pub fn opt_str2<'a>(maybestr: &'a Option<~str>) -> &'static str {
match *maybestr { //~ ERROR mismatched types
None => "(none)",
Some(ref s) => {
let s: &'a str = *s;
s
}
}
}
pub fn opt_str3<'a>(maybestr: &'a Option<~str>) -> &'static str {
match *maybestr { //~ ERROR mismatched types
Some(ref s) => {
let s: &'a str = *s;
s
}
None => "(none)",
}
}
fn main() {}