Skip to content

Commit d104dab

Browse files
committed
Make some NullablePointer enums FFI-compatible with the base pointer type.
1 parent 25c5422 commit d104dab

File tree

2 files changed

+94
-14
lines changed

2 files changed

+94
-14
lines changed

src/librustc/middle/trans/adt.rs

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ pub enum Repr {
9191
* field is known to be nonnull due to its type; if that field is null, then
9292
* it represents the other case, which is inhabited by at most one value
9393
* (and all other fields are undefined/unused).
94+
* If the case with the nullable pointer has a single field then we don't
95+
* wrap it in a struct and instead just deal with it directly as a pointer.
9496
*
9597
* For example, `std::option::Option` instantiated at a safe pointer type
9698
* is represented such that `None` is a null pointer and `Some` is the
@@ -413,8 +415,11 @@ pub fn incomplete_type_of(cx: &CrateContext, r: &Repr, name: &str) -> Type {
413415
}
414416
pub fn finish_type_of(cx: &CrateContext, r: &Repr, llty: &mut Type) {
415417
match *r {
416-
CEnum(..) | General(..) => { }
417-
Univariant(ref st, _) | NullablePointer{ nonnull: ref st, .. } =>
418+
CEnum(..) | General(..) => {
419+
}
420+
NullablePointer { nonnull: ref st, .. } if st.fields.len() == 1 => {
421+
}
422+
Univariant(ref st, _) | NullablePointer { nonnull: ref st, .. } =>
418423
llty.set_struct_body(struct_llfields(cx, st, false).as_slice(),
419424
st.packed)
420425
}
@@ -423,7 +428,14 @@ pub fn finish_type_of(cx: &CrateContext, r: &Repr, llty: &mut Type) {
423428
fn generic_type_of(cx: &CrateContext, r: &Repr, name: Option<&str>, sizing: bool) -> Type {
424429
match *r {
425430
CEnum(ity, _, _) => ll_inttype(cx, ity),
426-
Univariant(ref st, _) | NullablePointer{ nonnull: ref st, .. } => {
431+
NullablePointer { nonnull: ref st, .. } if st.fields.len() == 1 => {
432+
if sizing {
433+
type_of::sizing_type_of(cx, *st.fields.get(0))
434+
} else {
435+
type_of::type_of(cx, *st.fields.get(0))
436+
}
437+
}
438+
Univariant(ref st, _) | NullablePointer { nonnull: ref st, .. } => {
427439
match name {
428440
None => {
429441
Type::struct_(cx, struct_llfields(cx, st, sizing).as_slice(),
@@ -498,7 +510,7 @@ pub fn trans_switch(bcx: &Block, r: &Repr, scrutinee: ValueRef)
498510
CEnum(..) | General(..) => {
499511
(_match::switch, Some(trans_get_discr(bcx, r, scrutinee, None)))
500512
}
501-
NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, .. } => {
513+
NullablePointer { nonnull: ref nonnull, nndiscr, ptrfield, .. } => {
502514
(_match::switch, Some(nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee)))
503515
}
504516
Univariant(..) => {
@@ -528,7 +540,7 @@ pub fn trans_get_discr(bcx: &Block, r: &Repr, scrutinee: ValueRef, cast_to: Opti
528540
val = C_u8(bcx.ccx(), 0);
529541
signed = false;
530542
}
531-
NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, .. } => {
543+
NullablePointer { nonnull: ref nonnull, nndiscr, ptrfield, .. } => {
532544
val = nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee);
533545
signed = false;
534546
}
@@ -541,8 +553,12 @@ pub fn trans_get_discr(bcx: &Block, r: &Repr, scrutinee: ValueRef, cast_to: Opti
541553

542554
fn nullable_bitdiscr(bcx: &Block, nonnull: &Struct, nndiscr: Disr, ptrfield: uint,
543555
scrutinee: ValueRef) -> ValueRef {
556+
let llptr = if nonnull.fields.len() == 1 {
557+
Load(bcx, scrutinee)
558+
} else {
559+
Load(bcx, GEPi(bcx, scrutinee, [0, ptrfield]))
560+
};
544561
let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
545-
let llptr = Load(bcx, GEPi(bcx, scrutinee, [0, ptrfield]));
546562
let llptrty = type_of::type_of(bcx.ccx(), *nonnull.fields.get(ptrfield));
547563
ICmp(bcx, cmp, llptr, C_null(llptrty))
548564
}
@@ -590,7 +606,7 @@ pub fn trans_case<'a>(bcx: &'a Block<'a>, r: &Repr, discr: Disr)
590606
Univariant(..) => {
591607
bcx.ccx().sess().bug("no cases for univariants or structs")
592608
}
593-
NullablePointer{ .. } => {
609+
NullablePointer { .. } => {
594610
assert!(discr == 0 || discr == 1);
595611
_match::single_result(Result::new(bcx, C_i1(bcx.ccx(), discr != 0)))
596612
}
@@ -621,9 +637,13 @@ pub fn trans_start_init(bcx: &Block, r: &Repr, val: ValueRef, discr: Disr) {
621637
Univariant(..) => {
622638
assert_eq!(discr, 0);
623639
}
624-
NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, .. } => {
640+
NullablePointer { nonnull: ref nonnull, nndiscr, ptrfield, .. } => {
625641
if discr != nndiscr {
626-
let llptrptr = GEPi(bcx, val, [0, ptrfield]);
642+
let llptrptr = if nonnull.fields.len() == 1 {
643+
val
644+
} else {
645+
GEPi(bcx, val, [0, ptrfield])
646+
};
627647
let llptrty = type_of::type_of(bcx.ccx(),
628648
*nonnull.fields.get(ptrfield));
629649
Store(bcx, C_null(llptrty), llptrptr)
@@ -651,7 +671,7 @@ pub fn num_args(r: &Repr, discr: Disr) -> uint {
651671
st.fields.len() - (if dtor { 1 } else { 0 })
652672
}
653673
General(_, ref cases) => cases.get(discr as uint).fields.len() - 1,
654-
NullablePointer{ nonnull: ref nonnull, nndiscr,
674+
NullablePointer { nonnull: ref nonnull, nndiscr,
655675
nullfields: ref nullfields, .. } => {
656676
if discr == nndiscr { nonnull.fields.len() } else { nullfields.len() }
657677
}
@@ -675,10 +695,15 @@ pub fn trans_field_ptr(bcx: &Block, r: &Repr, val: ValueRef, discr: Disr,
675695
General(_, ref cases) => {
676696
struct_field_ptr(bcx, cases.get(discr as uint), val, ix + 1, true)
677697
}
678-
NullablePointer{ nonnull: ref nonnull, nullfields: ref nullfields,
698+
NullablePointer { nonnull: ref nonnull, nullfields: ref nullfields,
679699
nndiscr, .. } => {
680700
if discr == nndiscr {
681-
struct_field_ptr(bcx, nonnull, val, ix, false)
701+
if nonnull.fields.len() == 1 {
702+
assert_eq!(ix, 0);
703+
val
704+
} else {
705+
struct_field_ptr(bcx, nonnull, val, ix, false)
706+
}
682707
} else {
683708
// The unit-like case might have a nonzero number of unit-like fields.
684709
// (e.g., Result or Either with () as one side.)
@@ -759,7 +784,15 @@ pub fn trans_const(ccx: &CrateContext, r: &Repr, discr: Disr,
759784
let contents = build_const_struct(ccx, st, vals);
760785
C_struct(ccx, contents.as_slice(), st.packed)
761786
}
762-
NullablePointer{ nonnull: ref nonnull, nndiscr, .. } => {
787+
NullablePointer { nonnull: ref st, nndiscr, .. } if st.fields.len() == 1 => {
788+
if discr == nndiscr {
789+
assert_eq!(vals.len(), 1);
790+
vals[0]
791+
} else {
792+
C_null(type_of::sizing_type_of(ccx, *st.fields.get(0)))
793+
}
794+
}
795+
NullablePointer { nonnull: ref nonnull, nndiscr, .. } => {
763796
if discr == nndiscr {
764797
C_struct(ccx, build_const_struct(ccx,
765798
nonnull,
@@ -867,7 +900,15 @@ pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef)
867900
}
868901
}
869902
Univariant(..) => 0,
870-
NullablePointer{ nndiscr, ptrfield, .. } => {
903+
NullablePointer { nonnull: ref st, nndiscr, .. } if st.fields.len() == 1 => {
904+
if is_null(val) {
905+
/* subtraction as uint is ok because nndiscr is either 0 or 1 */
906+
(1 - nndiscr) as Disr
907+
} else {
908+
nndiscr
909+
}
910+
}
911+
NullablePointer { nndiscr, ptrfield, .. } => {
871912
if is_null(const_struct_field(ccx, val, ptrfield)) {
872913
/* subtraction as uint is ok because nndiscr is either 0 or 1 */
873914
(1 - nndiscr) as Disr
@@ -891,6 +932,10 @@ pub fn const_get_field(ccx: &CrateContext, r: &Repr, val: ValueRef,
891932
CEnum(..) => ccx.sess().bug("element access in C-like enum const"),
892933
Univariant(..) => const_struct_field(ccx, val, ix),
893934
General(..) => const_struct_field(ccx, val, ix + 1),
935+
NullablePointer { nonnull: ref st, .. } if st.fields.len() == 1 => {
936+
assert_eq!(ix, 0);
937+
val
938+
}
894939
NullablePointer{ .. } => const_struct_field(ccx, val, ix)
895940
}
896941
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// #11303, #11040:
12+
// This would previously crash on i686 linux due to abi differences
13+
// between returning an Option<T> and T, where T is a non nullable
14+
// pointer.
15+
// If we have an enum with two variants such that one is zero sized
16+
// and the other contains a nonnullable pointer, we don't use a
17+
// separate discriminant. Instead we use that pointer field to differentiate
18+
// between the 2 cases.
19+
// Also, if the variant with the nonnullable pointer has no other fields
20+
// then we simply express the enum as just a pointer and not wrap it
21+
// in a struct.
22+
23+
use std::mem;
24+
25+
#[inline(never)]
26+
extern "C" fn foo<'a>(x: &'a int) -> Option<&'a int> { Some(x) }
27+
28+
static FOO: int = 0xDEADBEE;
29+
30+
pub fn main() {
31+
unsafe {
32+
let f: extern "C" fn<'a>(&'a int) -> &'a int = mem::transmute(foo);
33+
assert_eq!(*f(&FOO), FOO);
34+
}
35+
}

0 commit comments

Comments
 (0)