Skip to content
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

Fixing C Incomplete Array to be treated as a Constant Array of length 1 #374

Merged
merged 1 commit into from
Aug 14, 2022
Merged
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
92 changes: 50 additions & 42 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -443,15 +443,18 @@ private void VisitFieldDecl(FieldDecl fieldDecl)

_outputBuilder.BeginField(in desc);

if (type.CanonicalType is ConstantArrayType constantArrayType)
if (type.CanonicalType is ConstantArrayType or IncompleteArrayType)
{
var count = Math.Max(constantArrayType.Size, 1).ToString();
var elementType = constantArrayType.ElementType;
while (elementType.CanonicalType is ConstantArrayType subConstantArrayType)
var arrayType = (ArrayType)type.CanonicalType;

var count = Math.Max((arrayType as ConstantArrayType)?.Size ?? 0, 1).ToString();
var elementType = arrayType.ElementType;
while (elementType.CanonicalType is ConstantArrayType or IncompleteArrayType)
{
var subArrayType = (ArrayType)elementType.CanonicalType;
count += " * ";
count += Math.Max(subConstantArrayType.Size, 1).ToString();
elementType = subConstantArrayType.ElementType;
count += Math.Max((subArrayType as ConstantArrayType)?.Size ?? 0, 1).ToString();
elementType = subArrayType.ElementType;
}

_outputBuilder.WriteFixedCountField(typeName, escapedName, GetArtificialFixedSizedBufferName(fieldDecl), count);
Expand Down Expand Up @@ -893,7 +896,7 @@ private void VisitIndirectFieldDecl(IndirectFieldDecl indirectFieldDecl)
_outputBuilder.WriteDivider(true);
_outputBuilder.BeginField(in desc);

var isFixedSizedBuffer = type.CanonicalType is ConstantArrayType;
var isFixedSizedBuffer = type.CanonicalType is ConstantArrayType or IncompleteArrayType;
var generateCompatibleCode = _config.GenerateCompatibleCode;
var typeString = string.Empty;

Expand Down Expand Up @@ -1027,7 +1030,7 @@ private void VisitIndirectFieldDecl(IndirectFieldDecl indirectFieldDecl)
if (isSupportedFixedSizedBufferType)
{
code.Write("[0], ");
code.Write(((ConstantArrayType)type.CanonicalType).Size);
code.Write(Math.Max((type.CanonicalType as ConstantArrayType)?.Size ?? 0, 1));
}
else
{
Expand Down Expand Up @@ -1730,9 +1733,9 @@ private void VisitRecordDecl(RecordDecl recordDecl)

Visit(recordDecl.Decls, excludedCursors);

foreach (var constantArray in recordDecl.Fields.Where((field) => field.Type.CanonicalType is ConstantArrayType))
foreach (var array in recordDecl.Fields.Where((field) => field.Type.CanonicalType is ConstantArrayType or IncompleteArrayType))
{
VisitConstantArrayFieldDecl(recordDecl, constantArray);
VisitConstantOrIncompleteArrayFieldDecl(recordDecl, array);
}

if (hasVtbl || hasBaseVtbl)
Expand Down Expand Up @@ -2689,15 +2692,15 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl,
previousSize = Math.Max(previousSize, currentSize);
}

void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)
void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantOrIncompleteArray)
{
Debug.Assert(constantArray.Type.CanonicalType is ConstantArrayType);
Debug.Assert(constantOrIncompleteArray.Type.CanonicalType is ConstantArrayType or IncompleteArrayType);

var outputBuilder = _outputBuilder;
var type = (ConstantArrayType)constantArray.Type.CanonicalType;
var typeName = GetRemappedTypeName(constantArray, context: null, constantArray.Type, out _);
var arrayType = (ArrayType)constantOrIncompleteArray.Type.CanonicalType;
var arrayTypeName = GetRemappedTypeName(constantOrIncompleteArray, context: null, constantOrIncompleteArray.Type, out _);

if (IsSupportedFixedSizedBufferType(typeName))
if (IsSupportedFixedSizedBufferType(arrayTypeName))
{
return;
}
Expand All @@ -2707,35 +2710,40 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)
var alignment = Math.Max(recordDecl.TypeForDecl.Handle.AlignOf, 1);
var maxAlignm = recordDecl.Fields.Any() ? recordDecl.Fields.Max((fieldDecl) => Math.Max(fieldDecl.Type.Handle.AlignOf, 1)) : alignment;

var accessSpecifier = GetAccessSpecifier(constantArray, matchStar: false);
var canonicalElementType = type.ElementType.CanonicalType;
var accessSpecifier = GetAccessSpecifier(constantOrIncompleteArray, matchStar: false);
var canonicalElementType = arrayType.ElementType.CanonicalType;
var isUnsafeElementType =
((canonicalElementType is PointerType) || (canonicalElementType is ReferenceType)) &&
(typeName != "IntPtr") && (typeName != "UIntPtr");
(arrayTypeName != "IntPtr") && (arrayTypeName != "UIntPtr");

var name = GetArtificialFixedSizedBufferName(constantArray);
var name = GetArtificialFixedSizedBufferName(constantOrIncompleteArray);
var escapedName = EscapeName(name);

var totalSize = Math.Max(type.Size, 1);
var sizePerDimension = new List<(long index, long size)>() {(0, type.Size)};
var arraySize = Math.Max((arrayType as ConstantArrayType)?.Size ?? 0, 1);
var totalSize = arraySize;
var sizePerDimension = new List<(long index, long size)>() {(0, arraySize) };

var elementType = type.ElementType;
var elementType = arrayType.ElementType;

while (elementType.CanonicalType is ConstantArrayType subConstantArrayType)
while (elementType.CanonicalType is ConstantArrayType or IncompleteArrayType)
{
totalSize *= Math.Max(subConstantArrayType.Size, 1);
sizePerDimension.Add((0, Math.Max(subConstantArrayType.Size, 1)));
elementType = subConstantArrayType.ElementType;
var subArrayType = (ArrayType)elementType.CanonicalType;

var subArraySize = Math.Max((subArrayType as ConstantArrayType)?.Size ?? 0, 1);
totalSize *= subArraySize;
sizePerDimension.Add((0, subArraySize));

elementType = subArrayType.ElementType;
}

long alignment32 = -1;
long alignment64 = -1;

GetTypeSize(constantArray, constantArray.Type, ref alignment32, ref alignment64, out var size32, out var size64);
GetTypeSize(constantOrIncompleteArray, constantOrIncompleteArray.Type, ref alignment32, ref alignment64, out var size32, out var size64);

if ((size32 == 0 || size64 == 0) && _testOutputBuilder != null)
{
AddDiagnostic(DiagnosticLevel.Info, $"{escapedName} (constant array field) has a size of 0", constantArray);
AddDiagnostic(DiagnosticLevel.Info, $"{escapedName} (constant array field) has a size of 0", constantOrIncompleteArray);
}

var desc = new StructDesc {
Expand All @@ -2751,15 +2759,15 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)
MaxFieldAlignment = maxAlignm,
Kind = LayoutKind.Sequential
},
Location = constantArray.Location,
Location = constantOrIncompleteArray.Location,
IsNested = true,
WriteCustomAttrs = static context => {
(var fieldDecl, var generator) = ((FieldDecl, PInvokeGenerator))context;

generator.WithAttributes(fieldDecl);
generator.WithUsings(fieldDecl);
},
CustomAttrGeneratorData = (constantArray, this),
CustomAttrGeneratorData = (constantOrIncompleteArray, this),
};

_outputBuilder.BeginStruct(in desc);
Expand Down Expand Up @@ -2804,11 +2812,11 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)
EscapedName = fieldName,
Offset = null,
NeedsNewKeyword = false,
Location = constantArray.Location,
Location = constantOrIncompleteArray.Location,
};

_outputBuilder.BeginField(in fieldDesc);
_outputBuilder.WriteRegularField(typeName, fieldName);
_outputBuilder.WriteRegularField(arrayTypeName, fieldName);
_outputBuilder.EndField(in fieldDesc);
if (!separateStride)
{
Expand All @@ -2821,7 +2829,7 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)
if (generateCompatibleCode || isUnsafeElementType)
{
_outputBuilder.BeginIndexer(AccessSpecifier.Public, generateCompatibleCode && !isUnsafeElementType);
_outputBuilder.WriteIndexer($"ref {typeName}");
_outputBuilder.WriteIndexer($"ref {arrayTypeName}");
_outputBuilder.BeginIndexerParameters();
var param = new ParameterDesc {
Name = "index",
Expand All @@ -2836,7 +2844,7 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)
var code = _outputBuilder.BeginCSharpCode();

code.WriteIndented("fixed (");
code.Write(typeName);
code.Write(arrayTypeName);
code.Write("* pThis = &");
code.Write(firstFieldName);
code.WriteLine(')');
Expand All @@ -2854,7 +2862,7 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)
else
{
_outputBuilder.BeginIndexer(AccessSpecifier.Public, false);
_outputBuilder.WriteIndexer($"ref {typeName}");
_outputBuilder.WriteIndexer($"ref {arrayTypeName}");
_outputBuilder.BeginIndexerParameters();
var param = new ParameterDesc {
Name = "index",
Expand All @@ -2872,7 +2880,7 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)

code.WriteIndented("return ref AsSpan(");

if (type.Size == 1)
if (arraySize == 1)
{
code.Write("int.MaxValue");
}
Expand All @@ -2892,8 +2900,8 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)
IsAggressivelyInlined = _config.GenerateAggressiveInlining,
IsStatic = false,
IsMemberFunction = true,
ReturnType = $"Span<{typeName}>",
Location = constantArray.Location,
ReturnType = $"Span<{arrayTypeName}>",
Location = constantOrIncompleteArray.Location,
HasBody = true,
};

Expand All @@ -2902,7 +2910,7 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)

_outputBuilder.BeginFunctionInnerPrototype(in function);

if (type.Size == 1)
if (arraySize == 1)
{
param = new ParameterDesc {
Name = "length",
Expand All @@ -2921,7 +2929,7 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)
code.Write(firstFieldName);
code.Write(", ");

if (type.Size == 1)
if (arraySize == 1)
{
code.Write("length");
}
Expand Down Expand Up @@ -3027,7 +3035,7 @@ void ForPointeeType(TypedefDecl typedefDecl, Type parentType, Type pointeeType,
{
ForPointeeType(typedefDecl, typedefType, typedefType.Decl.UnderlyingType, onlyHandleRemappings);
}
else if (pointeeType is not ConstantArrayType and not BuiltinType and not TagType and not TemplateTypeParmType)
else if (pointeeType is not ConstantArrayType and not IncompleteArrayType and not BuiltinType and not TagType and not TemplateTypeParmType)
{
AddDiagnostic(DiagnosticLevel.Error, $"Unsupported pointee type: '{pointeeType.TypeClassSpelling}'. Generating bindings may be incomplete.", typedefDecl);
}
Expand Down Expand Up @@ -3214,7 +3222,7 @@ private void VisitVarDecl(VarDecl varDecl)
flags |= ValueFlags.Initializer;
}

if (type.IsLocalConstQualified || isMacroDefinitionRecord || (type is ConstantArrayType))
if (type.IsLocalConstQualified || isMacroDefinitionRecord || (type is ConstantArrayType or IncompleteArrayType))
{
flags |= ValueFlags.Constant;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1049,9 +1049,9 @@ long CalculateRootSize(InitListExpr initListExpr, ArrayType arrayType, bool isUn
}
long size = -1;

if (arrayType is ConstantArrayType constantArrayType)
if (arrayType is ConstantArrayType or IncompleteArrayType)
{
size = constantArrayType.Size;
size = Math.Max((arrayType as ConstantArrayType)?.Size ?? 0, 1);
}
else
{
Expand Down
43 changes: 18 additions & 25 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2582,9 +2582,18 @@ private string GetRemappedTypeName(Cursor cursor, Cursor context, Type type, out
{
var canonicalType = type.CanonicalType;

if ((canonicalType is ConstantArrayType constantArrayType) && (constantArrayType.ElementType is RecordType))
if (canonicalType is ConstantArrayType or IncompleteArrayType)
{
canonicalType = constantArrayType.ElementType;
var arrayType = (ArrayType)canonicalType;

if (arrayType.ElementType is RecordType)
{
canonicalType = arrayType.ElementType;
}
}
else if ((canonicalType is IncompleteArrayType incompleteArrayType) && (incompleteArrayType.ElementType is RecordType))
{
canonicalType = incompleteArrayType.ElementType;
}

if ((canonicalType is RecordType recordType) && remappedName.StartsWith("__AnonymousRecord_"))
Expand Down Expand Up @@ -2773,7 +2782,7 @@ private string GetTypeName(Cursor cursor, Cursor context, Type rootType, Type ty
{
result.typeName = GetTypeName(cursor, context, rootType, arrayType.ElementType, ignoreTransparentStructsWhereRequired, out _);

if ((cursor is FunctionDecl or ParmVarDecl) || (arrayType is IncompleteArrayType))
if (cursor is FunctionDecl or ParmVarDecl)
{
result.typeName = GetRemappedName(result.typeName, cursor, tryRemapOperatorName: false, out _, skipUsing: true);
result.typeName += '*';
Expand Down Expand Up @@ -3270,29 +3279,13 @@ private void GetTypeSize(Cursor cursor, Type type, ref long alignment32, ref lon

if (type is ArrayType arrayType)
{
if (type is ConstantArrayType constantArrayType)
{
GetTypeSize(cursor, arrayType.ElementType, ref alignment32, ref alignment64, ref has8BytePrimitiveField, out var elementSize32, out var elementSize64);

size32 = elementSize32 * Math.Max(constantArrayType.Size, 1);
size64 = elementSize64 * Math.Max(constantArrayType.Size, 1);

if (alignment32 == -1)
{
alignment32 = elementSize32;
}

if (alignment64 == -1)
{
alignment64 = elementSize64;
}
}
else if (type is IncompleteArrayType)
if (type is ConstantArrayType or IncompleteArrayType)
{
GetTypeSize(cursor, arrayType.ElementType, ref alignment32, ref alignment64, ref has8BytePrimitiveField, out var elementSize32, out var elementSize64);
var count = Math.Max((arrayType as ConstantArrayType)?.Size ?? 0, 1);
GetTypeSize(cursor, arrayType.ElementType, ref alignment32, ref alignment64, ref has8BytePrimitiveField, out var elementSize32, out var elementSize64);

size32 = elementSize32;
size64 = elementSize64;
size32 = elementSize32 * Math.Max(count, 1);
size64 = elementSize64 * Math.Max(count, 1);

if (alignment32 == -1)
{
Expand Down Expand Up @@ -5099,7 +5092,7 @@ private bool IsUnsafe(FieldDecl fieldDecl)
{
var type = fieldDecl.Type;

if (type.CanonicalType is ConstantArrayType)
if (type.CanonicalType is ConstantArrayType or IncompleteArrayType)
{
var name = GetTypeName(fieldDecl, context: null, type, ignoreTransparentStructsWhereRequired: false, out _);

Expand Down
1 change: 0 additions & 1 deletion sources/ClangSharp/Types/IncompleteArrayType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ namespace ClangSharp
{
public sealed class IncompleteArrayType : ArrayType
{

internal IncompleteArrayType(CXType handle) : base(handle, CXTypeKind.CXType_IncompleteArray, CX_TypeClass.CX_TypeClass_IncompleteArray)
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ namespace ClangSharp.UnitTests
{
public abstract class StructDeclarationTest : PInvokeGeneratorTest
{
[TestCase("double", "double*")]
[TestCase("short", "short*")]
[TestCase("int", "int*")]
[TestCase("float", "float*")]
public Task ArrayUnknownSizeTest(string nativeType, string expectedManagedType) => ArrayUnknownSizeTestImpl(nativeType, expectedManagedType);
[TestCase("double", "double")]
[TestCase("short", "short")]
[TestCase("int", "int")]
[TestCase("float", "float")]
public Task IncompleteArraySizeTest(string nativeType, string expectedManagedType) => IncompleteArraySizeTestImpl(nativeType, expectedManagedType);

[TestCase("double", "double")]
[TestCase("short", "short")]
Expand Down Expand Up @@ -206,7 +206,7 @@ public abstract class StructDeclarationTest : PInvokeGeneratorTest
[Test]
public Task SourceLocationAttributeTest() => SourceLocationAttributeTestImpl();

protected abstract Task ArrayUnknownSizeTestImpl(string nativeType, string expectedManagedType);
protected abstract Task IncompleteArraySizeTestImpl(string nativeType, string expectedManagedType);

protected abstract Task BasicTestImpl(string nativeType, string expectedManagedType);

Expand Down
Loading