ClangSharp icon indicating copy to clipboard operation
ClangSharp copied to clipboard

Marker interfaces should be partial to support user extensions

Open tompipe opened this issue 1 month ago • 1 comments

Problem

When --generate-marker-interfaces is enabled, generated nested interfaces are not marked as partial, preventing users from manually extending them with custom implementations.

Simple Example

C++ input:

struct IFoo
{
    virtual void Method1() = 0;
    virtual void Method2() = 0;
};

Generated with --generate-marker-interfaces:

public unsafe partial struct IFoo : IFoo.Interface
{
    public interface Interface  // NOT partial - users cannot extend
    {
        void Method1();
        void Method2();
    }
}

User tries to manually add a custom helper method:

// User's manually-written code in a separate file
public partial struct IFoo
{
    public partial interface Interface  // ✗ Error: no partial declaration exists
    {
        void CallBoth();  // User wants to add this to the interface contract
    }
    
    // User's manual implementation
    public void CallBoth() 
    {
        Method1();
        Method2();
    }
}

Generic constraint breaks:

void Process<T>(T foo) where T : unmanaged, IFoo.Interface
{
    foo.CallBoth();  // ✗ Error: Interface doesn't contain CallBoth
}

Solution

Change CSharpOutputBuilder.VisitDecl.cs:829:

WriteIndented("public partial interface Interface");

This allows users to manually extend the interface with custom methods while maintaining backward compatibility (adding partial is non-breaking).

I have a fix ready in my fork.

tompipe avatar Oct 30 '25 18:10 tompipe

As on the PR, this is potentially problematic as it can break the ABI compatibility if users try to use these types with COM wrappers or similar functionality.

Users wanting to extend an interface should typically use extension methods instead.

tannergooding avatar Nov 01 '25 14:11 tannergooding