Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AOT fails on mono-ios with "Method has zero rva" in PolyType-generated methods #73

Closed
AArnott opened this issue Dec 5, 2024 · 13 comments · Fixed by #75
Closed

AOT fails on mono-ios with "Method has zero rva" in PolyType-generated methods #73

AArnott opened this issue Dec 5, 2024 · 13 comments · Fixed by #75

Comments

@AArnott
Copy link
Contributor

AArnott commented Dec 5, 2024

I just tried to consume Nerdbank.MessagePack from an iOS app, and the (mono) AOT compiler failed with the following:

Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_Security_TickerSymbol (Nerdbank.Cryptocurrencies.Security,string)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_Security_Name (Nerdbank.Cryptocurrencies.Security,string)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_Security_Precision (Nerdbank.Cryptocurrencies.Security,int)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_Security_IsTestNet (Nerdbank.Cryptocurrencies.Security,bool)' due to: 'Method has zero rva'.
Unable to compile method 'int& PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__FieldAccessor_ContactManager_nextContactId (Nerdbank.Zcash.App.Models.ContactManager)' due to: 'Method has zero rva'.
Unable to compile method 'Nerdbank.Zcash.App.Models.ContactManager PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__CtorAccessor_ContactManager (System.Collections.Generic.IReadOnlyList`1<Nerdbank.Zcash.App.Models.Contact>)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_ZcashTransaction_IsIncoming (Nerdbank.Zcash.App.Models.ZcashTransaction,bool)' due to: 'Method has zero rva'.
Unable to compile method 'int& PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__FieldAccessor_ZcashWallet_nextAccountId (Nerdbank.Zcash.App.Models.ZcashWallet)' due to: 'Method has zero rva'.
Unable to compile method 'Nerdbank.Zcash.App.Models.ZcashWallet PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__CtorAccessor_ZcashWallet (System.Collections.Generic.IReadOnlyList`1<Nerdbank.Zcash.App.Models.HDWallet>,System.Collections.Generic.IReadOnlyList`1<Nerdbank.Zcash.App.Models.Account>)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_ExtendedViewingKey_DerivationPath (Nerdbank.Bitcoin.Bip32HDWallet/ExtendedKeyBase,Nerdbank.Cryptocurrencies.Bip32KeyPath)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_HDDerivationSource_Wallet (Nerdbank.Zcash.ZcashAccount/HDDerivationSource&,Nerdbank.Zcash.Zip32HDWallet)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_HDDerivationSource_AccountIndex (Nerdbank.Zcash.ZcashAccount/HDDerivationSource&,uint)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_OrchardAddress_Metadata (Nerdbank.Zcash.UnifiedAddress,Nerdbank.Zcash.UnifiedEncodingMetadata)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_LineItem_ToAddress (Nerdbank.Zcash.App.Models.ZcashTransaction/LineItem,Nerdbank.Zcash.ZcashAddress)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_LineItem_Amount (Nerdbank.Zcash.App.Models.ZcashTransaction/LineItem,System.Decimal)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_LineItem_Memo (Nerdbank.Zcash.App.Models.ZcashTransaction/LineItem,Nerdbank.Zcash.Memo)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_UnifiedEncodingMetadata_ExpirationDate (Nerdbank.Zcash.UnifiedEncodingMetadata,System.Nullable`1<System.DateTimeOffset>)' due to: 'Method has zero rva'.
Unable to compile method 'void PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__SetAccessor_UnifiedEncodingMetadata_ExpirationHeight (Nerdbank.Zcash.UnifiedEncodingMetadata,System.Nullable`1<uint>)' due to: 'Method has zero rva'.
Unable to compile method 'Nerdbank.Zcash.App.Models.Contact PolyType.SourceGenerator.ShapeProvider_Nerdbank_Zcash_App:__CtorAccessor_Contact (System.Collections.Immutable.ImmutableDictionary`2<int, Nerdbank.Zcash.App.Models.Contact/AssignedSendingAddresses>,System.Collections.ObjectModel.ObservableCollection`1<Nerdbank.Zcash.ZcashAddress>)' due to: 'Method has zero rva'.
Unable to compile method 'void System.Runtime.Serialization.SerializationGuard:<ThrowIfDeserializationInProgress>g__ThrowIfDeserializationInProgress|0_0 (System.Runtime.Serialization.SerializationInfo,string,int&)' due to: 'Method has zero rva'.
Mono Ahead of Time compiler - compiling assembly /Volumes/Expansion/myagent/_work/1/s/obj/src/Nerdbank.Zcash.App/Nerdbank.Zcash.App.iOS/Release/net8.0-ios/ios-arm64/linked/aot-instances.dll
AOTID B6D586B2-791F-12F5-B34D-7EEB9AD4A19F
Adding 75931 dedup-ed methods.
Executing opt: "/Volumes/Expansion/myagent/_work/_tool/dotnet/packs/Microsoft.NETCore.App.Runtime.AOT.osx-arm64.Cross.ios-arm64/8.0.11/Sdk/../tools/opt" -f -disable-tail-calls -passes="default<O2>,place-safepoints" -spp-all-backedges -o "../../../obj/src/Nerdbank.Zcash.App/Nerdbank.Zcash.App.iOS/Release/net8.0-ios/ios-arm64/nativelibraries/aot-output/arm64/aot-instances.dll.s.opt.bc" "../../../obj/src/Nerdbank.Zcash.App/Nerdbank.Zcash.App.iOS/Release/net8.0-ios/ios-arm64/nativelibraries/aot-output/arm64/aot-instances.dll.s.bc"
Executing llc: "/Volumes/Expansion/myagent/_work/_tool/dotnet/packs/Microsoft.NETCore.App.Runtime.AOT.osx-arm64.Cross.ios-arm64/8.0.11/Sdk/../tools/llc"  -enable-implicit-null-checks -disable-fault-maps -march=aarch64 -asm-verbose=false -mtriple=arm64-ios -disable-gnu-eh-frame -enable-mono-eh-frame -mono-eh-frame-symbol=_mono_aot_aot_instances_eh_frame -disable-tail-calls -relocation-model=static -filetype=obj -o "../../../obj/src/Nerdbank.Zcash.App/Nerdbank.Zcash.App.iOS/Release/net8.0-ios/ios-arm64/nativelibraries/aot-output/arm64/aot-instances.dll.llvm.o" "../../../obj/src/Nerdbank.Zcash.App/Nerdbank.Zcash.App.iOS/Release/net8.0-ios/ios-arm64/nativelibraries/aot-output/arm64/aot-instances.dll.s.opt.bc"
Compiled: 67644/67664
Output file: '../../../obj/src/Nerdbank.Zcash.App/Nerdbank.Zcash.App.iOS/Release/net8.0-ios/ios-arm64/nativelibraries/aot-output/arm64/aot-instances.dll.s'.
Linking symbol: '_mono_aot_module_aot_instances_info'.
LLVM output file: '../../../obj/src/Nerdbank.Zcash.App/Nerdbank.Zcash.App.iOS/Release/net8.0-ios/ios-arm64/nativelibraries/aot-output/arm64/aot-instances.dll.llvm.o'.
JIT time: 26715 ms, Generation time: 219460 ms, Assembly+Link time: 16 ms.
@AArnott
Copy link
Contributor Author

AArnott commented Dec 5, 2024

dotnet/runtime#91066 (comment)

My app is targeting .NET 8. Given the comment that arm64 would only be fixed in .NET 9, I'll try retargeting.

@AArnott
Copy link
Contributor Author

AArnott commented Dec 6, 2024

Unfortunately once I target .NET 9, the SDK requires me to use Xcode 16.1, but then I hit a new bug:
dotnet/macios#21762

I hate iOS development.

@eiriktsarpalis
Copy link
Owner

Judging by the error message it's crashing because of the unsafe accessors correct? In ns2.0 the source generator replaces those with regular reflection, would it make sense to have a setting that defaults back to reflection everywhere? Alternatively, updating the accessibility modifiers to at least be internal is one potential workaround.

@AArnott
Copy link
Contributor Author

AArnott commented Dec 6, 2024

would it make sense to have a setting that defaults back to reflection everywhere

I don't think so. At least not at this point. Because what you propose would influence source generation, so it would have to be done in every consuming project manually. Or you could do it automatically when the project targets net8.0-ios which would be good, but it relies on targeting that specific target framework, and most libraries target something more general.

@AArnott
Copy link
Contributor Author

AArnott commented Dec 8, 2024

Here's the odd thing though: Yes, there are unsafe accessors, but no they aren't required.

This is what the source generator produces:

[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "set_TickerSymbol")]
private static extern void __SetAccessor_Security_TickerSymbol(global::Nerdbank.Cryptocurrencies.Security obj, string value);

[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "set_Name")]
private static extern void __SetAccessor_Security_Name(global::Nerdbank.Cryptocurrencies.Security obj, string value);

[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "set_Precision")]
private static extern void __SetAccessor_Security_Precision(global::Nerdbank.Cryptocurrencies.Security obj, int value);

[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "set_IsTestNet")]
private static extern void __SetAccessor_Security_IsTestNet(global::Nerdbank.Cryptocurrencies.Security obj, bool value);

But these unsafe accessors are not called. And in fact there is no need for them to be, because the members they provide access to are settable through a primary constructor

public record Security(string TickerSymbol, string? Name = null, int Precision = 8, bool IsTestNet = false)

So there doesn't appear to be any way for me to remove the unsafe accessors from being generated, and it seems a bug that the source generator is producing them in the first place.

The constructor parameter is detected and source generated for:

new global::PolyType.SourceGenModel.SourceGenConstructorParameterShape<(string, string, int, bool), bool>
{
    Position = 3,
    Name = "IsTestNet",
    ParameterType = Boolean,
    Kind = global::PolyType.Abstractions.ConstructorParameterKind.ConstructorParameter,
    IsRequired = false,
    IsNonNullable = true,
    IsPublic = true,
    HasDefaultValue = true,
    DefaultValue = false,
    Setter = static (ref (string, string, int, bool) state, bool value) => state.Item4 = value,
    AttributeProviderFunc = static () => typeof(global::Nerdbank.Cryptocurrencies.Security).GetConstructor(__BindingFlags_Instance_All, new[] { typeof(string), typeof(string), typeof(int), typeof(bool) })?.GetParameters()[3],
},

And of course the property getter is accessible via this source-generated shape:

new global::PolyType.SourceGenModel.SourceGenPropertyShape<global::Nerdbank.Cryptocurrencies.Security, bool>
{
    Name = "IsTestNet",
    DeclaringType = (global::PolyType.Abstractions.IObjectTypeShape<global::Nerdbank.Cryptocurrencies.Security>)Security,
    PropertyType = Boolean,
    Getter = static (ref global::Nerdbank.Cryptocurrencies.Security obj) => obj.IsTestNet,
    AttributeProviderFunc = static () => typeof(global::Nerdbank.Cryptocurrencies.Security).GetProperty("IsTestNet", __BindingFlags_Instance_All, null, typeof(bool), [], null),
    IsField = false,
    IsGetterPublic = true,
    IsSetterPublic = false,
    IsGetterNonNullable = true,
    IsSetterNonNullable = false,
},

@eiriktsarpalis
Copy link
Owner

What does the source generated parameterized constructor look like for the type?

@AArnott
Copy link
Contributor Author

AArnott commented Dec 8, 2024

You mean this?

private global::PolyType.Abstractions.IConstructorShape __CreateConstructor_Security()
{
    return new global::PolyType.SourceGenModel.SourceGenConstructorShape<global::Nerdbank.Cryptocurrencies.Security, (string, string, int, bool)>
    {
        DeclaringType = (global::PolyType.Abstractions.IObjectTypeShape<global::Nerdbank.Cryptocurrencies.Security>)Security,
        ParameterCount = 4,
        GetParametersFunc = __CreateConstructorParameters_Security,
        ArgumentStateConstructorFunc = static () => (default!, null!, 8, false),
        ParameterizedConstructorFunc = static (ref (string, string, int, bool) state) => new global::Nerdbank.Cryptocurrencies.Security(state.Item1, state.Item2!, state.Item3, state.Item4),
        AttributeProviderFunc = static () => typeof(global::Nerdbank.Cryptocurrencies.Security).GetConstructor(__BindingFlags_Instance_All, new[] { typeof(string), typeof(string), typeof(int), typeof(bool) }),
        IsPublic = true,
    };
}

@eiriktsarpalis
Copy link
Owner

Yup, just needed to make sure the ctor delegate isn't setting the property itself. Can you share a minimal repro for the type?

@AArnott
Copy link
Contributor Author

AArnott commented Dec 9, 2024

It's as simple as this:

[GenerateShape]
public partial record Security(string TickerSymbol);

That alone leads to construction of the unsafe accessor.

@eiriktsarpalis
Copy link
Owner

Oh wow. I'll take a look.

@AArnott
Copy link
Contributor Author

AArnott commented Dec 14, 2024

What about the last one?

Unable to compile method 'void System.Runtime.Serialization.SerializationGuard:g__ThrowIfDeserializationInProgress|0_0 (System.Runtime.Serialization.SerializationInfo,string,int&)' due to: 'Method has zero rva'.

Why is that being generated? That method is defined in .NET.

@AArnott
Copy link
Contributor Author

AArnott commented Dec 14, 2024

I'm also still seeing these UnsafeAccessor members being generated for setters on properties like this:

public required ZcashAddress ToAddress { get; init; }

@eiriktsarpalis
Copy link
Owner

eiriktsarpalis commented Dec 14, 2024

What about the last one?

Unable to compile method 'void System.Runtime.Serialization.SerializationGuard:g__ThrowIfDeserializationInProgress|0_0 (System.Runtime.Serialization.SerializationInfo,string,int&)' due to: 'Method has zero rva'.

Why is that being generated? That method is defined in .NET.

It doesn't look like a method generated by PolyType, it's a local method in BCL. How is the method being rooted?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants