Using pointer syntax as a shorthand for IEnumerable

Another quickie extension to C#. In the current language, a type declaration T! is shorthand for Nullable.

But equally important in modern C# programs are sequences of values, so a similar shorthand for IEnumerable would be ideal. The asterisk symbol is underused (you can suffix a type with asterisk to make a pointer, but only in unsafe contexts), and this was the choice made by the intriguing research language Cω that influenced LINQ, so let’s copy that:

int* numbers = new[] { 1, 6, 55, 4, 11 };

foreach (var n in numbers)
    Console.WriteLine(n);

In Binder_Symbols.cs we find:

case SyntaxKind.PointerType:
    {
        var node = (PointerTypeSyntax)syntax;
        ReportUnsafeIfNotAllowed(node, diagnostics);

        var elementType = BindType(node.ElementType, diagnostics, basesBeingResolved);
        if (elementType.IsManagedType)
        {
            // "Cannot take the address of, get the size of, or declare a pointer to a managed type ('{0}')"
            Error(diagnostics, ErrorCode.ERR_ManagedAddr, node, elementType);
        }

        return new PointerTypeSymbol(elementType);
    }

The call ReportUnsafeIfNotAllowed is what checks for the unsafe context. So I commented that out and added my own check for not-unsafe, before doing something very similar to what happens for T? -> Nullable elsewhere in the same file:

case SyntaxKind.PointerType:
    {
        var node = (PointerTypeSyntax)syntax;

        //ReportUnsafeIfNotAllowed(node, diagnostics);
        if (this.IsIndirectlyInIterator || !this.InUnsafeRegion)
        {
            NamedTypeSymbol enumerableT = GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T, diagnostics, syntax);
            TypeSymbol typeArgument = BindType(node.ElementType, diagnostics, basesBeingResolved);
            NamedTypeSymbol constructedType = enumerableT.Construct(typeArgument);
            if (ShouldCheckConstraints)
            {
                constructedType.CheckConstraints(this.Compilation, this.Conversions, syntax.Location, diagnostics);
            }
            return constructedType;
        }

        var elementType = BindType(node.ElementType, diagnostics, basesBeingResolved);
        if (elementType.IsManagedType)
        {
            // "Cannot take the address of, get the size of, or declare a pointer to a managed type ('{0}')"
            Error(diagnostics, ErrorCode.ERR_ManagedAddr, node, elementType);
        }

        return new PointerTypeSymbol(elementType);
    }

And we’re done! We can now say things like:

static int *Squares(int *nums)
{
    return nums.Select(n => n * n);
}

Roslyn may not be very pretty - it’s a hand-crafted top-down system, so it looks nothing like the “text book” functional parsers. But it’s a real product as opposed to a clean example, so that’s hardly surprising. Regardless, it is proving easy (even fun) to hack.




Not yet regretting the time you've spent here?

Keep reading:

  • Vectors - Intuitions
  • Poorly Structured Notes on AI Part 4
  • Poorly Structured Notes on AI Part 3
  • Poorly Structured Notes on AI Part 2
  • Poorly Structured Notes on AI Part 1