From 039dd71a42b00e708c15d233b777305bec5470ac Mon Sep 17 00:00:00 2001 From: YoelDruxman Date: Thu, 15 Jan 2026 09:25:12 -0500 Subject: [PATCH 1/2] test: Add test for generic base class with different type instantiations This test exposes a bug where UnsafeAccessor caching causes wrong type arguments when two types inherit from different instantiations of the same generic base class (e.g., A and A). The generated code incorrectly uses the cached type argument from the first processed type for all subsequent types. Co-Authored-By: Claude Opus 4.5 --- .../Mapping/UnsafeAccessorTest.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/Riok.Mapperly.Tests/Mapping/UnsafeAccessorTest.cs b/test/Riok.Mapperly.Tests/Mapping/UnsafeAccessorTest.cs index 0fa0b95656..a66673e7b4 100644 --- a/test/Riok.Mapperly.Tests/Mapping/UnsafeAccessorTest.cs +++ b/test/Riok.Mapperly.Tests/Mapping/UnsafeAccessorTest.cs @@ -88,6 +88,29 @@ public Task PrivatePropertyInGenericClassMultipleTypeParametersWithConstraints() return TestHelper.VerifyGenerator(source); } + [Fact] + public Task ProtectedPropertyInGenericBaseClassWithDifferentInstantiations() + { + var source = TestSourceBuilder.MapperWithBodyAndTypes( + """ + partial B1 Map1(A1 source); + partial B2 Map2(A2 source); + """, + TestSourceBuilderOptions.WithMemberVisibility(MemberVisibility.All), + "interface IA { }", + "interface IB : IA { }", + "interface IC : IA { }", + "class A where T : IA { protected T _value { get; set; } }", + "class A1 : A { }", + "class A2 : A { }", + "class B where T : IA { protected T _value { get; set; } }", + "class B1 : B { }", + "class B2 : B { }" + ); + + return TestHelper.VerifyGenerator(source); + } + [Fact] public Task ProtectedProperty() { From 33993fd4f085b11797288e6fb93894eb7f0c16ec Mon Sep 17 00:00:00 2001 From: YoelDruxman Date: Thu, 15 Jan 2026 09:39:00 -0500 Subject: [PATCH 2/2] fix: Pass actual containing type to UnsafeAccessor invocations Fix incorrect type arguments in generated UnsafeAccessor code when multiple types inherit from different instantiations of the same generic base class. Previously, UnsafeAccessorTypeContext cached accessors by symbol.OriginalDefinition, causing all derived types to use the type arguments from the first processed type. This fix passes the actual containing type at invocation time, ensuring each mapping uses the correct type arguments. Changes: - Add containingType parameter to IMemberSetter.BuildAssignment - Add containingType parameter to IMemberGetter.BuildAccess - Update UnsafeAccessor implementations to use passed type - Update callers to pass member's containing type Fixes the CS1503 error when mapping types like: - A1 : A and A2 : A where both should use their respective type arguments. Co-Authored-By: Claude Opus 4.5 --- .../Capacity/EnsureCapacityMethodSetter.cs | 8 +++- .../UnsafeAccess/UnsafeFieldAccessor.cs | 16 ++++++-- .../UnsafeAccess/UnsafeGetPropertyAccessor.cs | 9 ++++- .../UnsafeAccess/UnsafeSetPropertyAccessor.cs | 14 ++++++- .../Members/ConstructorParameterMember.cs | 6 ++- .../Symbols/Members/FieldMember.cs | 9 ++++- .../Symbols/Members/IMemberGetter.cs | 3 +- .../Symbols/Members/IMemberSetter.cs | 8 +++- .../Symbols/Members/MemberPathGetter.cs | 10 ++--- .../Symbols/Members/MemberPathSetter.cs | 13 +++++-- .../Symbols/Members/ParameterSourceMember.cs | 6 ++- .../Symbols/Members/PropertyMember.cs | 9 ++++- ...nGenericClassMultiple#Mapper.g.verified.cs | 2 +- ...fferentInstantiations#Mapper.g.verified.cs | 39 +++++++++++++++++++ 14 files changed, 126 insertions(+), 26 deletions(-) create mode 100644 test/Riok.Mapperly.Tests/_snapshots/UnsafeAccessorTest.ProtectedPropertyInGenericBaseClassWithDifferentInstantiations#Mapper.g.verified.cs diff --git a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/EnsureCapacityMethodSetter.cs b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/EnsureCapacityMethodSetter.cs index 1214487e7e..2cedf750cf 100644 --- a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/EnsureCapacityMethodSetter.cs +++ b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/EnsureCapacityMethodSetter.cs @@ -1,3 +1,4 @@ +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Riok.Mapperly.Symbols.Members; using static Riok.Mapperly.Emit.Syntax.SyntaxFactoryHelper; @@ -17,7 +18,12 @@ private EnsureCapacityMethodSetter() { } public bool SupportsCoalesceAssignment => false; - public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, ExpressionSyntax valueToAssign, bool coalesceAssignment = false) + public ExpressionSyntax BuildAssignment( + ExpressionSyntax? baseAccess, + ExpressionSyntax valueToAssign, + INamedTypeSymbol? containingType = null, + bool coalesceAssignment = false + ) { if (baseAccess == null) throw new ArgumentNullException(nameof(baseAccess)); diff --git a/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeFieldAccessor.cs b/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeFieldAccessor.cs index 583a1a6ded..8652333e4a 100644 --- a/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeFieldAccessor.cs +++ b/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeFieldAccessor.cs @@ -43,7 +43,7 @@ public MethodDeclarationSyntax BuildAccessorMethod(SourceEmitterContext ctx) return ctx.SyntaxFactory.PublicStaticExternMethod(returnType, methodName, parameters, [attribute]); } - public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, bool nullConditional = false) + public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, INamedTypeSymbol? containingType = null, bool nullConditional = false) { if (baseAccess == null) throw new ArgumentNullException(nameof(baseAccess)); @@ -54,7 +54,10 @@ public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, bool nullCondi return InvocationWithoutIndention(method); } - var genericClassName = GenericName(className).WithTypeArgumentList(TypeArgumentList(symbol.ContainingType.TypeArguments)); + // Use the passed containingType for type arguments if provided, + // otherwise fall back to the symbol's containing type. + var typeArgs = containingType?.TypeArguments ?? symbol.ContainingType.TypeArguments; + var genericClassName = GenericName(className).WithTypeArgumentList(TypeArgumentList(typeArgs)); var invocation = InvocationExpression(MemberAccess(genericClassName, methodName)) .WithArgumentList(ArgumentListWithoutIndention([baseAccess])); @@ -64,9 +67,14 @@ public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, bool nullCondi return Conditional(IsNotNull(baseAccess), invocation, DefaultLiteral()); } - public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, ExpressionSyntax valueToAssign, bool coalesceAssignment = false) + public ExpressionSyntax BuildAssignment( + ExpressionSyntax? baseAccess, + ExpressionSyntax valueToAssign, + INamedTypeSymbol? containingType = null, + bool coalesceAssignment = false + ) { - var access = BuildAccess(baseAccess); + var access = BuildAccess(baseAccess, containingType); return Assignment(access, valueToAssign, coalesceAssignment); } } diff --git a/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeGetPropertyAccessor.cs b/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeGetPropertyAccessor.cs index 859e04dba9..8e2c9d3bfa 100644 --- a/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeGetPropertyAccessor.cs +++ b/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeGetPropertyAccessor.cs @@ -46,7 +46,7 @@ public MethodDeclarationSyntax BuildAccessorMethod(SourceEmitterContext ctx) ); } - public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, bool nullConditional = false) + public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, INamedTypeSymbol? containingType = null, bool nullConditional = false) { if (baseAccess == null) throw new ArgumentNullException(nameof(baseAccess)); @@ -57,7 +57,12 @@ public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, bool nullCondi return InvocationWithoutIndention(method); } - var genericClassName = GenericName(className).WithTypeArgumentList(TypeArgumentList(symbol.ContainingType.TypeArguments)); + // Use the passed containingType for type arguments if provided, + // otherwise fall back to the symbol's containing type. + // This is critical for inherited members where the cached symbol's + // type arguments may differ from the actual derived type being mapped. + var typeArgs = containingType?.TypeArguments ?? symbol.ContainingType.TypeArguments; + var genericClassName = GenericName(className).WithTypeArgumentList(TypeArgumentList(typeArgs)); var invocation = InvocationExpression(MemberAccess(genericClassName, methodName)) .WithArgumentList(ArgumentListWithoutIndention([baseAccess])); diff --git a/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeSetPropertyAccessor.cs b/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeSetPropertyAccessor.cs index 23250fe26b..e1c6fd7e86 100644 --- a/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeSetPropertyAccessor.cs +++ b/src/Riok.Mapperly/Descriptors/UnsafeAccess/UnsafeSetPropertyAccessor.cs @@ -53,7 +53,12 @@ public MethodDeclarationSyntax BuildAccessorMethod(SourceEmitterContext ctx) ); } - public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, ExpressionSyntax valueToAssign, bool coalesceAssignment = false) + public ExpressionSyntax BuildAssignment( + ExpressionSyntax? baseAccess, + ExpressionSyntax valueToAssign, + INamedTypeSymbol? containingType = null, + bool coalesceAssignment = false + ) { if (baseAccess == null) throw new ArgumentNullException(nameof(baseAccess)); @@ -63,8 +68,13 @@ public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, Expression return InvocationWithoutIndention(MemberAccess(baseAccess, methodName), valueToAssign); } + // Use the passed containingType for type arguments if provided, + // otherwise fall back to the symbol's containing type. + // This is critical for inherited members where the cached symbol's + // type arguments may differ from the actual derived type being mapped. + var typeArgs = containingType?.TypeArguments ?? symbol.ContainingType.TypeArguments; var args = new[] { baseAccess, valueToAssign }; - var genericClassName = GenericName(className).WithTypeArgumentList(TypeArgumentList(symbol.ContainingType.TypeArguments)); + var genericClassName = GenericName(className).WithTypeArgumentList(TypeArgumentList(typeArgs)); return InvocationExpression(MemberAccess(genericClassName, methodName)).WithArgumentList(ArgumentListWithoutIndention(args)); } } diff --git a/src/Riok.Mapperly/Symbols/Members/ConstructorParameterMember.cs b/src/Riok.Mapperly/Symbols/Members/ConstructorParameterMember.cs index d49fe522c8..8e848e23c7 100644 --- a/src/Riok.Mapperly/Symbols/Members/ConstructorParameterMember.cs +++ b/src/Riok.Mapperly/Symbols/Members/ConstructorParameterMember.cs @@ -36,5 +36,9 @@ public class ConstructorParameterMember(IParameterSymbol symbol, SymbolAccessor public IMemberSetter BuildSetter(UnsafeAccessorContext ctx) => throw new InvalidOperationException($"Cannot create a setter for {nameof(ParameterSourceMember)}"); - public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, bool nullConditional = false) => IdentifierName(Name); + public ExpressionSyntax BuildAccess( + ExpressionSyntax? baseAccess, + INamedTypeSymbol? containingType = null, + bool nullConditional = false + ) => IdentifierName(Name); } diff --git a/src/Riok.Mapperly/Symbols/Members/FieldMember.cs b/src/Riok.Mapperly/Symbols/Members/FieldMember.cs index eeaa901506..88794b6122 100644 --- a/src/Riok.Mapperly/Symbols/Members/FieldMember.cs +++ b/src/Riok.Mapperly/Symbols/Members/FieldMember.cs @@ -54,13 +54,18 @@ public IMemberSetter BuildSetter(UnsafeAccessorContext ctx) return ctx.GetOrBuildFieldGetter(this); } - public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, ExpressionSyntax valueToAssign, bool coalesceAssignment = false) + public ExpressionSyntax BuildAssignment( + ExpressionSyntax? baseAccess, + ExpressionSyntax valueToAssign, + INamedTypeSymbol? containingType = null, + bool coalesceAssignment = false + ) { var targetMemberRef = BuildAccess(baseAccess); return Assignment(targetMemberRef, valueToAssign, coalesceAssignment); } - public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, bool nullConditional = false) + public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, INamedTypeSymbol? containingType = null, bool nullConditional = false) { if (baseAccess == null) return SyntaxFactory.IdentifierName(Name); diff --git a/src/Riok.Mapperly/Symbols/Members/IMemberGetter.cs b/src/Riok.Mapperly/Symbols/Members/IMemberGetter.cs index 9a5ce17a86..ddae4298d6 100644 --- a/src/Riok.Mapperly/Symbols/Members/IMemberGetter.cs +++ b/src/Riok.Mapperly/Symbols/Members/IMemberGetter.cs @@ -1,8 +1,9 @@ +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Riok.Mapperly.Symbols.Members; public interface IMemberGetter { - ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, bool nullConditional = false); + ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, INamedTypeSymbol? containingType = null, bool nullConditional = false); } diff --git a/src/Riok.Mapperly/Symbols/Members/IMemberSetter.cs b/src/Riok.Mapperly/Symbols/Members/IMemberSetter.cs index 5c5e470265..9c8884b010 100644 --- a/src/Riok.Mapperly/Symbols/Members/IMemberSetter.cs +++ b/src/Riok.Mapperly/Symbols/Members/IMemberSetter.cs @@ -1,3 +1,4 @@ +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Riok.Mapperly.Symbols.Members; @@ -6,5 +7,10 @@ public interface IMemberSetter { bool SupportsCoalesceAssignment { get; } - ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, ExpressionSyntax valueToAssign, bool coalesceAssignment = false); + ExpressionSyntax BuildAssignment( + ExpressionSyntax? baseAccess, + ExpressionSyntax valueToAssign, + INamedTypeSymbol? containingType = null, + bool coalesceAssignment = false + ); } diff --git a/src/Riok.Mapperly/Symbols/Members/MemberPathGetter.cs b/src/Riok.Mapperly/Symbols/Members/MemberPathGetter.cs index 8f26a6512e..153cc061f2 100644 --- a/src/Riok.Mapperly/Symbols/Members/MemberPathGetter.cs +++ b/src/Riok.Mapperly/Symbols/Members/MemberPathGetter.cs @@ -56,7 +56,7 @@ public static MemberPathGetter Build(SimpleMappingBuilderContext ctx, MemberPath { return path.AggregateWithPrevious( baseAccess, - (expr, prevProp, prop) => prop.Getter.BuildAccess(expr, prevProp.Member?.IsNullable == true) + (expr, prevProp, prop) => prop.Getter.BuildAccess(expr, prop.Member.ContainingType, prevProp.Member?.IsNullable == true) ); } @@ -66,12 +66,12 @@ public static MemberPathGetter Build(SimpleMappingBuilderContext ctx, MemberPath baseAccess, (a, b) => b.Member.Type.IsNullableValueType() - ? MemberAccess(b.Getter.BuildAccess(a), NullableValueProperty) - : b.Getter.BuildAccess(a) + ? MemberAccess(b.Getter.BuildAccess(a, b.Member.ContainingType), NullableValueProperty) + : b.Getter.BuildAccess(a, b.Member.ContainingType) ); } - return path.Aggregate(baseAccess, (a, b) => b.Getter.BuildAccess(a)); + return path.Aggregate(baseAccess, (a, b) => b.Getter.BuildAccess(a, b.Member.ContainingType)); } /// @@ -99,7 +99,7 @@ private BinaryExpressionSyntax BuildNonNullCondition(ExpressionSyntax baseAccess var conditions = new List(); foreach (var pathPart in nullablePath) { - access = pathPart.Getter.BuildAccess(access); + access = pathPart.Getter.BuildAccess(access, pathPart.Member.ContainingType); if (!pathPart.Member.IsNullable) continue; diff --git a/src/Riok.Mapperly/Symbols/Members/MemberPathSetter.cs b/src/Riok.Mapperly/Symbols/Members/MemberPathSetter.cs index 580fecb182..ba0b0399d0 100644 --- a/src/Riok.Mapperly/Symbols/Members/MemberPathSetter.cs +++ b/src/Riok.Mapperly/Symbols/Members/MemberPathSetter.cs @@ -13,12 +13,19 @@ public class MemberPathSetter private readonly NonEmptyMemberPath _memberPath; private readonly MemberPathGetter _baseAccessGetter; private readonly IMemberSetter _memberSetter; + private readonly IMappableMember _member; - private MemberPathSetter(NonEmptyMemberPath memberPath, MemberPathGetter baseAccessGetter, IMemberSetter memberSetter) + private MemberPathSetter( + NonEmptyMemberPath memberPath, + MemberPathGetter baseAccessGetter, + IMemberSetter memberSetter, + IMappableMember member + ) { _memberPath = memberPath; _baseAccessGetter = baseAccessGetter; _memberSetter = memberSetter; + _member = member; } public bool SupportsCoalesceAssignment => _memberSetter.SupportsCoalesceAssignment; @@ -30,13 +37,13 @@ public static MemberPathSetter Build(SimpleMappingBuilderContext ctx, NonEmptyMe var objectPath = MemberPath.Create(path.RootType, path.ObjectPath.ToList()); var objectGetter = objectPath.BuildGetter(ctx); var memberSetter = path.Member.BuildSetter(ctx.UnsafeAccessorContext); - return new MemberPathSetter(path, objectGetter, memberSetter); + return new MemberPathSetter(path, objectGetter, memberSetter, path.Member); } public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, ExpressionSyntax valueToAssign, bool coalesceAssignment = false) { baseAccess = _baseAccessGetter.BuildAccess(baseAccess); - return _memberSetter.BuildAssignment(baseAccess, valueToAssign, coalesceAssignment); + return _memberSetter.BuildAssignment(baseAccess, valueToAssign, _member.ContainingType, coalesceAssignment); } public override bool Equals(object? obj) diff --git a/src/Riok.Mapperly/Symbols/Members/ParameterSourceMember.cs b/src/Riok.Mapperly/Symbols/Members/ParameterSourceMember.cs index 1dc39a84cc..051ed60a28 100644 --- a/src/Riok.Mapperly/Symbols/Members/ParameterSourceMember.cs +++ b/src/Riok.Mapperly/Symbols/Members/ParameterSourceMember.cs @@ -33,7 +33,11 @@ public class ParameterSourceMember(MethodParameter parameter) : IMappableMember, public IMemberSetter BuildSetter(UnsafeAccessorContext ctx) => throw new InvalidOperationException($"Cannot create a setter for {nameof(ParameterSourceMember)}"); - public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, bool nullConditional = false) => IdentifierName(Name); + public ExpressionSyntax BuildAccess( + ExpressionSyntax? baseAccess, + INamedTypeSymbol? containingType = null, + bool nullConditional = false + ) => IdentifierName(Name); public override bool Equals(object? obj) { diff --git a/src/Riok.Mapperly/Symbols/Members/PropertyMember.cs b/src/Riok.Mapperly/Symbols/Members/PropertyMember.cs index 68865d969c..d0ad48ea92 100644 --- a/src/Riok.Mapperly/Symbols/Members/PropertyMember.cs +++ b/src/Riok.Mapperly/Symbols/Members/PropertyMember.cs @@ -64,7 +64,12 @@ public IMemberSetter BuildSetter(UnsafeAccessorContext ctx) return ctx.GetOrBuildPropertySetter(this); } - public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, ExpressionSyntax valueToAssign, bool coalesceAssignment = false) + public ExpressionSyntax BuildAssignment( + ExpressionSyntax? baseAccess, + ExpressionSyntax valueToAssign, + INamedTypeSymbol? containingType = null, + bool coalesceAssignment = false + ) { Debug.Assert(CanSetDirectly); ExpressionSyntax targetMember = baseAccess == null ? IdentifierName(Name) : MemberAccess(baseAccess, Name); @@ -72,7 +77,7 @@ public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, Expression return Assignment(targetMember, valueToAssign, coalesceAssignment); } - public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, bool nullConditional = false) + public ExpressionSyntax BuildAccess(ExpressionSyntax? baseAccess, INamedTypeSymbol? containingType = null, bool nullConditional = false) { Debug.Assert(CanGetDirectly); if (baseAccess == null) diff --git a/test/Riok.Mapperly.Tests/_snapshots/UnsafeAccessorTest.PrivatePropertyInGenericClassMultiple#Mapper.g.verified.cs b/test/Riok.Mapperly.Tests/_snapshots/UnsafeAccessorTest.PrivatePropertyInGenericClassMultiple#Mapper.g.verified.cs index 83065d4733..b62cc37989 100644 --- a/test/Riok.Mapperly.Tests/_snapshots/UnsafeAccessorTest.PrivatePropertyInGenericClassMultiple#Mapper.g.verified.cs +++ b/test/Riok.Mapperly.Tests/_snapshots/UnsafeAccessorTest.PrivatePropertyInGenericClassMultiple#Mapper.g.verified.cs @@ -15,7 +15,7 @@ public partial class Mapper partial global::B Map(global::A source) { var target = new global::B(); - BAccessor.SetValue(target, AAccessor.GetValue(source)); + BAccessor.SetValue(target, AAccessor.GetValue(source)); return target; } } diff --git a/test/Riok.Mapperly.Tests/_snapshots/UnsafeAccessorTest.ProtectedPropertyInGenericBaseClassWithDifferentInstantiations#Mapper.g.verified.cs b/test/Riok.Mapperly.Tests/_snapshots/UnsafeAccessorTest.ProtectedPropertyInGenericBaseClassWithDifferentInstantiations#Mapper.g.verified.cs new file mode 100644 index 0000000000..cd892d7033 --- /dev/null +++ b/test/Riok.Mapperly.Tests/_snapshots/UnsafeAccessorTest.ProtectedPropertyInGenericBaseClassWithDifferentInstantiations#Mapper.g.verified.cs @@ -0,0 +1,39 @@ +//HintName: Mapper.g.cs +// +#nullable enable +public partial class Mapper +{ + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + partial global::B1 Map1(global::A1 source) + { + var target = new global::B1(); + BAccessor.SetValue(target, AAccessor.GetValue(source)); + return target; + } + + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + partial global::B2 Map2(global::A2 source) + { + var target = new global::B2(); + BAccessor.SetValue(target, AAccessor.GetValue(source)); + return target; + } +} + +[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] +static file class AAccessor + where T : global::IA +{ + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "get__value")] + public static extern T GetValue(global::A source); +} + +[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] +static file class BAccessor + where T : global::IA +{ + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "set__value")] + public static extern void SetValue(global::B target, T value); +} \ No newline at end of file