How to check that a function always return a value (aka "doesn't fall off the end")? - swift

I'm building a didactic compiler, and I'd like to check if the function will always return a value. I intend to do this in the semantic analysis step (as this is not covered by the language grammar).
Out of all the flow control statements, this didactic language only has if, else, and while statements (so no do while, for, switch cases, etc). Note that else if is also possible. The following are all valid example snippets:
a)
if (condition) {
// non-returning commands
}
return value
b)
if (condition) {
return value
}
return anotherValue
c)
if (condition) {
return value1
} else {
return value2
}
// No return value needed here
I've searched a lot about this but couldn't find a pseudoalgorithm that I could comprehend. I've searched for software path testing, the white box testing, and also other related Stack Overflow questions like this and this.
I've heard that this can be solved using graphs, and also using a stack, but I have no idea how to implement those strategies.
Any help with pseudocode would be very helpful!
(and if it matters, I'm implementing my compiler in Swift)

If you have a control flow graph, checking that a function always returns is as easy as checking that the implicit return at the end of the function is unreachable. So since there are plenty of analyses and optimizations where you'll want a CFG, it would not be a bad idea to construct one.
That said, even without a control flow graph, this property is pretty straight forward to check assuming some common restrictions (specifically that you're okay with something like if(cond) return x; if(!cond) return y; being seen as falling of the end even though it's equivalent to if(cond) return x; else return y;, which would be allowed). I also assume there's no goto because you didn't list it in your list of control flow statements (I make no assumptions about break and continue because those only appear within loops and loops don't matter).
We just need to consider the cases of what a legal block (i.e. one that always reaches a return) would look like:
So an empty block would clearly not be allowed because it can't reach a return if it's empty. A block that directly (i.e. not inside an if or loop) contains a return would be allowed (and if it isn't at the end of the block, everything after the return in the block would be unreachable, which you might also want to turn into an error or warning).
Loops don't matter. That is, if your block contains a loop, it still has to have a return outside of the loop even if the loop contains a return because the loop condition may be false, so there's no need for us to even check what's inside the loop. This wouldn't be true for do-while loops, but you don't have those.
If the block directly contains an if with an else and both the then-block and the else-block always reach a return, this block also always reaches a return. In that case, everything after the if-else is unreachable. Otherwise the if doesn't matter just like loops.
So in pseudo code that would be:
alwaysReturns( {} ) = false
alwaysReturns( {return exp; ...rest} ) = true
alwaysReturns( { if(exp) thenBlock else elseBlock; ...rest}) =
(alwaysReturns(thenBlock) && alwaysReturns(elseBlock)) || alwaysReturns(rest)
alwaysReturns( {otherStatement; ...rest} ) = alwaysReturns(rest)

So, after 5 hours thinking how to implement this, I came up with a decent solution (at least I haven't been able to break it so far). I actually spent most of the time browsing the web (with no luck) than actually thinking about the problem and trying to solve it on my own.
Below is my implementation (in Swift 4.2, but the syntax is fairly easy to pick up), using a graph:
final class SemanticAnalyzer {
private var currentNode: Node!
private var rootNode: Node!
final class Node {
var nodes: [Node] = []
var returnsExplicitly = false
let parent: Node?
var elseNode: Node!
var alwaysReturns: Bool { return returnsExplicitly || elseNode?.validate() == true }
init(parent: Node?) {
self.parent = parent
}
func validate() -> Bool {
if alwaysReturns {
return true
} else {
return nodes.isEmpty ? false : nodes.allSatisfy { $0.alwaysReturns }
}
}
}
/// Initializes the components of the semantic analyzer.
func startAnalyzing() {
rootNode = Node(parent: nil)
currentNode = rootNode
}
/// Execute when an `if` statement is found.
func handleIfStatementFound() {
let ifNode = Node(parent: currentNode)
let elseNode = Node(parent: currentNode)
// Assigning is not necessary if the current node returns explicitly.
// But assigning is not allowed if the else node always returns, so we check if the current node always returns.
if !currentNode.alwaysReturns {
currentNode.elseNode = elseNode
}
currentNode.nodes += [ ifNode, elseNode ]
currentNode = ifNode
}
/// Execute when an `else` statement is found.
func handleElseStatementFound() {
currentNode = currentNode.elseNode
}
/// Execute when a branch scope is closed.
func handleBranchClosing() {
currentNode = currentNode.parent! // If we're in a branch, the parent node is never nil
}
/// Execute when a function return statement is found.
func handleReturnStatementFound() {
currentNode.returnsExplicitly = true
}
/// Determine whether the function analyzed always returns a value.
///
/// - Returns: whether the root node validates.
func validate() -> Bool {
return rootNode.validate()
}
}
Basically what it does is:
When it finds an if statement is create 2 new nodes and point the current node to both of them (as in a binary tree node).
When the else statement is found, we just switch the current node to the else node created previously in the if statement.
When a branch is closed (e.g. in an if statement's } character), it switches the current node to the parent node.
When it finds a function return statement, it can assume that the current node will always have a return value.
Finally, to validate a node, either the node has an explicit return value, or all of the nodes must be valid.
This works with nested if/else statements, as well as branches without return values at all.

Related

Swift + Not Deleting Current Item in Single Linked List

I have a linked list that is not deleting the current item based off the inserted value which in the case below is the value of 5.0. I want to delete 5.0 from my inserted values but the console is still printing out all inserted values of 5.0, 8.0, and 10.0. I tried changing the conditions in func delete() and tried several changes in the block of code that is executed in func delete() as well. I suspect something is backwards with head?.nextNodeAddress = head?.nextNodeAddress inside func delete(). Also, does the current value actually get deleted in a linked list when setting head (e.g. pointer) to the next node address or does it just overlook the current node value that matches the inserted value?
Note: I did not include func insert(value:) because it did not seem to pertain to the issue. The value insert as expected.
//Node Class:
class Node {
let value: Double
var nextNodeAddress: Node?
init(value: Double, next: Node?) {
self.value = value
self.nextNodeAddress = next
}
}
//Linked List
class LinkedList {
var head: Node?
func delete(value: Double) {
if head?.value != value {
head?.nextNodeAddress = head?.nextNodeAddress
}
}
func displayListItems() {
var current = head
while current != nil {
print(current?.value ?? "")
current = current?.nextNodeAddress
}
}
}
let boltSize = LinkedList()
boltSize.insert(value: 5.0)
boltSize.insert(value: 8.0)
boltSize.insert(value: 10.0)
boltSize.delete(value: 5.0)
boltSize.displayListItems()
As Rob stated above, it's best to add your insert function as well so that we can rule out any bugs in the insert.
However, I does not seem like the delete function does anything.
Let's trace it.
After your three inserts your linked list looks like this assuming your insert works fine.
node(5.0) -> node(8.0) -> node(10.0) and Head is pointing to node(5.0)
Then you call boltSize.delete(value: 5.0)
and let's look at what the function does
func delete(value: Double) {
if head?.value != value {
head?.nextNodeAddress = head?.nextNodeAddress
}
}
You pass the function 5.0
The it compares Head.value != 5.0 which is 5.0 != 5.0 - false
So your next line of code does not run anyways and nothing gets deleted
Let's take another example, say you want to delete 8.0
You pass the function 8.0
The it compares Head.value != 5.0 which is 5.0 != 8.0 - true
So inside the if block head?.nextNodeAddress = head?.nextNodeAddress - Head's next node address is Node(8.0) and this line keeps it Node(8.0)
The function ends
So as you can see two issues:
There is no deletion happening
Your delete code does not have a loop to reach the correct node to delete as it ends after one iteration and comparison with the head
This is how I would approach the deletion and actually is similar to what you did in your display function:
func delete(_ value: Int)
{
// Check if head itself has the data to be deleted
if head?.value == value
{
// The new head is the node after head
head = head?.nextNodeAddress
// We can exit, nothing more to do
return
}
// Create a temp node pointer that will iterate through
// your linked list starting starting from the head
var currentNode = head
// Loop through the list till you reach the end
while currentNode?.nextNodeAddress != nil
{
// Check if the next node has the value we want to delete
if currentNode?.nextNodeAddress?.value == value
{
// Retrieve the next node
let nextNode = currentNode?.nextNodeAddress
// Assign current node's next to next node's next
// This way, there is no link to the next node anymore
currentNode?.nextNodeAddress = nextNode?.nextNodeAddress
// The delete is complete and we can exit
return
}
// Since the node was not found in this iteration, advance
// the pointer
currentNode = currentNode?.nextNodeAddress
}
}
This is probably more verbose way to clarify what is going on, you can feel free to shorten it as you see fit.

Confused about Optional vs Default value -1

We are working in a Swift project. A function was there like,
fun getSleepAmmount() -> Int {
// calculate sleep time
// return value when valid
// else
return -1
}
My team member prefers the above function where caller needs to check with -1 which is not I am comfortable with. My suggestion is to redesign with nil return (although callers still need to check nullability) like,
fun getSleepAmmount() -> Int? {
// calculate sleep time
// return value when valid
// else
return nil
}
But my colleagues do not want to redesign. Which version of the functions is cleaner and why?
Obviously, nil is much cleaner. Because of -1 means nothing. This is just a magic word. It is difficult to support, refactor and handle this case.
Returning nil is a better solution instead of using any garbage or default value.
Returning any default value may in future clash with the actual result.
Also, there might be other developers dealing with the same code. So, using nil will have a better explanation than using -1.
Second is the better as youll do
if let v = getSleepAmmount() {}
But with First
let v = getSleepAmmount()
if v > 0 {}
Returning nil means this isn't a valid return while -1 may mean another thing that will be miss-understood by a new developer that checks the code
If there is code in the caller that should only run if there is a valid sleep amount, then optionals is the better and clearer way to go. This is exactly what guard let and if let are designed for:
guard let sleepAmount = getSleepAmount() { else return }
// do something with sleepAmount
An even better way would be to throw an error inside the function:
func getSleepAmmount() throws -> Int {
// calculate sleep time
// return when valid
// else
throw InvalidSleepAmount
}
Then
do {
let sleepAmount = try getSleepAmount()
// do something with it
} catch InvalidSleepAmount {
// error processing
}
(If you want, your function could throw different errors so the caller gets to know why the sleep amount is invalid, SleepTooShort, SleepTooLong, NoSleep etc)

Impossible Index of of Range crash

In application I am working on, I have to take one element of the array by specifying element's index in the array, like this array[index].
It's a simple UICollectionView which is populated with items I am getting from the array.
In order to guard against Index out of range exception, I am doing this:
guard index < array.count else { return }
return array[index]
Even though I have this guard, I got an Index out of range exception on the array[index] line (but not always).
I don't know how this can happen.
I have even added another check:
extension Collection where Indices.Iterator.Element == Index {
subscript (optional index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
So I am doing this (this is the actual code snippet from the application):
...
guard let section = chatSections[optional: indexPath.section] else {
return nil
}
guard indexPath.item < section.itemViewModels.count else {
return nil
}
return section.itemViewModels[optional: indexPath.item]
It doesn't always happen, but sometimes I get the Index out of Range exception there.
I was debugging most of the day, trying to figure out conditions when crash happens so I might be able to figure out why, but it seems to happen randomly.
Does anyone have any idea how is this possible? Did anyone encounter this kind of issue?
Are you, by any chance, doing any of your updating from a background thread/queue? If so, make sure your UI interactions are done on the main thread/queue and that you aren't changing the array contents behind your UI's back.
That is, if you're changing the array contents in one queue and trying to update your UI while this is happening, your guard statement could be passing just before the array is modified elsewhere, then by the time the rest of your UI-interacting code executes, the index my no longer be valid.
Without a more complete picture, it's hard to say what's going on, but all these bounds checks you're adding in order to guard against mysteriously-changing array indexes are a big clue to multithreading shenanigans.
I believe you have multiple access to the your data source array (multiple thread try to access add/remove from the array).
to overcome this you should use something to enforce synchronization while accessing your array, there is multiple approce using semaphore or DispatchGroup.
I would recommend to use semaphore since the array is considered as shared resource, example:
private let semaphore = DispatchSemaphore(value: 1)
private var _array:[Item] = []
var array:[Item] {
get {
semaphore.wait()
let result = self._allMessages
defer {
semaphore.signal()
}
return result
}
set {
semaphore.wait()
self. _array = newValue
semaphore.signal()
}
}
and use the array variable to access the array data source not the private _array.

Swift function compiler error 'missing return'

I've been trying to get this function to return a Bool value but I don't understand why i'm getting the error "missing return in a function expected to return 'Bool'. I've been looking around online and tried different things but I can't seem to find a solution. Any help would be appreciated!
func trueSquare(a:[Int], b:[Int]) -> Bool {
for i in b[0]...b.endIndex {
if b[i] == a[i]*a[i] {
return true
}
else {
return false
}
}
}
EDIT: I have changed the loop to for i in 0...(b.count - 1) but I am still getting the same error even when I call the function with a and b both having the same numbers of elements.
What if your array has no element? Then for each loop never runs and then your method returns nothing which is obviously wrong. So you need to return value even outside of the loop.
But, your logic is bad. You're returning boolean value depending on if just first element from b is equal to a*a.
So, logic should be something like: if every element meets the condition, then return true, otherwise, return false. To achieve this, in Swift 4.2+ you can use method allSatisfy
func trueSquare(a:[Int], b:[Int]) -> Bool {
guard a.count == b.count else { return false } // if arrays have different number of elements, return false
return a.enumerated().allSatisfy {$0.element * $0.element == b[$0.offset]}
}
I suspect the compiler requires a return value for the case when the loop is not executed at all.
Now, a ClosedRange can never be empty, so b[0]...b.endIndex won't ever be empty (if it results in an empty or invalid range, the code would crash), but the compiler is not smart enough to know that.
PS: Are you sure b[0]...b.endIndex is actually the sequence you want to loop over. This creates a range from the first element of b to the endIndex of b. That doesn't make any sense to me.
Your function does not handle case where b is an empty array.
You need to define what you want the return value to be for such case, because your loop will be skipped when b is an empty array.
Secondly, your logic is also incomplete, because if the condition is good for i==0, you immediately return true, without checking the rest of the items.
Thirdly, you probably want to make sure a and b have same length.
So here is what your function should look like:
func trueSquare(a:[Int], b:[Int]) -> Bool {
if a.count != b.count {
return false
}
for i in 0..<b.count {
if b[i] != a[i]*a[i] {
return false
}
}
return true
}

Need clarification about Linked Lists using Swift

I am looking at some code implementing a LinkedList using Swift, and I need someone to clarify for me a couple of things. First off, here is the code below for my LinkedList class, and my function to remove a Node from the list:
public class Node<T> {
var value:T
var next: Node?
}
public class LinkedList<T:Equatable> {
private var head = Node<T>()
func remove(at index: Int) {
if ((index < 0 || (index > (self.count - 1)) || (head.value == nil)) {
print("link does not exist.")
return
}
var current: Node? = head
var previous: Node<T>?
var listIndex:Int = 0
if index == 0 {
current = current?.next
head = current!
return
}
while current != nil {
if listIndex == index {
previous!.next = current?.next
current = nil
break
}
previous = current
current = current?.next
listIndex += 1
}
}
}
When it comes to removing an object from the list, in the following code block:
if index == 0 {
current = current?.next
head = current!
return
}
My question related to the above code block is, I realize that I move the current pointer down one node in the list, and then change the reference of the head pointer to point to the node that current is now pointing to, however, what happens to the node that originally was pointing to current.next? There are no references to it, but IT still has a reference to the second node in the list, correct? How is this node removed completely if it still has a reference to the next node in the list? I have the same question for the following block later on, when the node is found in the middle of the list:
if listIndex == index {
previous!.next = current?.next
current = nil
break
}
Please note: I am not in school and this is not homework. I'm studying algorithms on my own to review the concepts I learned originally in Java, and apply them to Swift.
You correct that after the index == 0 block executes, there will be no references to the original head. This means that for the rest of the program you cannot do anything with that node. It is desirable that the memory allocated for the node should be reclaimed so it can be used for other objects (otherwise you'll have a completely useless node that is wasting memory).
Swift uses automatic reference counting, so it detects when there a no references to an object and reclaims the memory. The memory will be reclaimed without you having to do anything special.
How is this node removed completely if it still has a reference to the next node in the list?
The fact that the original head has a reference to another node does not prevent it from being reclaimed by the system. The program can't query which objects reference that other node, so it makes no difference to the rest of the program if the original head is reclaimed or not (other than the extra memory that becomes available).