Closed kindermannhubert closed 8 months ago
Is it possible to intercept the abstract syntax tree and append a call to .ToString()
/.ToArray()
? That's the only way I can see this being fixed since Span<T>
is by-design a stack-only type, and bypassing this would probably lead to undefined behavior.
The only problem with that is that not all ref-structs are so easily convertible. Take the following type for example.
ref struct A;
You cannot call .ToString()
on it because that comes from ValueType
, A
cannot be passed into generic methods either. You would need to scaffold an entire function inside of the script context, and inject that function, which I think is a bit overkill.
Looking at other REPLs, they don't handle this at all.
Mono REPL:
csharp> Span<int>.Empty
(1,12): error CS0029: Cannot implicitly convert type `System.Span<int>' to `object'
Yeah, that's a non-trivial problem. At first, I thought the problem is our formatting (I didn't read the callstack carefully) but it is a limitation of Roslyn's scripting engine.
I have a working prototype of suggested input interception. I believe it should suffice.
For Span<T>
/ ReadonlySpan<T>
I will slice it to get the first 1000 (?) items and call ToArray() (if the span is >1000 we should write an error, that the result will be truncated).
For other ref structs I would output just some error that we cannot do it.
Hm, maybe the interception shouldn't return T[]
but some custom type:
class __Span<T>
{
public readonly T[] Array;
public readonly OriginalLength;
//custom ToString
}
So the output would be more clear and instead of char[5]
(from the example) there would be something like ReadOnlySpan<char>(Length: 5)
.
If possible though, do add the edgecase that ReadOnlySpan<char>
/Span<char>
has over other types. Specifically .ToString()
should call new string(char[])
if typeof(T) == typeof(char)
, and keep the array otherwise.
I wonder if this issue is related to the problem I'm having. Basically, when I run this:
ReadOnlySpan<byte> test = "test"u8;
I get
(1,1): error CS8345: Field or auto-implemented property cannot be of type 'ReadOnlySpan<byte>' unless it is an instance member of a ref struct.
But saving it to a file and dotnet run
causes no errors.
In dotnet run
, that's a top-level statement.
[CompilerGenerated]
internal class Program
{
private unsafe static void <Main>$(string[] args)
{
ReadOnlySpan<byte> readOnlySpan = "test"u8;
}
}
In a REPL context, you're inside a class
, and any variables you declare are actually part of its fields.
public sealed class Submission#0
{
public ReadOnlySpan<byte> test;
[SpecialName]
public async global::System.Threading.Tasks.Task<object> <Initialize>()
{
test = "test"u8;
return null;
}
public Submission#0(object[] submissionArray)
{
submissionArray[1] = this;
}
[SpecialName]
public static global::System.Threading.Tasks.Task<object> <Factory>(object[] submissionArray)
{
return new Submission#0(submissionArray).<Initialize>();
}
}
ReadOnlySpan<T>
is a ref struct
: It must live on the stack at all times. A class by design is always heap-allocated, so therefore you cannot put a span as a field, and this error message makes sense. You can however work around it by wrapping it:
new Action(() =>
{
ReadOnlySpan<byte> test = "test"u8;
})()
Hope this helps!
This is super helpful - many thanks!
Version
last
What happened?
"hello".AsSpan()