Skip to content

Commit 9f33d3d

Browse files
author
Tim Foley
authored
Add an attribute to disable the overlapping-bindings warning (shader-slang#1005)
Currently if the user gives two global shader parameters conflicting bindings, they get a warning diagnostic: ```hlsl Texture2D a : register(t0); Texture2D b : register(t0); // WARNING: overlapping bindings ``` This change adds a way to locally disable that warning using an attribute: ```hlsl [allow("overlapping-bindings")] Texture2D a : register(t0); [allow("overlapping-bindings")] Texture2D b : register(t0); // OK ``` Note that as a policy decision, the implementation requires `[allow("overlapping-bindings")]` on both declarations in order to disable the warning, under the assumption that the behavior should be strictly opt-in, and not silently affect a programmer who adds a new shader parameter with no knowledge or expectation of possible overlap. The `[allow(...)]` attribute is intended to be a fairly generally mechanism for disabling optional diagnostics within certain scopes (e.g., for the body of a function definition), but as implemented in this change it is quite restrictive: * Only the single name `"overlapping-bindings"` will be recognized, and this name cannot be used with, e.g., a `-W` flag on the command line to enable/disable the same diagnostic, or turn it into an error. Adding more cases would be easy enough, but wiring it up to command-line flags could be trickier. * Only the code that checks for parameter binding overlap is currently checking for `[allow(...)]` attributes, so it is not "wired up" to enable/disable any others. Doing this systematically would ideally involve something in `diagnose()`, but there could be complications to a systematic approach (finding the AST node(s) to use when searching for `[allow(...)]`. On gotcha here is that versions of Slang without this feature will error out on the `[allow(...)]` attribute since they don't understand it, and if we add future diagnostics that it covers then old compiler versions will (as written) error out on a diagnostic they haven't heard of rather than just assume the `[allow(...)]` attribute doesn't apply to them. These kinds of issues can and should be addressed in future changes.
1 parent ade2c39 commit 9f33d3d

10 files changed

+121
-5
lines changed

source/slang/core.meta.slang

+4
Original file line numberDiff line numberDiff line change
@@ -1314,3 +1314,7 @@ attribute_syntax [__AttributeUsage(target : _AttributeTargets)] : AttributeUsage
13141314

13151315
__attributeTarget(VarDeclBase)
13161316
attribute_syntax [format(format : String)] : FormatAttribute;
1317+
1318+
__attributeTarget(Decl)
1319+
attribute_syntax [allow(diagnostic: String)] : AllowAttribute;
1320+

source/slang/core.meta.slang.h

+4
Original file line numberDiff line numberDiff line change
@@ -1345,3 +1345,7 @@ SLANG_RAW("attribute_syntax [__AttributeUsage(target : _AttributeTargets)] : Att
13451345
SLANG_RAW("\n")
13461346
SLANG_RAW("__attributeTarget(VarDeclBase)\n")
13471347
SLANG_RAW("attribute_syntax [format(format : String)] : FormatAttribute;\n")
1348+
SLANG_RAW("\n")
1349+
SLANG_RAW("__attributeTarget(Decl)\n")
1350+
SLANG_RAW("attribute_syntax [allow(diagnostic: String)] : AllowAttribute;\n")
1351+
SLANG_RAW("\n")

source/slang/slang-check.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -2934,6 +2934,24 @@ namespace Slang
29342934

29352935
formatAttr->format = format;
29362936
}
2937+
else if (auto allowAttr = as<AllowAttribute>(attr))
2938+
{
2939+
SLANG_ASSERT(attr->args.getCount() == 1);
2940+
2941+
String diagnosticName;
2942+
if(!checkLiteralStringVal(attr->args[0], &diagnosticName))
2943+
{
2944+
return false;
2945+
}
2946+
2947+
auto diagnosticInfo = findDiagnosticByName(diagnosticName.getUnownedSlice());
2948+
if(!diagnosticInfo)
2949+
{
2950+
getSink()->diagnose(attr->args[0], Diagnostics::unknownDiagnosticName, diagnosticName);
2951+
}
2952+
2953+
allowAttr->diagnostic = diagnosticInfo;
2954+
}
29372955
else
29382956
{
29392957
if(attr->args.getCount() == 0)

source/slang/slang-diagnostic-defs.h

+1
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ DIAGNOSTIC(31006, Error, attributeFunctionNotFound, "Could not find function '$0
269269

270270
DIAGNOSTIC(31100, Error, unknownStageName, "unknown stage name '$0'")
271271
DIAGNOSTIC(31101, Error, unknownImageFormatName, "unknown image format '$0'")
272+
DIAGNOSTIC(31101, Error, unknownDiagnosticName, "unknown diagnostic '$0'")
272273

273274
DIAGNOSTIC(31120, Error, invalidAttributeTarget, "invalid syntax target for user defined attribute")
274275

source/slang/slang-diagnostics.cpp

+26-1
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,37 @@ void DiagnosticSink::diagnoseRaw(
339339
}
340340
}
341341

342-
343342
namespace Diagnostics
344343
{
345344
#define DIAGNOSTIC(id, severity, name, messageFormat) const DiagnosticInfo name = { id, Severity::severity, messageFormat };
346345
#include "slang-diagnostic-defs.h"
347346
}
348347

348+
DiagnosticInfo const* findDiagnosticByName(UnownedStringSlice const& name)
349+
{
350+
// TODO: We should eventually have a more formal system for associating individual
351+
// diagnostics, or groups of diagnostics, with user-exposed names for use when
352+
// enabling/disabling warnings (or turning warnings into errors, etc.).
353+
//
354+
// For now we build an ad hoc mapping from string names to corresponding single
355+
// diagnostics (not groups).
356+
//
357+
static const struct
358+
{
359+
char const* name;
360+
DiagnosticInfo const* diagnostic;
361+
} kDiagnostics[] =
362+
{
363+
{ "overlapping-bindings", &Diagnostics::parameterBindingsOverlap },
364+
};
365+
366+
for( auto& entry : kDiagnostics )
367+
{
368+
if( name == entry.name )
369+
return entry.diagnostic;
370+
}
371+
return nullptr;
372+
}
373+
349374

350375
} // namespace Slang

source/slang/slang-diagnostics.h

+2
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ namespace Slang
257257
DiagnosticSink* m_sink = nullptr;
258258
};
259259

260+
DiagnosticInfo const* findDiagnosticByName(UnownedStringSlice const& name);
261+
260262
namespace Diagnostics
261263
{
262264
#define DIAGNOSTIC(id, severity, name, messageFormat) extern const DiagnosticInfo name;

source/slang/slang-modifier-defs.h

+4
Original file line numberDiff line numberDiff line change
@@ -461,3 +461,7 @@ END_SYNTAX_CLASS()
461461
SYNTAX_CLASS(FormatAttribute, Attribute)
462462
FIELD(ImageFormat, format)
463463
END_SYNTAX_CLASS()
464+
465+
SYNTAX_CLASS(AllowAttribute, Attribute)
466+
FIELD_INIT(DiagnosticInfo const*, diagnostic, nullptr)
467+
END_SYNTAX_CLASS()

source/slang/slang-parameter-binding.cpp

+38-4
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,25 @@ static UInt allocateUnusedSpaces(
784784
return context->shared->usedSpaces.Allocate(nullptr, count);
785785
}
786786

787+
static bool shouldDisableDiagnostic(
788+
Decl* decl,
789+
DiagnosticInfo const& diagnosticInfo)
790+
{
791+
for( auto dd = decl; dd; dd = dd->ParentDecl )
792+
{
793+
for( auto modifier : dd->modifiers )
794+
{
795+
auto allowAttr = as<AllowAttribute>(modifier);
796+
if(!allowAttr)
797+
continue;
798+
799+
if(allowAttr->diagnostic == &diagnosticInfo)
800+
return true;
801+
}
802+
}
803+
return false;
804+
}
805+
787806
static void addExplicitParameterBinding(
788807
ParameterBindingContext* context,
789808
RefPtr<ParameterInfo> parameterInfo,
@@ -842,11 +861,26 @@ static void addExplicitParameterBinding(
842861
auto paramA = parameterInfo->varLayouts[0]->varDecl.getDecl();
843862
auto paramB = overlappedVarLayout->varDecl.getDecl();
844863

845-
getSink(context)->diagnose(paramA, Diagnostics::parameterBindingsOverlap,
846-
getReflectionName(paramA),
847-
getReflectionName(paramB));
864+
auto& diagnosticInfo = Diagnostics::parameterBindingsOverlap;
865+
866+
// If *both* of the shader parameters declarations agree
867+
// that overlapping bindings should be allowed, then we
868+
// will not emit a diagnostic. Otherwise, we will warn
869+
// the user because such overlapping bindings are likely
870+
// to indicate a programming error.
871+
//
872+
if(shouldDisableDiagnostic(paramA, diagnosticInfo)
873+
&& shouldDisableDiagnostic(paramB, diagnosticInfo))
874+
{
875+
}
876+
else
877+
{
878+
getSink(context)->diagnose(paramA, diagnosticInfo,
879+
getReflectionName(paramA),
880+
getReflectionName(paramB));
848881

849-
getSink(context)->diagnose(paramB, Diagnostics::seeDeclarationOf, getReflectionName(paramB));
882+
getSink(context)->diagnose(paramB, Diagnostics::seeDeclarationOf, getReflectionName(paramB));
883+
}
850884
}
851885
}
852886
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// overlapping-bindings.slang
2+
3+
//DIAGNOSTIC_TEST:SIMPLE:-target hlsl
4+
5+
// Two parameters with the same `register:
6+
7+
Texture2D a : register(t0);
8+
9+
Texture2D b : register(t0);
10+
11+
// Parameters marked to ignore overlap:
12+
13+
[allow("overlapping-bindings")]
14+
Texture2D c : register(t1);
15+
16+
[allow("overlapping-bindings")]
17+
Texture2D d : register(t1);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
result code = 0
2+
standard error = {
3+
tests/diagnostics/overlapping-bindings.slang(9): warning 39001: explicit binding for parameter 'b' overlaps with parameter 'a'
4+
tests/diagnostics/overlapping-bindings.slang(7): note: see declaration of 'a'
5+
}
6+
standard output = {
7+
}

0 commit comments

Comments
 (0)