Skip to content

Commit

Permalink
Allow tuples to work with initializer list. (#6301)
Browse files Browse the repository at this point in the history
* Allow tuples to work with initiailizer list.

* Update definition of C-Style types.
  • Loading branch information
csyonghe authored Feb 6, 2025
1 parent 78f26f0 commit 6b63ff0
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 7 deletions.
13 changes: 9 additions & 4 deletions docs/proposals/004-initialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,18 @@ void test()

If the above code passes type check, then it will be used as the way to initialize `obj`.

If the above code does not pass type check, and if there is only one constructor for`MyType` that is synthesized as described in the previous section (and therefore marked as `[Synthesized]`, Slang continues to check if `S` meets the standard of a "legacy C-style struct` type.
A type is a "legacy C-Style struct" if all of the following conditions are met:
- It is a user-defined struct type or an enum, a basic scalar, vector or matrix type, e.g. `int`, `float4x4`.
If the above code does not pass type check, and if there is only one constructor for`MyType` that is synthesized as described in the previous section (and therefore marked as `[Synthesized]`, Slang continues to check if `S` meets the standard of a "legacy C-style struct` type. A type is a "legacy C-Style" type if it is a:
- Basic scalar type (e.g. `int`, `float`).
- Enum type.
- Sized array type where the element type is C-style type.
- Tuple type where all member types are C-style types.
- A "C-Style" struct.

A struct is C-Style if all of the following conditions are met:
- It does not inherit from any other types.
- It does not contain any explicit constructors defined by the user.
- All its members have the same visibility as the type itself.
- All its members are legacy C-Style structs or arrays of legacy C-style structs.
- All its members are legacy C-Style types.
Note that C-Style structs are allowed to have member default values.
In such case, we perform a legacy "read data" style consumption of the initializer list to synthesize the arguments to call the constructor, so that the following behavior is valid:

Expand Down
38 changes: 38 additions & 0 deletions source/slang/slang-check-conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,21 @@ bool SemanticsVisitor::isCStyleType(Type* type, HashSet<Type*>& isVisit)
return cacheResult(true);


// A tuple type is C-style if all of its members are C-style.
if (auto tupleType = as<TupleType>(type))
{
for (Index i = 0; i < tupleType->getMemberCount(); i++)
{
auto elementType = tupleType->getMember(i);
// Avoid infinite loop in case of circular reference.
if (isVisit.contains(elementType))
return cacheResult(false);
if (!isCStyleType(elementType, isVisit))
return cacheResult(false);
}
return cacheResult(true);
}

if (auto structDecl = isDeclRefTypeOf<StructDecl>(type).getDecl())
{
// 2. It cannot have inheritance, but inherit from interface is fine.
Expand Down Expand Up @@ -299,6 +314,7 @@ bool SemanticsVisitor::isCStyleType(Type* type, HashSet<Type*>& isVisit)
if (!isCStyleType(elementType, isVisit))
return cacheResult(false);
}

return cacheResult(true);
}

Expand Down Expand Up @@ -700,6 +716,28 @@ bool SemanticsVisitor::_readAggregateValueFromInitializerList(
}
}
}
else if (auto tupleType = as<TupleType>(toType))
{
for (Index ee = 0; ee < tupleType->getMemberCount(); ++ee)
{
auto elementType = tupleType->getMember(ee);
Expr* coercedArg = nullptr;
bool argResult = _readValueFromInitializerList(
elementType,
outToExpr ? &coercedArg : nullptr,
fromInitializerListExpr,
ioArgIndex);

// No point in trying further if any argument fails
if (!argResult)
return false;

if (coercedArg)
{
coercedArgs.add(coercedArg);
}
}
}
else if (auto toDeclRefType = as<DeclRefType>(toType))
{
auto toTypeDeclRef = toDeclRefType->getDeclRef();
Expand Down
23 changes: 20 additions & 3 deletions source/slang/slang-lower-to-ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4701,6 +4701,16 @@ struct ExprLoweringVisitorBase : public ExprVisitor<Derived, LoweredValInfo>
{
return LoweredValInfo::simple(getBuilder()->getNullPtrValue(irType));
}
else if (auto tupleType = as<TupleType>(type))
{
List<IRInst*> args;
for (Index i = 0; i < tupleType->getMemberCount(); i++)
{
args.add(getSimpleVal(context, getDefaultVal(tupleType->getMember(i))));
}
return LoweredValInfo::simple(
getBuilder()->emitMakeTuple(irType, args.getCount(), args.getBuffer()));
}
else if (auto declRefType = as<DeclRefType>(type))
{
DeclRef<Decl> declRef = declRefType->getDeclRef();
Expand Down Expand Up @@ -4925,9 +4935,16 @@ struct ExprLoweringVisitorBase : public ExprVisitor<Derived, LoweredValInfo>
args.add(irDefaultValue);
}
}

return LoweredValInfo::simple(
getBuilder()->emitMakeStruct(irType, args.getCount(), args.getBuffer()));
if (as<TupleType>(type))
{
return LoweredValInfo::simple(
getBuilder()->emitMakeTuple(irType, args.getCount(), args.getBuffer()));
}
else
{
return LoweredValInfo::simple(
getBuilder()->emitMakeStruct(irType, args.getCount(), args.getBuffer()));
}
}
}

Expand Down
28 changes: 28 additions & 0 deletions tests/initializer-list/tuple.slang
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-vk -output-using-type
//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-output-using-type

Tuple<bool, float> createTuple() {
return {};
}

// We should also enable the following use of initialization list:

Tuple<bool, float> createTuple2() {
return {false, 1.0};
}


//TEST_INPUT: set output = out ubuffer(data=[0 0 0 0], stride=4)
RWStructuredBuffer<float> output;

[numthreads(1, 1, 1)]
void computeMain()
{
let hit = createTuple();
output[0] = hit._1 + 1.0;

let hit2 = createTuple2();
output[1] = hit2._1 + 1.0;
// CHECK: 1.0
// CHECK: 2.0
}

0 comments on commit 6b63ff0

Please sign in to comment.