Skip to content

Commit eeab137

Browse files
committed
Add parent checking to BB methods. Fixes rust-lang#93
1 parent 05c56ec commit eeab137

File tree

2 files changed

+45
-27
lines changed

2 files changed

+45
-27
lines changed

Diff for: src/basic_block.rs

+39-13
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ impl BasicBlock {
137137
if self.get_parent().is_none() {
138138
return None;
139139
}
140-
140+
141141
let bb = unsafe {
142142
LLVMGetNextBasicBlock(self.basic_block)
143143
};
@@ -146,6 +146,7 @@ impl BasicBlock {
146146
}
147147

148148
/// Prepends one `BasicBlock` before another.
149+
/// It returns `Err(())` when either `BasicBlock` has no parent, as LLVM assumes it has a parent.
149150
///
150151
/// # Example
151152
/// ```no_run
@@ -168,13 +169,21 @@ impl BasicBlock {
168169
/// assert_eq!(basic_block2.get_next_basic_block().unwrap(), basic_block1);
169170
/// ```
170171
// REVIEW: What happens if blocks are from different scopes?
171-
pub fn move_before(&self, basic_block: &BasicBlock) {
172+
pub fn move_before(&self, basic_block: &BasicBlock) -> Result<(), ()> {
173+
// This method is UB if the parent no longer exists, so we must check for parent (or encode into type system)
174+
if self.get_parent().is_none() || basic_block.get_parent().is_none() {
175+
return Err(());
176+
}
177+
172178
unsafe {
173179
LLVMMoveBasicBlockBefore(self.basic_block, basic_block.basic_block)
174180
}
181+
182+
Ok(())
175183
}
176184

177185
/// Appends one `BasicBlock` after another.
186+
/// It returns `Err(())` when either `BasicBlock` has no parent, as LLVM assumes it has a parent.
178187
///
179188
/// # Example
180189
/// ```no_run
@@ -197,10 +206,17 @@ impl BasicBlock {
197206
/// assert_eq!(basic_block2.get_next_basic_block().unwrap(), basic_block1);
198207
/// ```
199208
// REVIEW: What happens if blocks are from different scopes?
200-
pub fn move_after(&self, basic_block: &BasicBlock) {
209+
pub fn move_after(&self, basic_block: &BasicBlock) -> Result<(), ()> {
210+
// This method is UB if the parent no longer exists, so we must check for parent (or encode into type system)
211+
if self.get_parent().is_none() || basic_block.get_parent().is_none() {
212+
return Err(());
213+
}
214+
201215
unsafe {
202216
LLVMMoveBasicBlockAfter(self.basic_block, basic_block.basic_block)
203217
}
218+
219+
Ok(())
204220
}
205221

206222
/// Prepends a new `BasicBlock` before this one.
@@ -340,6 +356,7 @@ impl BasicBlock {
340356
}
341357

342358
/// Removes this `BasicBlock` from its parent `FunctionValue`. Does nothing if it has no parent.
359+
/// It returns `Err(())` when it has no parent to remove from, as LLVM assumes it has a parent.
343360
///
344361
/// # Example
345362
/// ```no_run
@@ -364,16 +381,21 @@ impl BasicBlock {
364381
// by taking ownership of self (though BasicBlock's are not uniquely obtained...)
365382
// might have to make some methods do something like -> Result<..., BasicBlock<Orphan>> for BasicBlock<HasParent>
366383
// and would move_before/after make it no longer orphaned? etc..
367-
pub fn remove_from_function(&self) {
384+
pub fn remove_from_function(&self) -> Result<(), ()> {
368385
// This method is UB if the parent no longer exists, so we must check for parent (or encode into type system)
369-
if self.get_parent().is_some() {
370-
unsafe {
371-
LLVMRemoveBasicBlockFromParent(self.basic_block)
372-
}
386+
if self.get_parent().is_none() {
387+
return Err(());
373388
}
389+
390+
unsafe {
391+
LLVMRemoveBasicBlockFromParent(self.basic_block)
392+
}
393+
394+
Ok(())
374395
}
375396

376397
/// Removes this `BasicBlock` completely from memory. This is unsafe because you could easily have other references to the same `BasicBlock`.
398+
/// It returns `Err(())` when it has no parent to delete from, as LLVM assumes it has a parent.
377399
///
378400
/// # Example
379401
/// ```no_run
@@ -393,11 +415,15 @@ impl BasicBlock {
393415
/// }
394416
/// assert!(function.get_basic_blocks().is_empty());
395417
/// ```
396-
// REVIEW: Could potentially be unsafe if there are existing references. Might need a global ref counter
397-
pub unsafe fn delete(self) {
398-
// unsafe {
399-
LLVMDeleteBasicBlock(self.basic_block)
400-
// }
418+
pub unsafe fn delete(self) -> Result<(), ()> {
419+
// This method is UB if the parent no longer exists, so we must check for parent (or encode into type system)
420+
if self.get_parent().is_none() {
421+
return Err(());
422+
}
423+
424+
LLVMDeleteBasicBlock(self.basic_block);
425+
426+
Ok(())
401427
}
402428

403429
/// Obtains the `ContextRef` this `BasicBlock` belongs to.

Diff for: tests/all/test_basic_block.rs

+6-14
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ fn test_basic_block_ordering() {
3333
assert_eq!(basic_blocks[2], basic_block3);
3434
assert_eq!(basic_blocks[3], basic_block4);
3535

36-
basic_block3.move_before(&basic_block2);
37-
basic_block.move_after(&basic_block4);
36+
assert!(basic_block3.move_before(&basic_block2).is_ok());
37+
assert!(basic_block.move_after(&basic_block4).is_ok());
3838

3939
let basic_block5 = basic_block.prepend_basic_block("block5");
4040
let basic_blocks = function.get_basic_blocks();
@@ -62,7 +62,7 @@ fn test_basic_block_ordering() {
6262
assert!(basic_block3.get_previous_basic_block().is_none());
6363

6464
unsafe {
65-
bb4.delete();
65+
assert!(bb4.delete().is_ok());
6666
}
6767

6868
let bb2 = basic_block5.get_previous_basic_block().unwrap();
@@ -153,21 +153,13 @@ fn test_no_parent() {
153153
assert_eq!(basic_block.get_parent().unwrap(), function);
154154
assert_eq!(basic_block.get_next_basic_block().unwrap(), basic_block2);
155155

156-
// TODO: Test if this method is unsafe if parent function was hard deleted
157-
basic_block.remove_from_function();
156+
assert!(basic_block.remove_from_function().is_ok());
158157

159158
assert!(basic_block.get_next_basic_block().is_none());
160159
assert!(basic_block2.get_previous_basic_block().is_none());
161160

162-
// The problem here is that calling the function more than once becomes UB
163-
// for some reason so we have to manually check for the parent function (in the call)
164-
// until we have SubTypes to solve this at compile time by doing something like:
165-
// impl BasicBlock<HasParent> { fn remove_from_function(self) -> BasicBlock<Orphan> }
166-
// though having to take ownership does raise some flags for when you just want to make
167-
// everything borrowable. I'm not sure it's possible to swap in place since they have
168-
// different subtypes
169-
basic_block.remove_from_function();
170-
basic_block.remove_from_function();
161+
assert!(basic_block.remove_from_function().is_err());
162+
assert!(basic_block.remove_from_function().is_err());
171163

172164
assert!(basic_block.get_parent().is_none());
173165
}

0 commit comments

Comments
 (0)