Skip to content

Commit

Permalink
Auto merge of #37112 - pnkfelix:fix-issue-36744, r=arielb1
Browse files Browse the repository at this point in the history
Fix ICE: inject bitcast if types mismatch for invokes/calls/stores

Fix ICE: inject bitcast if types mismatch for invokes/calls

Fix #36744
  • Loading branch information
bors authored Oct 17, 2016
2 parents 07b86d0 + 0562654 commit ce31626
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 17 deletions.
73 changes: 56 additions & 17 deletions src/librustc_trans/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use value::Value;
use util::nodemap::FnvHashMap;
use libc::{c_uint, c_char};

use std::borrow::Cow;
use std::ffi::CString;
use std::ptr;
use syntax_pos::Span;
Expand Down Expand Up @@ -175,8 +176,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.collect::<Vec<String>>()
.join(", "));

check_call("invoke", llfn, args);

let args = self.check_call("invoke", llfn, args);
let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(ptr::null_mut());

unsafe {
Expand Down Expand Up @@ -543,6 +543,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
debug!("Store {:?} -> {:?}", Value(val), Value(ptr));
assert!(!self.llbuilder.is_null());
self.count_insn("store");
let ptr = self.check_store(val, ptr);
unsafe {
llvm::LLVMBuildStore(self.llbuilder, val, ptr)
}
Expand All @@ -552,6 +553,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
debug!("Store {:?} -> {:?}", Value(val), Value(ptr));
assert!(!self.llbuilder.is_null());
self.count_insn("store.volatile");
let ptr = self.check_store(val, ptr);
unsafe {
let insn = llvm::LLVMBuildStore(self.llbuilder, val, ptr);
llvm::LLVMSetVolatile(insn, llvm::True);
Expand All @@ -562,6 +564,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, order: AtomicOrdering) {
debug!("Store {:?} -> {:?}", Value(val), Value(ptr));
self.count_insn("store.atomic");
let ptr = self.check_store(val, ptr);
unsafe {
let ty = Type::from_ref(llvm::LLVMTypeOf(ptr));
let align = llalign_of_pref(self.ccx, ty.element_type());
Expand Down Expand Up @@ -857,8 +860,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.collect::<Vec<String>>()
.join(", "));

check_call("call", llfn, args);

let args = self.check_call("call", llfn, args);
let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(ptr::null_mut());

unsafe {
Expand Down Expand Up @@ -1100,10 +1102,32 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
llvm::LLVMRustBuildAtomicFence(self.llbuilder, order, scope);
}
}
}

fn check_call(typ: &str, llfn: ValueRef, args: &[ValueRef]) {
if cfg!(debug_assertions) {
/// Returns the ptr value that should be used for storing `val`.
fn check_store<'b>(&self,
val: ValueRef,
ptr: ValueRef) -> ValueRef {
let dest_ptr_ty = val_ty(ptr);
let stored_ty = val_ty(val);
let stored_ptr_ty = stored_ty.ptr_to();

assert_eq!(dest_ptr_ty.kind(), llvm::TypeKind::Pointer);

if dest_ptr_ty == stored_ptr_ty {
ptr
} else {
debug!("Type mismatch in store. \
Expected {:?}, got {:?}; inserting bitcast",
dest_ptr_ty, stored_ptr_ty);
self.bitcast(ptr, stored_ptr_ty)
}
}

/// Returns the args that should be used for a call to `llfn`.
fn check_call<'b>(&self,
typ: &str,
llfn: ValueRef,
args: &'b [ValueRef]) -> Cow<'b, [ValueRef]> {
let mut fn_ty = val_ty(llfn);
// Strip off pointers
while fn_ty.kind() == llvm::TypeKind::Pointer {
Expand All @@ -1115,16 +1139,31 @@ fn check_call(typ: &str, llfn: ValueRef, args: &[ValueRef]) {

let param_tys = fn_ty.func_params();

let iter = param_tys.into_iter()
.zip(args.iter().map(|&v| val_ty(v)));
for (i, (expected_ty, actual_ty)) in iter.enumerate() {
if expected_ty != actual_ty {
bug!("Type mismatch in function call of {:?}. \
Expected {:?} for param {}, got {:?}",
Value(llfn),
expected_ty, i, actual_ty);
let all_args_match = param_tys.iter()
.zip(args.iter().map(|&v| val_ty(v)))
.all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);

if all_args_match {
return Cow::Borrowed(args);
}

let casted_args: Vec<_> = param_tys.into_iter()
.zip(args.iter())
.enumerate()
.map(|(i, (expected_ty, &actual_val))| {
let actual_ty = val_ty(actual_val);
if expected_ty != actual_ty {
debug!("Type mismatch in function call of {:?}. \
Expected {:?} for param {}, got {:?}; injecting bitcast",
Value(llfn),
expected_ty, i, actual_ty);
self.bitcast(actual_val, expected_ty)
} else {
actual_val
}
})
.collect();

}
}
return Cow::Owned(casted_args);
}
}
32 changes: 32 additions & 0 deletions src/test/run-pass/issue-36744-bitcast-args-if-needed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2016 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.

// This tests for an ICE (and, if ignored, subsequent LLVM abort) when
// a lifetime-parametric fn is passed into a context whose expected
// type has a differing lifetime parameterization.

struct A<'a> {
_a: &'a i32,
}

fn call<T>(s: T, functions: &Vec<for <'n> fn(&'n T)>) {
for function in functions {
function(&s);
}
}

fn f(a: &A) { println!("a holds {}", a._a); }

fn main() {
let a = A { _a: &10 };

let vec: Vec<for <'u,'v> fn(&'u A<'v>)> = vec![f];
call(a, &vec);
}
22 changes: 22 additions & 0 deletions src/test/run-pass/issue-36744-without-calls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2016 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.

// Tests for an LLVM abort when storing a lifetime-parametric fn into
// context that is expecting one that is not lifetime-parametric
// (i.e. has no `for <'_>`).

pub struct A<'a>(&'a ());
pub struct S<T>(T);

pub fn bad<'s>(v: &mut S<fn(A<'s>)>, y: S<for<'b> fn(A<'b>)>) {
*v = y;
}

fn main() {}

0 comments on commit ce31626

Please sign in to comment.