Skip to content

Commit

Permalink
[c#] Add support for System.Collections.Immutable collections
Browse files Browse the repository at this point in the history
Co-authored-by: Christopher Warrington <[email protected]>
  • Loading branch information
enioluwas and chwarr authored Nov 30, 2022
1 parent 39d23f7 commit b13fee1
Show file tree
Hide file tree
Showing 19 changed files with 448 additions and 23 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ different versioning scheme, following the Haskell community's
code will be generated for them. See [issue \#1131, Bond-over-gRPC will be
deprecated February 2022](https://github.com/microsoft/bond/issues/1131),
for the full announcement.
* Added codegen and deserialization support for container type aliases to
use
[System.Collections.Immutable](https://learn.microsoft.com/dotnet/api/system.collections.immutable)
collections. (Pull request
[\#1161](https://github.com/microsoft/bond/pull/1161))

## 10.0: 2022-03-07 ##

Expand Down
8 changes: 7 additions & 1 deletion compiler/src/Language/Bond/Codegen/Cs/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ module Language.Bond.Codegen.Cs.Util
import Data.Int (Int64)
import Data.Monoid
import Prelude
import Data.Text.Lazy (Text)
import Data.Text.Lazy (Text, isPrefixOf)
import Data.Text.Lazy.Builder (toLazyText)
import Text.Shakespeare.Text
import Paths_bond (version)
import Data.Version (showVersion)
Expand Down Expand Up @@ -112,15 +113,20 @@ paramConstraints = newlineBeginSep 2 constraint
constraint (TypeParam _ Nothing) = mempty
constraint (TypeParam name (Just Value)) = [lt|where #{name} : struct|]

isImmutableCollection :: MappingContext -> Type -> Bool
isImmutableCollection cs t = [lt|System.Collections.Immutable.Immutable|] `isPrefixOf` toLazyText (getInstanceTypeName cs t)

-- Initial value for C# field/property or Nothing if C# implicit default is OK
defaultValue :: MappingContext -> Field -> Maybe Text
defaultValue cs Field {fieldDefault = Nothing, ..} = implicitDefault fieldType
where
newInstance t = Just [lt|new #{getInstanceTypeName cs t}()|]
staticEmptyField t = Just [lt|#{getInstanceTypeName cs t}.Empty|]
implicitDefault (BT_Bonded t) = Just [lt|global::Bond.Bonded<#{getTypeName cs t}>.Empty|]
implicitDefault t@(BT_TypeParam _) = Just [lt|global::Bond.GenericFactory.Create<#{getInstanceTypeName cs t}>()|]
implicitDefault t@BT_Blob = newInstance t
implicitDefault t@(BT_UserDefined a@Alias {..} args)
| isImmutableCollection cs t = staticEmptyField t
| customAliasMapping cs a = newInstance t
| otherwise = implicitDefault $ resolveAlias a args
implicitDefault t
Expand Down
10 changes: 10 additions & 0 deletions compiler/tests/TestMain.hs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ tests = testGroup "Compiler tests"
, verifyCsCodegen "inheritance"
, verifyCsCodegen "aliases"
, verifyCsCodegen "complex_inheritance"
, verifyCodegen
[ "c#"
, "--using=ImmutableArray=System.Collections.Immutable.ImmutableArray<{0}>"
, "--using=ImmutableList=System.Collections.Immutable.ImmutableList<{0}>"
, "--using=ImmutableHashSet=System.Collections.Immutable.ImmutableHashSet<{0}>"
, "--using=ImmutableSortedSet=System.Collections.Immutable.ImmutableSortedSet<{0}>"
, "--using=ImmutableDictionary=System.Collections.Immutable.ImmutableDictionary<{0},{1}>"
, "--using=ImmutableSortedDictionary=System.Collections.Immutable.ImmutableSortedDictionary<{0},{1}>"
]
"immutable_collections"
, verifyCodegenVariation
[ "c#"
, "--preview-constructor-parameters"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@


// suppress "Missing XML comment for publicly visible type or member"
#pragma warning disable 1591


#region ReSharper warnings
// ReSharper disable PartialTypeWithSinglePart
// ReSharper disable RedundantNameQualifier
// ReSharper disable InconsistentNaming
// ReSharper disable CheckNamespace
// ReSharper disable UnusedParameter.Local
// ReSharper disable RedundantUsingDirective
#endregion

namespace tests
{
using System.Collections.Generic;

[global::Bond.Schema]
[System.CodeDom.Compiler.GeneratedCode("gbc", "0.12.1.0")]
public partial class ImmutableCollectionsHolder
{
[global::Bond.Id(0)]
public System.Collections.Immutable.ImmutableArray<string> ImmutableArrayString { get; set; }

[global::Bond.Id(1)]
public System.Collections.Immutable.ImmutableList<string> ImmutableListString { get; set; }

[global::Bond.Id(2)]
public System.Collections.Immutable.ImmutableHashSet<string> ImmutableHashSetString { get; set; }

[global::Bond.Id(3)]
public System.Collections.Immutable.ImmutableSortedSet<int> ImmutableSortedSetInt { get; set; }

[global::Bond.Id(4)]
public System.Collections.Immutable.ImmutableDictionary<string,string> ImmutableDictionaryStringMap { get; set; }

[global::Bond.Id(5)]
public System.Collections.Immutable.ImmutableSortedDictionary<int,string> ImmutableSortedDictionaryStringMap { get; set; }

public ImmutableCollectionsHolder()
: this("tests.ImmutableCollectionsHolder", "ImmutableCollectionsHolder")
{}

protected ImmutableCollectionsHolder(string fullName, string name)
{
ImmutableArrayString = System.Collections.Immutable.ImmutableArray<string>.Empty;
ImmutableListString = System.Collections.Immutable.ImmutableList<string>.Empty;
ImmutableHashSetString = System.Collections.Immutable.ImmutableHashSet<string>.Empty;
ImmutableSortedSetInt = System.Collections.Immutable.ImmutableSortedSet<int>.Empty;
ImmutableDictionaryStringMap = System.Collections.Immutable.ImmutableDictionary<string,string>.Empty;
ImmutableSortedDictionaryStringMap = System.Collections.Immutable.ImmutableSortedDictionary<int,string>.Empty;
}
}
} // tests
56 changes: 56 additions & 0 deletions compiler/tests/generated/immutable_collections_types.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@


// suppress "Missing XML comment for publicly visible type or member"
#pragma warning disable 1591


#region ReSharper warnings
// ReSharper disable PartialTypeWithSinglePart
// ReSharper disable RedundantNameQualifier
// ReSharper disable InconsistentNaming
// ReSharper disable CheckNamespace
// ReSharper disable UnusedParameter.Local
// ReSharper disable RedundantUsingDirective
#endregion

namespace tests
{
using System.Collections.Generic;

[global::Bond.Schema]
[System.CodeDom.Compiler.GeneratedCode("gbc", "0.12.1.0")]
public partial class ImmutableCollectionsHolder
{
[global::Bond.Id(0)]
public System.Collections.Immutable.ImmutableArray<string> ImmutableArrayString { get; set; }

[global::Bond.Id(1)]
public System.Collections.Immutable.ImmutableList<string> ImmutableListString { get; set; }

[global::Bond.Id(2)]
public System.Collections.Immutable.ImmutableHashSet<string> ImmutableHashSetString { get; set; }

[global::Bond.Id(3)]
public System.Collections.Immutable.ImmutableSortedSet<int> ImmutableSortedSetInt { get; set; }

[global::Bond.Id(4)]
public System.Collections.Immutable.ImmutableDictionary<string,string> ImmutableDictionaryStringMap { get; set; }

[global::Bond.Id(5)]
public System.Collections.Immutable.ImmutableSortedDictionary<int,string> ImmutableSortedDictionaryStringMap { get; set; }

public ImmutableCollectionsHolder()
: this("tests.ImmutableCollectionsHolder", "ImmutableCollectionsHolder")
{}

protected ImmutableCollectionsHolder(string fullName, string name)
{
ImmutableArrayString = System.Collections.Immutable.ImmutableArray<string>.Empty;
ImmutableListString = System.Collections.Immutable.ImmutableList<string>.Empty;
ImmutableHashSetString = System.Collections.Immutable.ImmutableHashSet<string>.Empty;
ImmutableSortedSetInt = System.Collections.Immutable.ImmutableSortedSet<int>.Empty;
ImmutableDictionaryStringMap = System.Collections.Immutable.ImmutableDictionary<string,string>.Empty;
ImmutableSortedDictionaryStringMap = System.Collections.Immutable.ImmutableSortedDictionary<int,string>.Empty;
}
}
} // tests
18 changes: 18 additions & 0 deletions compiler/tests/schema/immutable_collections.bond
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace tests

using ImmutableArray<T> = list<T>;
using ImmutableList<T> = list<T>;
using ImmutableHashSet<T> = set<T>;
using ImmutableSortedSet<T> = set<T>;
using ImmutableDictionary<K, V> = map<K, V>;
using ImmutableSortedDictionary<K, V> = map<K, V>;

struct ImmutableCollectionsHolder
{
0: ImmutableArray<string> ImmutableArrayString;
1: ImmutableList<string> ImmutableListString;
2: ImmutableHashSet<string> ImmutableHashSetString;
3: ImmutableSortedSet<int32> ImmutableSortedSetInt;
4: ImmutableDictionary<string, string> ImmutableDictionaryStringMap;
5: ImmutableSortedDictionary<int32, string> ImmutableSortedDictionaryStringMap;
}
21 changes: 21 additions & 0 deletions cs/cs.sln
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "default-ignore-output", "te
{21E175D5-BBDD-4B63-8FB7-38899BF2F9D1} = {21E175D5-BBDD-4B63-8FB7-38899BF2F9D1}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "immutable_collections", "..\examples\cs\core\immutable_collections\immutable_collections.csproj", "{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -881,6 +883,24 @@ Global
{98BF6B10-F821-4402-8923-5F7B726BBFC8}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{98BF6B10-F821-4402-8923-5F7B726BBFC8}.Release|Win32.ActiveCfg = Release|Any CPU
{98BF6B10-F821-4402-8923-5F7B726BBFC8}.Release|Win32.Build.0 = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Debug|Win32.ActiveCfg = Debug|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Debug|Win32.Build.0 = Debug|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Fields|Any CPU.ActiveCfg = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Fields|Any CPU.Build.0 = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Fields|Mixed Platforms.ActiveCfg = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Fields|Mixed Platforms.Build.0 = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Fields|Win32.ActiveCfg = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Fields|Win32.Build.0 = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Release|Any CPU.Build.0 = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Release|Win32.ActiveCfg = Release|Any CPU
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6}.Release|Win32.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -920,6 +940,7 @@ Global
{4FACD9A1-B8AC-4D76-B4B5-B71607EA8F8C} = {621A2166-EEE0-4A27-88AA-5BE5AC996452}
{3805B86E-2BE3-4FFA-A11C-B4981069EC88} = {4268A1D3-AF40-4120-B021-D95A0F754221}
{98BF6B10-F821-4402-8923-5F7B726BBFC8} = {8CF1F5CD-548F-4323-869E-AA5903C88B6A}
{9CC2754E-501D-4DB3-8EAA-8B91025E5DF6} = {621A2166-EEE0-4A27-88AA-5BE5AC996452}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6EB58560-CA9C-4F6C-B916-CCA6C7FE2A2B}
Expand Down
4 changes: 4 additions & 0 deletions cs/nuget/bond.csharp.test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="NUnit" Version="3.10.*" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.*" />
<PackageReference Include="System.Collections.Immutable" Version="1.7.1" />
</ItemGroup>

<ItemGroup>
Expand All @@ -39,5 +40,8 @@
<BondCodegen Update="..\test\core\UnitTest.bond">
<Options>$(BondOptions) --using="DateTime=System.DateTime"</Options>
</BondCodegen>
<BondCodegen Update="ImmutableCollections.bond">
<Options>$(BondOptions) --using="ImmutableArray=System.Collections.Immutable.ImmutableArray&lt;{0}&gt;" --using="ImmutableList=System.Collections.Immutable.ImmutableList&lt;{0}&gt;" --using="ImmutableHashSet=System.Collections.Immutable.ImmutableHashSet&lt;{0}&gt;" --using="ImmutableSortedSet=System.Collections.Immutable.ImmutableSortedSet&lt;{0}&gt;" --using="ImmutableDictionary=System.Collections.Immutable.ImmutableDictionary&lt;{0},{1}&gt;" --using="ImmutableSortedDictionary=System.Collections.Immutable.ImmutableSortedDictionary&lt;{0},{1}&gt;"</Options>
</BondCodegen>
</ItemGroup>
</Project>
11 changes: 10 additions & 1 deletion cs/src/core/Comparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,16 @@ static Expression ObjectsEqual(Expression left, Expression right)
return Expression.Call(null, comparerEqual.MakeGenericMethod(type), left, right);

if (type.IsBondContainer())
return EnumerablesEqual(left, right);
{
if (type.IsValueType())
{
return StructsEqual(left, right);
}
else
{
return EnumerablesEqual(left, right);
}
}

if (type.IsGenericType() && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
return KeyValuePairEqual(left, right);
Expand Down
Loading

0 comments on commit b13fee1

Please sign in to comment.