diff --git a/include/NZSL/Ast/SanitizeVisitor.hpp b/include/NZSL/Ast/SanitizeVisitor.hpp index 835bb9b..6459963 100644 --- a/include/NZSL/Ast/SanitizeVisitor.hpp +++ b/include/NZSL/Ast/SanitizeVisitor.hpp @@ -20,6 +20,15 @@ namespace nzsl::Ast { + using OptionHash = std::uint32_t; + + template constexpr OptionHash HashOption(Args&&... args); + + namespace Literals + { + constexpr OptionHash operator ""_opt(const char* str, std::size_t length); + } + class NZSL_API SanitizeVisitor final : Cloner { friend class AstTypeExpressionVisitor; @@ -42,7 +51,7 @@ namespace nzsl::Ast { std::function identifierSanitizer; //< ignored when performing partial sanitization std::shared_ptr moduleResolver; - std::unordered_map optionValues; + std::unordered_map optionValues; bool forceAutoBindingResolve = false; bool makeVariableNameUnique = false; bool partialSanitization = false; diff --git a/include/NZSL/Ast/SanitizeVisitor.inl b/include/NZSL/Ast/SanitizeVisitor.inl index 45c6797..0996815 100644 --- a/include/NZSL/Ast/SanitizeVisitor.inl +++ b/include/NZSL/Ast/SanitizeVisitor.inl @@ -2,9 +2,24 @@ // This file is part of the "Nazara Shading Language" project // For conditions of distribution and use, see copyright notice in Config.hpp +#include namespace nzsl::Ast { + template + constexpr OptionHash HashOption(Args&&... args) + { + return Nz::FNV1a32(std::forward(args)...); + } + + namespace Literals + { + constexpr OptionHash operator""_opt(const char* str, std::size_t length) + { + return HashOption(std::string_view(str, length)); + } + } + inline ModulePtr SanitizeVisitor::Sanitize(const Module& module, std::string* error) { return Sanitize(module, {}, error); diff --git a/include/NZSL/Lang/ErrorList.hpp b/include/NZSL/Lang/ErrorList.hpp index 1755d16..22d1e03 100644 --- a/include/NZSL/Lang/ErrorList.hpp +++ b/include/NZSL/Lang/ErrorList.hpp @@ -124,6 +124,7 @@ NZSL_SHADERLANG_COMPILER_ERROR(ModuleCompilationFailed, "module {} compilation f NZSL_SHADERLANG_COMPILER_ERROR(ModuleFeatureMismatch, "module {} requires feature {}", std::string, Ast::ModuleFeature) NZSL_SHADERLANG_COMPILER_ERROR(ModuleNotFound, "module {} not found", std::string) NZSL_SHADERLANG_COMPILER_ERROR(NoModuleResolver, "import statement found but no module resolver has been set (and partial sanitization is not enabled)") +NZSL_SHADERLANG_COMPILER_ERROR(OptionHashCollision, "option {} has the same hash as option {}", std::string, std::string) NZSL_SHADERLANG_COMPILER_ERROR(OptionDeclarationInsideFunction, "options must be declared outside of functions") NZSL_SHADERLANG_COMPILER_ERROR(PartialTypeExpect, "expected a {} type at #{}", std::string, std::uint32_t) NZSL_SHADERLANG_COMPILER_ERROR(PartialTypeTooFewParameters, "parameter count mismatch (expected at least {}, got {})", std::uint32_t, std::uint32_t) diff --git a/src/NZSL/Ast/SanitizeVisitor.cpp b/src/NZSL/Ast/SanitizeVisitor.cpp index 7f20971..ce45086 100644 --- a/src/NZSL/Ast/SanitizeVisitor.cpp +++ b/src/NZSL/Ast/SanitizeVisitor.cpp @@ -194,6 +194,7 @@ namespace nzsl::Ast std::unordered_map moduleByName; std::unordered_map usedBindingIndexes; std::unordered_map declaredExternalVar; + std::unordered_map declaredOptions; std::shared_ptr globalEnv; std::shared_ptr currentEnv; std::shared_ptr moduleEnv; @@ -1750,7 +1751,16 @@ namespace nzsl::Ast clone->optType = std::move(resolvedType); - std::uint32_t optionHash = Nz::CRC32(reinterpret_cast(clone->optName.data()), clone->optName.size()); + OptionHash optionHash = HashOption(clone->optName.data()); + + // Detect hash collisions + if (auto it = m_context->declaredOptions.find(optionHash); it != m_context->declaredOptions.end()) + { + if (it->second != clone->optName) + throw CompilerOptionHashCollisionError{ node.sourceLocation, clone->optName, it->second }; + } + else + m_context->declaredOptions.emplace(optionHash, clone->optName); if (auto optionValueIt = m_context->options.optionValues.find(optionHash); optionValueIt != m_context->options.optionValues.end()) clone->optIndex = RegisterConstant(clone->optName, optionValueIt->second, node.optIndex, node.sourceLocation); diff --git a/tests/src/Tests/ConstTests.cpp b/tests/src/Tests/ConstTests.cpp index 031095c..8d23cf8 100644 --- a/tests/src/Tests/ConstTests.cpp +++ b/tests/src/Tests/ConstTests.cpp @@ -99,7 +99,9 @@ fn main() WHEN("Enabling option") { - options.optionValues[Nz::CRC32("UseInt")] = true; + using namespace nzsl::Ast::Literals; + + options.optionValues["UseInt"_opt] = true; ExpectOutput(*shaderModule, options, R"( struct inputStruct @@ -123,7 +125,9 @@ fn main() WHEN("Disabling option") { - options.optionValues[Nz::CRC32("UseInt")] = false; + using namespace nzsl::Ast::Literals; + + options.optionValues["UseInt"_opt] = false; ExpectOutput(*shaderModule, options, R"( struct inputStruct