Skip to content
Draft
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
111 changes: 111 additions & 0 deletions source/slang/slang-check-conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,104 @@ bool SemanticsVisitor::_readAggregateValueFromInitializerList(
return true;
}

// Recursively walk a type to find resource types that would be default-initialized
// (i.e., not explicitly provided in the initializer list) and emit warnings.
void SemanticsVisitor::_warnAboutDefaultInitializedResources(
Type* type,
UInt argCount,
UInt& ioArgIndex,
SourceLoc loc)
{
// If we still have arguments available, this type will be explicitly initialized
if (ioArgIndex < argCount)
{
// For resource types, one argument is consumed
if (as<ResourceType>(type))
{
ioArgIndex++;
return;
}

// For arrays, recurse into elements
if (auto arrayType = as<ArrayExpressionType>(type))
{
auto elementType = arrayType->getElementType();
if (!arrayType->isUnsized())
{
if (auto constCount = as<ConstantIntVal>(arrayType->getElementCount()))
{
UInt elementCount = (UInt)constCount->getValue();
for (UInt i = 0; i < elementCount; i++)
{
_warnAboutDefaultInitializedResources(
elementType,
argCount,
ioArgIndex,
loc);
}
}
}
return;
}

// For structs, recurse into fields
if (auto structDeclRef = isDeclRefTypeOf<StructDecl>(type))
{
for (auto field : structDeclRef.getDecl()->getMembersOfType<VarDeclBase>())
{
if (field->hasModifier<HLSLStaticModifier>())
continue;
_warnAboutDefaultInitializedResources(field->getType(), argCount, ioArgIndex, loc);
}
return;
}

// For other types (scalars, vectors, matrices), one argument is consumed
ioArgIndex++;
return;
}

// No more arguments - this type will be default-initialized
// Check if it contains resource types
if (as<ResourceType>(type))
{
getSink()->diagnose(loc, Diagnostics::cannotDefaultInitializeResourceType, type);
return;
}

// For arrays, recurse to check element type
if (auto arrayType = as<ArrayExpressionType>(type))
{
auto elementType = arrayType->getElementType();
if (!arrayType->isUnsized())
{
if (auto constCount = as<ConstantIntVal>(arrayType->getElementCount()))
{
UInt elementCount = (UInt)constCount->getValue();
for (UInt i = 0; i < elementCount; i++)
{
_warnAboutDefaultInitializedResources(elementType, argCount, ioArgIndex, loc);
}
}
}
return;
}

// For structs, recurse into fields
if (auto structDeclRef = isDeclRefTypeOf<StructDecl>(type))
{
for (auto field : structDeclRef.getDecl()->getMembersOfType<VarDeclBase>())
{
if (field->hasModifier<HLSLStaticModifier>())
continue;
_warnAboutDefaultInitializedResources(field->getType(), argCount, ioArgIndex, loc);
}
return;
}

// Other types (scalars, vectors, matrices) are default-initializable - no warning needed
}

bool SemanticsVisitor::_coerceInitializerList(
Type* toType,
Expr** outToExpr,
Expand Down Expand Up @@ -959,6 +1057,19 @@ bool SemanticsVisitor::_coerceInitializerList(
return true;
}

// Before falling back to legacy initializer list logic, check for resource types
// that would be default-initialized and warn about them.
// Skip this check when synthesizing default initializers (e.g., for ParameterBlocks).
if (!isInSynthesizedDefaultInit())
{
UInt warningArgIndex = 0;
_warnAboutDefaultInitializedResources(
toType,
argCount,
warningArgIndex,
fromInitializerListExpr->loc);
}

// Try to invoke the synthesized constructor if it exists
if (createInvokeExprForSynthesizedCtor(toType, fromInitializerListExpr, outToExpr))
{
Expand Down
39 changes: 33 additions & 6 deletions source/slang/slang-check-decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2646,7 +2646,10 @@ bool isDefaultInitializable(VarDeclBase* varDecl)
return true;
}

static Expr* constructDefaultConstructorForType(SemanticsVisitor* visitor, Type* type)
static Expr* constructDefaultConstructorForType(
SemanticsVisitor* visitor,
Type* type,
SourceLoc loc)
{
ConstructorDecl* defaultCtor = nullptr;
auto declRefType = as<DeclRefType>(type);
Expand Down Expand Up @@ -2677,14 +2680,18 @@ static Expr* constructDefaultConstructorForType(SemanticsVisitor* visitor, Type*
if (visitor->isCStyleType(type, visitSet))
{
auto initListExpr = visitor->getASTBuilder()->create<InitializerListExpr>();
initListExpr->loc = loc;
initListExpr->type = visitor->getASTBuilder()->getInitializerListType();
Expr* outExpr = nullptr;
auto fromType = type;
if (auto atomicType = as<AtomicType>(fromType))
{
fromType = atomicType->getElementType();
}
if (visitor->_coerceInitializerList(fromType, &outExpr, initListExpr))
// Use a sub-visitor with the synthesized default init flag set to suppress
// warnings about default-initializing resource types.
SemanticsVisitor subVisitor(visitor->withInSynthesizedDefaultInit());
if (subVisitor._coerceInitializerList(fromType, &outExpr, initListExpr))
return outExpr;
}

Expand All @@ -2699,7 +2706,8 @@ static Expr* constructDefaultInitExprForType(SemanticsVisitor* visitor, VarDeclB
if (!isDefaultInitializable(varDecl))
return nullptr;

if (auto defaultInitExpr = constructDefaultConstructorForType(visitor, varDecl->type.type))
if (auto defaultInitExpr =
constructDefaultConstructorForType(visitor, varDecl->type.type, varDecl->loc))
{
return defaultInitExpr;
}
Expand Down Expand Up @@ -9681,7 +9689,27 @@ void SemanticsDeclBodyVisitor::visitParamDecl(ParamDecl* paramDecl)
// actual type of the parameter.
//
initExpr = CheckTerm(initExpr);
initExpr = coerce(CoercionSite::Initializer, typeExpr.type, initExpr, getSink());

// For synthesized constructor parameters, the default value is derived
// from the field initializer, which has already been checked and warned
// about. Suppress duplicate warnings by using the synthesized default
// init context.
bool isSynthesizedCtorParam = false;
if (auto ctorDecl = as<ConstructorDecl>(paramDecl->parentDecl))
{
isSynthesizedCtorParam = ctorDecl->findModifier<SynthesizedModifier>() != nullptr;
}

if (isSynthesizedCtorParam)
{
SemanticsVisitor subVisitor(withInSynthesizedDefaultInit());
initExpr =
subVisitor.coerce(CoercionSite::Initializer, typeExpr.type, initExpr, getSink());
}
else
{
initExpr = coerce(CoercionSite::Initializer, typeExpr.type, initExpr, getSink());
}
paramDecl->initExpr = initExpr;

// TODO: a default argument expression needs to
Expand Down Expand Up @@ -13596,9 +13624,8 @@ static Expr* _getParamDefaultValue(SemanticsVisitor* visitor, VarDeclBase* varDe
if (!isDefaultInitializable(varDecl))
return nullptr;

if (auto expr = constructDefaultConstructorForType(visitor, varDecl->type.type))
if (auto expr = constructDefaultConstructorForType(visitor, varDecl->type.type, varDecl->loc))
{
expr->loc = varDecl->loc;
return expr;
}

Expand Down
21 changes: 21 additions & 0 deletions source/slang/slang-check-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,16 @@ struct SemanticsContext

bool getInForLoopSideEffect() { return m_inForLoopSideEffect; }

// Setup the flag to indicate we're synthesizing a default initializer,
// where warnings about default-initializing resource types should be suppressed.
SemanticsContext withInSynthesizedDefaultInit()
{
SemanticsContext result(*this);
result.m_inSynthesizedDefaultInit = true;
return result;
}

bool isInSynthesizedDefaultInit() { return m_inSynthesizedDefaultInit; }

TryClauseType getEnclosingTryClauseType() { return m_enclosingTryClauseType; }

Expand Down Expand Up @@ -1211,6 +1221,9 @@ struct SemanticsContext
// allowed
bool m_inForLoopSideEffect = false;

// Flag to track when we're synthesizing a default initializer, where warnings about
// default-initializing resource types should be suppressed.
bool m_inSynthesizedDefaultInit = false;

ExpandExpr* m_parentExpandExpr = nullptr;

Expand Down Expand Up @@ -1734,6 +1747,14 @@ struct SemanticsVisitor : public SemanticsContext
Expr** outToExpr,
InitializerListExpr* fromInitializerListExpr);

/// Recursively walk a type to find resource types that would be default-initialized
/// and emit warnings.
void _warnAboutDefaultInitializedResources(
Type* type,
UInt argCount,
UInt& ioArgIndex,
SourceLoc loc);

/// Report that implicit type coercion is not possible.
bool _failedCoercion(Type* toType, Expr** outToExpr, Expr* fromExpr, DiagnosticSink* sink);

Expand Down
6 changes: 6 additions & 0 deletions source/slang/slang-diagnostic-defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1770,6 +1770,11 @@ DIAGNOSTIC(
Error,
cannotUseInitializerListForCoopVectorOfUnknownSize,
"cannot use initializer list for CoopVector of statically unknown size '$0'")
DIAGNOSTIC(
30506,
Warning,
cannotDefaultInitializeResourceType,
"cannot default-initialize resource type '$0', leaving uninitialized")

// 3062x: variables
DIAGNOSTIC(
Expand Down Expand Up @@ -2575,6 +2580,7 @@ DIAGNOSTIC(
"capabilities are: '$2'")
DIAGNOSTIC(41015, Warning, usingUninitializedOut, "use of uninitialized out parameter '$0'")
DIAGNOSTIC(41016, Warning, usingUninitializedVariable, "use of uninitialized variable '$0'")
DIAGNOSTIC(41016, Warning, usingUninitializedValue, "use of uninitialized value of type '$0'")
DIAGNOSTIC(
41017,
Warning,
Expand Down
14 changes: 13 additions & 1 deletion source/slang/slang-ir-use-uninitialized-values.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,19 @@ static void checkUninitializedValues(IRFunc* func, DiagnosticSink* sink)
auto loads = getUnresolvedVariableLoads(reachability, inst);
for (auto load : loads)
{
sink->diagnose(load, Diagnostics::usingUninitializedVariable, inst);
// Check if we have a meaningful name for the variable
bool hasName = inst->findDecoration<IRNameHintDecoration>() != nullptr ||
inst->findDecoration<IRLinkageDecoration>() != nullptr;

if (hasName)
{
sink->diagnose(load, Diagnostics::usingUninitializedVariable, inst);
}
else
{
// For poison ops and other unnamed instructions, show type instead
sink->diagnose(load, Diagnostics::usingUninitializedValue, type);
}
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions source/slang/slang-lower-to-ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5473,6 +5473,13 @@ struct ExprLoweringVisitorBase : public ExprVisitor<Derived, LoweredValInfo>
return LoweredValInfo::simple(
getBuilder()->emitMakeTuple(irType, args.getCount(), args.getBuffer()));
}
else if (auto resourceType = as<ResourceType>(type))
{
// In practice, a resource type must always be bound to a particular resource,
// so we return an undefined value here. We expect later instructions to assign
// a valid resource to this value before using it.
return LoweredValInfo::simple(getBuilder()->emitPoison(irType));
}
else if (auto declRefType = as<DeclRefType>(type))
{
DeclRef<Decl> declRef = declRefType->getDeclRef();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
result code = -1
standard error = {
tests/diagnostics/uninitialized-resource-type.slang(10): warning 30506: cannot default-initialize resource type 'Texture2D<vector<float,4>>', leaving uninitialized
Texture2D<float4> bar = {};
^
tests/diagnostics/uninitialized-resource-type.slang(40): warning 41016: use of uninitialized variable 'foo'
const let result = process(foo);
^
Expand Down
41 changes: 41 additions & 0 deletions tests/spirv/zero-init-resource-type-field.slang
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -output-using-type
//TEST:SIMPLE(filecheck=CHECK):-target spirv

//TEST_INPUT: Texture2D(size=4, content = zero):name tex1
Texture2D<float4> tex1;
SamplerState samp;

//TEST_INPUT: ubuffer(data=[0 0 0 0], stride=1, count=4):out,name outputBuffer
RWStructuredBuffer<float> outputBuffer;

struct S {
float2 uv;
Texture2D<float4> t;
};

[numthreads(2, 2, 1)]
void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
{
uint index = dispatchThreadID.x + dispatchThreadID.y * 2;
float param = float(dispatchThreadID.x) / 2.0;
float2 coord = float2(dispatchThreadID.xy) / 2.0;

bool b = (param >= 0.5);

//CHECK: ([[# @LINE+1]]): warning 41016{{.*}}Texture2D
S s = (S)0;
s.uv = coord;

if (b) {
s.t = tex1;
}

float4 col = float4(1, 1, 1, 1);

if (b) {
col = s.t.SampleLevel(samp, s.uv, 0);
}

outputBuffer[index] = col.x;
}