The use of swift macros prevents the execution of other code

41 Views Asked by At

I created a unwrap freestanding macro that unwraps optional values. enter image description here The implementation looks like the following code:

{ [\(raw: argumentName)] in
    guard let \(raw: argumentName) else {
        preconditionFailure("Unexpectedly found nil")
    }
    return \(raw: argumentName)
}()

When applied to actual code, it expands as shown below:

enter image description here

Secondly, I created a unwrapNotEmpty macro that unwraps an optional value and checks whether it's empty: enter image description here

{ [\(raw: argumentName)] in
    let unwrapValue = #unwrap(\(raw: argumentName))
    guard !unwrapValue.isEmpty else {
        preconditionFailure("Unexpectedly found empty")
    }
    return unwrapValue
}()

The expansion looks like this:

![image-20240224185538944](/Users/sujikang/Library/Application Support/typora-user-images/image-20240224185538944.png)

I expected a failure in the unwrapNotEmpty macro when I created an empty string called "greeting" and executed each macro:

let greeting: String? = ""
let unwrap = #unwrap(greeting)
let unwrapNotEmpty = #unwrapNotEmpty(greeting)

print("unwrap: \(unwrap)")
print("unwrapNotEmpty: \(unwrapNotEmpty)")

However, in reality, both print statements at the bottom executed without errors:

unwrap: 
unwrapNotEmpty: 

When I commented out the execution of the #unwrap macro, the failure in the unwrapNotEmpty macro was triggered.

I suspected an issue when macros are executed consecutively. To investigate, I tried using only the unwrap macro without the unwrapNotEmpty macro, keeping the expanded form as it is:

let greeting: String? = ""
let unwrap = #unwrap(greeting)
let unwrapNotEmpty = { [greeting] in
    let unwrapValue = { [greeting] in
        guard let greeting else {
            preconditionFailure("Unexpectedly found nil")
        }
        return greeting
    }()
    guard !unwrapValue.isEmpty else {
        preconditionFailure("Unexpectedly found empty")
    }
    return unwrapValue
}()

print("unwrap: \(unwrap)")
print("unwrapNotEmpty: \(unwrapNotEmpty)")

However, even in this case, both print statements at the bottom executed without errors:

unwrap: 
unwrapNotEmpty: 

I also tried the opposite, where I kept the unwrap macro in its expanded form and used only the unwrapNotEmpty macro:

let greeting: String? = ""
let unwrap = { [greeting] in
    guard let greeting else {
        preconditionFailure("Unexpectedly found nil")
    }
    return greeting
}()
let unwrapNotEmpty = #unwrapNotEmpty(greeting)

print("unwrap: \(unwrap)")
print("unwrapNotEmpty: \(unwrapNotEmpty)")

Still, in this case, both print statements at the bottom executed without errors:

unwrap: 
unwrapNotEmpty: 

When I wrote both macros in their expanded forms, the Unexpectedly found empty error was triggered as expected:

let unwrap = { [greeting] in
    guard let greeting else {
        preconditionFailure("Unexpectedly found nil")
    }
    return greeting
}()
let unwrapNotEmpty = { [greeting] in
    let unwrapValue = { [greeting] in
        guard let greeting else {
            preconditionFailure("Unexpectedly found nil")
        }
        return greeting
    }()
    guard !unwrapValue.isEmpty else {
        preconditionFailure("Unexpectedly found empty")
    }
    return unwrapValue
}()

print("unwrap: \(unwrap)")
print("unwrapNotEmpty: \(unwrapNotEmpty)")

I also created a notEmpty macro that checks if the value is not empty and returns it: enter image description here

{ [\(raw: argumentName)] in
    guard let \(raw: argumentName), !\(raw: argumentName).isEmpty else {
        return nil
    }
    return \(raw: argumentName)
}()

enter image description here

Then, I removed the unwrap macro and tried executing unwrapNotEmpty consecutively:

let greeting: String? = ""
let notEmpty = #notEmpty(greeting)
let unwrapNotEmpty = #unwrapNotEmpty(greeting)
print("notEmpty: \(notEmpty)")
print("unwrapNotEmpty: \(unwrapNotEmpty)")

This time, a failure was triggered in the unwrapNotEmpty macro:

enter image description here

So, I learned that the fact that two macros are executed consecutively doesn't mean a specific code won't work.

Under what circumstances does the use of macros prevent the execution of other code?

0

There are 0 best solutions below