Issues with ExpressibleByStringLiteral, ExpressibleByIntegerLiteral in Swift 5.1

I am developing my first project with Swift. Yesterday I attempted to add protocols ExpressibleByStringLiteral, ExpressibleByIntegerLiteral to a numerical class. I could get the changes to compile but could not get an object to instantiate with them and would get a fatal error when I ran the project. I could not even get the debugger to break in the two inits. Then I happen to discover the issue. If I instantiate an object of this class by any init method that are not one of these two protocols then the protocols will work fine from then on. In other words I can't use either of these two protocols first to instantiate an object of the class. Both protocol inits work perfectly when they are eventually called.

Is this a bug or am I missing something to enable the protocols? It looks like both of these protocols are depending on the fact that any instance of the class exists and has been instantiated.

Example:

This code segment works:

// all variables are APMInt
ll = APMInt(I64: 20) // instantiation that doesn't use protocol ExpressibleByStringLiteralor or ExpressibleByIntegerLiteral
ii = 3
jj = 4
print(ii * jj)
ii = "99"
jj = "88"
print (ii - jj)
kk = ii * jj

This code fails:

// all variables are APMInt
ii = 3
jj = 4
print(ii * jj)
ii = "99"
jj = "88"
print (ii - jj)
kk = ii * jj

Result:
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7fff5965dea8) on line with print(ii * jj) because the objects ii and jj are garbage since they did not really go through their inits

Please post the code for APMInt, including the initializers for the protocols.

Also, when you post code on this forum, please put triple back-ticks (```) on the lines before and after it, so it shows up formatted properly.

Okay, the below is a stub of the class showing the inits and protocols. Not executable without a lot of the other code.

//
//  APMInt_Class.swift
//

import Foundation

let apmint_base_bits: Int = 30
let apmint_base: Int = 1 << apmint_base_bits
let apmint_lo_mask: Int = apmint_base - 1
let apmint_hi_mask: Int = ~apmint_lo_mask

// APInt Constants
let ZERO: APMInt = APMInt(I64: 0)
let ONE: APMInt = APMInt(I64: 1)
let TWO: APMInt = APMInt(I64: 2)
let THREE: APMInt = APMInt(I64: 3)
let MINUSONE: APMInt = APMInt(I64: -1)

// used to capture math errors
var apmint_error_status: Int = 0
var apmint_error_messages: [String] = [ ""
    , "Divide by Zero"
    , "Mod by Zero"
    , "Bad Bit Number"
    , "Square Root of Negative"]


extension APMInt: CustomStringConvertible {
    public var description: String {
        return self.to_string()
    }
}

class APMInt: ExpressibleByStringLiteral, ExpressibleByIntegerLiteral {
    
    var sign: Int     // -1 for negative, 0 or zero, 1 for positive
    var words: [Int]
    
    public required init(integerLiteral:Int) {
        if integerLiteral == 0 {
            self.sign = 0
            self.words = [0]
            return
        }
        
        var input: Int
        
        if integerLiteral < 0 {
            input = -integerLiteral
            self.sign = -1
        } else {
            input = integerLiteral
            self.sign = 1
        }
        
        if input <= apmint_lo_mask {
            self.words = [input]
        } else {
            self.words = [input & apmint_lo_mask] + [input >> apmint_base_bits]
        }
    }

    public required init(stringLiteral:String){
        var result = APMInt(I64: 0)
        var digit_found: Bool = false
        var sign: Int = 1
        for chars in stringLiteral.utf8 {
            if chars > 47 && chars < 58 {
                digit_found = true
                result = (result * 10) + Int(chars - 48)
            } else if chars == 45 {      // minus sign
                sign = -1
            } else if chars == 43 {      // plus sign
                sign = 1
            }
        }
        if digit_found == false || result.sign == 0 {
            self.sign = 0
            self.words = [0]
        } else {
            self.sign = sign
            self.words = result.words
        }
    }
    
    // init from Int
    init (I64: Int) {
        
        if I64 == 0 {
            self.sign = 0
            self.words = [0]
            return
        }
        
        var input: Int
        
        if I64 < 0 {
            input = -I64
            self.sign = -1
        } else {
            input = I64
            self.sign = 1
        }
        
        if input <= apmint_lo_mask {
            self.words = [input]
        } else {
            self.words = [input & apmint_lo_mask] + [input >> apmint_base_bits]
        }
    }
    
    // init from string
    required init (Text: String) {
        var result = APMInt(I64: 0)
        var digit_found: Bool = false
        var sign: Int = 1
        for chars in Text.utf8 {
            if chars > 47 && chars < 58 {
                digit_found = true
                result = (result * 10) + Int(chars - 48)
            } else if chars == 45 {      // minus sign
                sign = -1
            } else if chars == 43 {      // plus sign
                sign = 1
            }
        }
        if digit_found == false || result.sign == 0 {
            self.sign = 0
            self.words = [0]
        } else {
            self.sign = sign
            self.words = result.words
        }
    }
    
    
    func to_string() -> String {
        if self.words.count == 0 || self.sign == 0 {return "0"}
        var out_string: String = ""
        var temp: APMInt = self.abs()
        var digit: Int
        while temp.sign != 0 {
            digit = temp % 10
            do { temp = temp / 10}
            out_string = String(UnicodeScalar(UInt8(digit + 48))) + out_string
        }
        if self.sign == -1 {
            out_string = "-" + out_string
        }
        return out_string
    }
}
Terms of Service

Privacy Policy

Cookie Policy