Skip to content

Try to be strict around non-copyability #2440

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@
<VersionSuffix Condition="$(FullVersion.Contains('-'))">$(FullVersion.Split('-', 2)[1])</VersionSuffix>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Net.Compilers.Toolset" Version="4.0.1">
<PackageReference Include="Microsoft.CSharp" Version="4.*" />
<PackageReference Include="Microsoft.Net.Compilers.Toolset" Version="4.*">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="NonCopyableAnalyzer" Version="0.7.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
23 changes: 23 additions & 0 deletions pythonnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{142A6752
Directory.Build.props = Directory.Build.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NonCopyableAnalyzer", "tools\NonCopyableAnalyzer\NonCopyableAnalyzer.csproj", "{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -187,11 +189,32 @@ Global
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.TraceAlloc|x64.Build.0 = Debug|Any CPU
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.TraceAlloc|x86.ActiveCfg = Debug|Any CPU
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.TraceAlloc|x86.Build.0 = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Debug|x64.ActiveCfg = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Debug|x64.Build.0 = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Debug|x86.ActiveCfg = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Debug|x86.Build.0 = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Release|Any CPU.Build.0 = Release|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Release|x64.ActiveCfg = Release|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Release|x64.Build.0 = Release|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Release|x86.ActiveCfg = Release|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.Release|x86.Build.0 = Release|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.TraceAlloc|Any CPU.ActiveCfg = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.TraceAlloc|Any CPU.Build.0 = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.TraceAlloc|x64.ActiveCfg = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.TraceAlloc|x64.Build.0 = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.TraceAlloc|x86.ActiveCfg = Debug|Any CPU
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF}.TraceAlloc|x86.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C8845072-C642-4858-8627-27E862AD21BB}
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{FE20DBEA-F4F9-4280-AD5E-82B82AE32DDF} = {BC426F42-8494-4AA5-82C9-5109ACD97BD1}
EndGlobalSection
EndGlobal
6 changes: 4 additions & 2 deletions src/runtime/Native/NativeCall.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;

namespace Python.Runtime
{
Expand All @@ -9,12 +10,13 @@ namespace Python.Runtime
/// situations (specifically, calling functions through Python
/// type structures) where we need to call functions indirectly.
/// </summary>
[ExcludeFromCodeCoverage]
internal unsafe class NativeCall
{
public static void CallDealloc(IntPtr fp, StolenReference a1)
{
var d = (delegate* unmanaged[Cdecl]<StolenReference, void>)fp;
d(a1);
var d = (delegate* unmanaged[Cdecl]<IntPtr, void>)fp;
d(a1.DangerousGetAddress());
}

public static NewReference Call_3(IntPtr fp, BorrowedReference a1, BorrowedReference a2, BorrowedReference a3)
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/Native/NewReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public void Dispose()
/// </summary>
[Pure]
public static NewReference DangerousFromPointer(IntPtr pointer)
=> new() { pointer = pointer};
=> new NewReference { pointer = pointer };

[Pure]
internal static IntPtr DangerousGetAddressOrNull(in NewReference reference) => reference.pointer;
Expand Down
4 changes: 4 additions & 0 deletions src/runtime/Python.Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,9 @@
<PackageReference Include="Lost.Compat.NullabilityAttributes" Version="0.0.4" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
<ProjectReference Include="..\..\tools\NonCopyableAnalyzer\NonCopyableAnalyzer.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<OutputItemType>Analyzer</OutputItemType>
</ProjectReference>
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions src/runtime/PythonTypes/PyFloat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,6 @@ public static PyFloat AsFloat(PyObject value)
public double ToDouble() => Runtime.PyFloat_AsDouble(obj);

public override TypeCode GetTypeCode() => TypeCode.Double;
public override int GetHashCode() => ((PyObject)this).GetHashCode();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it would just stackoverflow

}
}
1 change: 1 addition & 0 deletions src/runtime/PythonTypes/PyInt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,5 +232,6 @@ public string ToString(string format, IFormatProvider formatProvider)
}

public override TypeCode GetTypeCode() => TypeCode.Int64;
public override int GetHashCode() => ((PyObject)this).GetHashCode();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

}
}
12 changes: 6 additions & 6 deletions src/runtime/Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ internal static T TryUsingDll<T>(Func<T> op)
/// </summary>
/// <param name="ob">PyObject Ptr</param>

internal static void Py_DecRef(StolenReference ob) => Delegates.Py_DecRef(ob);
internal static void Py_DecRef(StolenReference ob) => Delegates.Py_DecRef(ob.AnalyzerWorkaround());


internal static void Py_Initialize() => Delegates.Py_Initialize();
Expand Down Expand Up @@ -1459,7 +1459,7 @@ internal static bool PyList_Check(BorrowedReference ob)

internal static BorrowedReference PyList_GetItem(BorrowedReference pointer, nint index) => Delegates.PyList_GetItem(pointer, index);

internal static int PyList_SetItem(BorrowedReference pointer, nint index, StolenReference value) => Delegates.PyList_SetItem(pointer, index, value);
internal static int PyList_SetItem(BorrowedReference pointer, nint index, StolenReference value) => Delegates.PyList_SetItem(pointer, index, value.AnalyzerWorkaround());

internal static int PyList_Insert(BorrowedReference pointer, nint index, BorrowedReference value) => Delegates.PyList_Insert(pointer, index, value);

Expand Down Expand Up @@ -1497,7 +1497,7 @@ internal static int PyTuple_SetItem(BorrowedReference pointer, nint index, Borro
return PyTuple_SetItem(pointer, index, newRef.Steal());
}

internal static int PyTuple_SetItem(BorrowedReference pointer, nint index, StolenReference value) => Delegates.PyTuple_SetItem(pointer, index, value);
internal static int PyTuple_SetItem(BorrowedReference pointer, nint index, StolenReference value) => Delegates.PyTuple_SetItem(pointer, index, value.AnalyzerWorkaround());

internal static NewReference PyTuple_GetSlice(BorrowedReference pointer, nint start, nint end) => Delegates.PyTuple_GetSlice(pointer, start, end);

Expand Down Expand Up @@ -1657,7 +1657,7 @@ internal static bool PyType_IsSameAsOrSubtype(BorrowedReference type, BorrowedRe
internal static NewReference PyObject_GenericGetDict(BorrowedReference o) => PyObject_GenericGetDict(o, IntPtr.Zero);
internal static NewReference PyObject_GenericGetDict(BorrowedReference o, IntPtr context) => Delegates.PyObject_GenericGetDict(o, context);

internal static void PyObject_GC_Del(StolenReference ob) => Delegates.PyObject_GC_Del(ob);
internal static void PyObject_GC_Del(StolenReference ob) => Delegates.PyObject_GC_Del(ob.AnalyzerWorkaround());


internal static bool PyObject_GC_IsTracked(BorrowedReference ob)
Expand Down Expand Up @@ -1720,7 +1720,7 @@ internal static void PyErr_SetString(BorrowedReference ob, string message)
internal static void PyErr_Fetch(out NewReference type, out NewReference val, out NewReference tb) => Delegates.PyErr_Fetch(out type, out val, out tb);


internal static void PyErr_Restore(StolenReference type, StolenReference val, StolenReference tb) => Delegates.PyErr_Restore(type, val, tb);
internal static void PyErr_Restore(StolenReference type, StolenReference val, StolenReference tb) => Delegates.PyErr_Restore(type.AnalyzerWorkaround(), val.AnalyzerWorkaround(), tb.AnalyzerWorkaround());


internal static void PyErr_Clear() => Delegates.PyErr_Clear();
Expand All @@ -1738,7 +1738,7 @@ internal static NewReference PyException_GetTraceback(BorrowedReference ex)
/// Set the cause associated with the exception to cause. Use NULL to clear it. There is no type check to make sure that cause is either an exception instance or None. This steals a reference to cause.
/// </summary>
internal static void PyException_SetCause(BorrowedReference ex, StolenReference cause)
=> Delegates.PyException_SetCause(ex, cause);
=> Delegates.PyException_SetCause(ex, cause.AnalyzerWorkaround());
internal static int PyException_SetTraceback(BorrowedReference ex, BorrowedReference tb)
=> Delegates.PyException_SetTraceback(ex, tb);

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/Types/Iterator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ public static NewReference tp_iternext(BorrowedReference ob)
return Converter.ToPython(item, self.elemType);
}

public static NewReference tp_iter(BorrowedReference ob) => new (ob);
public static NewReference tp_iter(BorrowedReference ob) => new NewReference(ob);
}
}
4 changes: 2 additions & 2 deletions src/runtime/Types/ManagedType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ internal static unsafe void DecrefTypeAndFree(StolenReference ob)

var freePtr = Util.ReadIntPtr(type, TypeOffset.tp_free);
Debug.Assert(freePtr != IntPtr.Zero);
var free = (delegate* unmanaged[Cdecl]<StolenReference, void>)freePtr;
free(ob);
var free = (delegate* unmanaged[Cdecl]<ref StolenReference, void>)freePtr;
free(ref ob);
Comment on lines +86 to +87
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is wrong. It does not say anywhere but I'd expect tp_free to take a reference to PyObject (e.g. PyObject*), not a reference to reference to it (e.g. not a PyObject**).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that's probably one reason for the crashes :)


Runtime.XDecref(StolenReference.DangerousFromPointer(type.DangerousGetAddress()));
}
Expand Down
Empty file.
5 changes: 5 additions & 0 deletions tools/NonCopyableAnalyzer/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|--------------------
NoCopy99|Correction| Warning | N/A
Loading
Loading