Compiler segmentation fault when using property wrappers to log changes to a value

I have this: (on Xcode 11.2 11B52)

/// Adds a `didSet` observer which logs the new value of the wrapped property when it changes.
@propertyWrapper
struct LogChanges <ValueType: Equatable> {
    var wrappedValue: ValueType {
        didSet {
            if  wrappedValue != oldValue {
                print("\(oldValue) → \(wrappedValue)")
            }
        }
    }
}

struct Thingy {
    @LogChanges var trackedProperty: Int = 0
}

var thingy = Thingy()

thingy.trackedProperty = 1 // 0 → 1
thingy.trackedProperty = 2 // 1 → 2
thingy.trackedProperty = 3 // 2 → 3

and it works fine, but it's not very helpful when tracking multiple values because there's no way to tell which one is changing!

So I tried to add this to the wrapper:

let name: String

init(wrappedValue: ValueType,
     _ callerFile: String = #file,
     _ callerFunction: String = #function)
{
    self.wrappedValue = wrappedValue
    self.name = "\(callerFile) \(callerFunction)"
}

Something like that works with the inits of non-wrapper types, but trying to compile this gives a segmentation fault:

Stack dump:
...
1.	While running pass #0 SILModuleTransform "SerializeSILPass".
2.	While serializing 'trackedProperty' (at /Users/Me/PropertyWrapperProblem/PropertyWrapperProblem/main.swift:33:8)
0  swift                    0x0000000104f4da13 PrintStackTraceSignalHandler(void*) + 51
1  swift                    0x0000000104f4d1e6 SignalHandler(int) + 358
2  libsystem_platform.dylib 0x00007fff67263b1d _sigtramp + 29
3  swift                    0x0000000101b411aa llvm::Optional<swift::Type> llvm::function_ref<llvm::Optional<swift::Type> (swift::TypeBase*)>::callback_fn<substType(swift::Type, llvm::function_ref<swift::Type (swift::SubstitutableType*)>, llvm::function_ref<llvm::Optional<swift::ProtocolConformanceRef> (swift::CanType, swift::Type, swift::ProtocolDecl*)>, swift::SubstOptions)::$_17>(long, swift::TypeBase*) + 410
4  swift                    0x0000000101b04d3d swift::extractInlinableText(swift::SourceManager&, swift::ASTNode, llvm::SmallVectorImpl<char>&) + 461
5  swift                    0x0000000101a98796 swift::ParamDecl::getDefaultValueStringRepresentation(llvm::SmallVectorImpl<char>&) const + 1030
6  swift                    0x000000010181f98a swift::serialization::Serializer::writeDecl(swift::Decl const*) + 5946
7  swift                    0x000000010184542f swift::serialization::Serializer::writeAllDeclsAndTypes() + 62287
8  swift                    0x00000001018523f4 swift::serialization::Serializer::writeAST(llvm::PointerUnion<swift::ModuleDecl*, swift::SourceFile*>, bool) + 5364
9  swift                    0x000000010185f330 swift::serialization::Serializer::writeToStream(llvm::raw_ostream&, llvm::PointerUnion<swift::ModuleDecl*, swift::SourceFile*>, swift::SILModule const*, swift::SerializationOptions const&) + 6016
10 swift                    0x0000000101860c0b bool llvm::function_ref<bool (llvm::raw_pwrite_stream&)>::callback_fn<swift::serialize(llvm::PointerUnion<swift::ModuleDecl*, swift::SourceFile*>, swift::SerializationOptions const&, swift::SILModule const*)::$_8>(long, llvm::raw_pwrite_stream&) + 139
11 swift                    0x0000000100b67729 swift::withOutputFile(swift::DiagnosticEngine&, llvm::StringRef, llvm::function_ref<bool (llvm::raw_pwrite_stream&)>) + 2569
12 swift                    0x0000000101860a67 swift::serialize(llvm::PointerUnion<swift::ModuleDecl*, swift::SourceFile*>, swift::SerializationOptions const&, swift::SILModule const*) + 311
13 swift                    0x0000000100ba31bb std::__1::__function::__func<performCompileStepsPostSILGen(swift::CompilerInstance&, swift::CompilerInvocation&, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule> >, bool, llvm::PointerUnion<swift::ModuleDecl*, swift::SourceFile*>, swift::PrimarySpecificPaths const&, bool, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*)::$_12, std::__1::allocator<performCompileStepsPostSILGen(swift::CompilerInstance&, swift::CompilerInvocation&, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule> >, bool, llvm::PointerUnion<swift::ModuleDecl*, swift::SourceFile*>, swift::PrimarySpecificPaths const&, bool, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*)::$_12>, void ()>::operator()() + 603
14 swift                    0x000000010134ac61 SerializeSILPass::run() + 49
15 swift                    0x0000000101241129 swift::SILPassManager::execute() + 7305
16 swift                    0x0000000100e9dc0b swift::CompilerInstance::performSILProcessing(swift::SILModule*, swift::UnifiedStatsReporter*) + 1563
17 swift                    0x0000000100b99375 performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*) + 33925
18 swift                    0x0000000100b8d6e4 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 6820
19 swift                    0x0000000100b1abe3 main + 1219
20 libdyld.dylib            0x00007fff670622e5 start + 1
21 libdyld.dylib            0x0000000000000047 start + 2566511971
error: Segmentation fault: 11 (in target 'PropertyWrapperProblem' from project 'PropertyWrapperProblem')

I could manually specify the property name at every usage like @LogChanges(name: "trackedProperty") but that would be inconvenient, ugly and not always accurate.

Works fine on master, so I guess this got fixed a while ago (I know @harlanhaskins fixed a bug in getDefaultValueStringRepresentation related to optionals, but I am not sure if this is related).

It's a different change, where we no longer crash while trying to get the text from a property wrapper initializer, but instead get the bogus text var _trackedProperty: LogChanges<Int> = LogChanges(name: "trackedProperty") var _trackedProperty. We need the text when printing module interfaces.

The way to fix this is probably to wholesale construct the LogChanges(wrappedValue: <wrapped value>) call, but that gets hairy quickly with nested property wrappers.

1 Like

Now I'm seeing problems with using overloaded inits and default arguments:

init(wrappedValue: ValueType,
     propertyName: String,
     omitOldValue: Bool)
{
    self.wrappedValue = wrappedValue
    self.propertyName = propertyName
    self.omitOldValue = omitOldValue
}

init(_ wrappedValue: ValueType,
     propertyName: String)
{
    self.wrappedValue = wrappedValue
    self.propertyName = propertyName
    self.omitOldValue = false
}

I want to make omitOldValue optional, and I've tried various combinations, but I keep getting errors like:

Cannot invoke initializer for type 'LogInputEventChanges<_>' with an argument list of type '(propertyName: String)'

Extraneous argument label 'wrappedValue:' in call (even though there's no such explicit argument.)

I don't think you can define an init with no argument label and wrappedValue parameter label inside a property wrapper i.e. wrappedValue must be an argument label.

It turns out I had to provide a default value like = nil for optional properties. The argument label is not needed.

Hope we can get #file and #function back for property wrapper inits.

Terms of Service

Privacy Policy

Cookie Policy