diff --git a/include/Nazara/Shader/Ast/ExpressionType.hpp b/include/Nazara/Shader/Ast/ExpressionType.hpp index 7085c8506..7944e2415 100644 --- a/include/Nazara/Shader/Ast/ExpressionType.hpp +++ b/include/Nazara/Shader/Ast/ExpressionType.hpp @@ -166,6 +166,7 @@ namespace Nz::ShaderAst ExpressionValue layout; std::string name; std::vector members; + bool isConditional; }; inline bool IsAliasType(const ExpressionType& type); diff --git a/include/Nazara/Shader/Ast/SanitizeVisitor.hpp b/include/Nazara/Shader/Ast/SanitizeVisitor.hpp index 6d20b323f..2812642e8 100644 --- a/include/Nazara/Shader/Ast/SanitizeVisitor.hpp +++ b/include/Nazara/Shader/Ast/SanitizeVisitor.hpp @@ -218,6 +218,7 @@ namespace Nz::ShaderAst { std::size_t index; IdentifierCategory category; + bool isConditional = false; }; struct Identifier diff --git a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp index 3e3ba1a0d..5c508290b 100644 --- a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp +++ b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp @@ -158,6 +158,11 @@ namespace Nz::ShaderAst const DeclareFunctionStatement* node; }; + struct UsedExternalData + { + bool isConditional; + }; + static constexpr std::size_t ModuleIdSentinel = std::numeric_limits::max(); std::array entryFunctions = {}; @@ -165,8 +170,8 @@ namespace Nz::ShaderAst std::vector pendingFunctions; std::vector* currentStatementList = nullptr; std::unordered_map moduleByUuid; - std::unordered_set declaredExternalVar; - std::unordered_set usedBindingIndexes; + std::unordered_map usedBindingIndexes; + std::unordered_map declaredExternalVar; std::shared_ptr globalEnv; std::shared_ptr currentEnv; std::shared_ptr moduleEnv; @@ -182,6 +187,7 @@ namespace Nz::ShaderAst Options options; CurrentFunctionData* currentFunction = nullptr; bool allowUnknownIdentifiers = false; + bool inConditionalStatement = false; }; ModulePtr SanitizeVisitor::Sanitize(const Module& module, const Options& options, std::string* error) @@ -350,7 +356,12 @@ namespace Nz::ShaderAst } if (!fieldPtr) + { + if (s->isConditional) + return AstCloner::Clone(node); //< unresolved + throw ShaderLang::CompilerUnknownFieldError{ indexedExpr->sourceLocation, identifierEntry.identifier }; + } if (m_context->options.useIdentifierAccessesForStructs) { @@ -953,6 +964,11 @@ namespace Nz::ShaderAst ExpressionPtr cloneCondition = AstCloner::Clone(*node.condition); std::optional conditionValue = ComputeConstantValue(*cloneCondition); + + bool wasInConditionalStatement = m_context->inConditionalStatement; + m_context->inConditionalStatement = true; + CallOnExit restoreCond([=] { m_context->inConditionalStatement = wasInConditionalStatement; }); + if (!conditionValue.has_value()) { // Unresolvable condition @@ -1045,22 +1061,31 @@ namespace Nz::ShaderAst ComputeExprValue(extVar.bindingIndex, node.sourceLocation); + Context::UsedExternalData usedBindingData; + usedBindingData.isConditional = m_context->inConditionalStatement; + if (extVar.bindingSet.IsResultingValue() && extVar.bindingIndex.IsResultingValue()) { UInt64 bindingSet = extVar.bindingSet.GetResultingValue(); UInt64 bindingIndex = extVar.bindingIndex.GetResultingValue(); UInt64 bindingKey = bindingSet << 32 | bindingIndex; - if (m_context->usedBindingIndexes.find(bindingKey) != m_context->usedBindingIndexes.end()) - throw ShaderLang::CompilerExtBindingAlreadyUsedError{ extVar.sourceLocation, UInt32(bindingSet), UInt32(bindingIndex) }; + if (auto it = m_context->usedBindingIndexes.find(bindingKey); it != m_context->usedBindingIndexes.end()) + { + if (!it->second.isConditional || !usedBindingData.isConditional) + throw ShaderLang::CompilerExtBindingAlreadyUsedError{ extVar.sourceLocation, UInt32(bindingSet), UInt32(bindingIndex) }; + } - m_context->usedBindingIndexes.insert(bindingKey); + m_context->usedBindingIndexes.emplace(bindingKey, usedBindingData); } - if (m_context->declaredExternalVar.find(extVar.name) != m_context->declaredExternalVar.end()) - throw ShaderLang::CompilerExtAlreadyDeclaredError{ extVar.sourceLocation, extVar.name }; + if (auto it = m_context->declaredExternalVar.find(extVar.name); it != m_context->declaredExternalVar.end()) + { + if (!it->second.isConditional || !usedBindingData.isConditional) + throw ShaderLang::CompilerExtAlreadyDeclaredError{ extVar.sourceLocation, extVar.name }; + } - m_context->declaredExternalVar.insert(extVar.name); + m_context->declaredExternalVar.emplace(extVar.name, usedBindingData); std::optional resolvedType = ResolveTypeExpr(extVar.type, false, node.sourceLocation); if (!resolvedType.has_value()) @@ -1303,6 +1328,8 @@ namespace Nz::ShaderAst } } + clone->description.isConditional = m_context->inConditionalStatement; + clone->structIndex = RegisterStruct(clone->description.name, &clone->description, clone->structIndex, clone->sourceLocation); SanitizeIdentifier(clone->description.name); @@ -2420,7 +2447,8 @@ namespace Nz::ShaderAst m_context->currentEnv->identifiersInScope.push_back({ std::move(name), aliasIndex, - IdentifierCategory::Alias + IdentifierCategory::Alias, + m_context->inConditionalStatement }); return aliasIndex; @@ -2445,7 +2473,8 @@ namespace Nz::ShaderAst m_context->currentEnv->identifiersInScope.push_back({ std::move(name), constantIndex, - IdentifierCategory::Constant + IdentifierCategory::Constant, + m_context->inConditionalStatement }); return constantIndex; @@ -2494,7 +2523,8 @@ namespace Nz::ShaderAst m_context->currentEnv->identifiersInScope.push_back({ std::move(name), functionIndex, - IdentifierCategory::Function + IdentifierCategory::Function, + m_context->inConditionalStatement }); return functionIndex; @@ -2510,7 +2540,8 @@ namespace Nz::ShaderAst m_context->currentEnv->identifiersInScope.push_back({ std::move(name), intrinsicIndex, - IdentifierCategory::Intrinsic + IdentifierCategory::Intrinsic, + m_context->inConditionalStatement }); return intrinsicIndex; @@ -2526,7 +2557,8 @@ namespace Nz::ShaderAst m_context->currentEnv->identifiersInScope.push_back({ std::move(moduleIdentifier), moduleIndex, - IdentifierCategory::Module + IdentifierCategory::Module, + m_context->inConditionalStatement }); return moduleIndex; @@ -2534,8 +2566,14 @@ namespace Nz::ShaderAst std::size_t SanitizeVisitor::RegisterStruct(std::string name, std::optional description, std::optional index, const ShaderLang::SourceLocation& sourceLocation) { - if (FindIdentifier(name)) - throw ShaderLang::CompilerIdentifierAlreadyUsedError{ sourceLocation, name }; + bool unresolved = false; + if (const IdentifierData* identifierData = FindIdentifier(name)) + { + if (!m_context->inConditionalStatement || !identifierData->isConditional) + throw ShaderLang::CompilerIdentifierAlreadyUsedError{ sourceLocation, name }; + else + unresolved = true; + } std::size_t structIndex; if (description) @@ -2548,11 +2586,19 @@ namespace Nz::ShaderAst else structIndex = m_context->structs.RegisterNewIndex(true); - m_context->currentEnv->identifiersInScope.push_back({ - std::move(name), - structIndex, - IdentifierCategory::Struct - }); + if (!unresolved) + { + m_context->currentEnv->identifiersInScope.push_back({ + std::move(name), + { + structIndex, + IdentifierCategory::Struct, + m_context->inConditionalStatement + } + }); + } + else + RegisterUnresolved(std::move(name)); return structIndex; } @@ -2576,7 +2622,8 @@ namespace Nz::ShaderAst m_context->currentEnv->identifiersInScope.push_back({ std::move(name), typeIndex, - IdentifierCategory::Type + IdentifierCategory::Type, + m_context->inConditionalStatement }); return typeIndex; @@ -2607,7 +2654,8 @@ namespace Nz::ShaderAst m_context->currentEnv->identifiersInScope.push_back({ std::move(name), typeIndex, - IdentifierCategory::Type + IdentifierCategory::Type, + m_context->inConditionalStatement }); return typeIndex; @@ -2618,17 +2666,21 @@ namespace Nz::ShaderAst m_context->currentEnv->identifiersInScope.push_back({ std::move(name), std::numeric_limits::max(), - IdentifierCategory::Unresolved + IdentifierCategory::Unresolved, + m_context->inConditionalStatement }); } std::size_t SanitizeVisitor::RegisterVariable(std::string name, std::optional type, std::optional index, const ShaderLang::SourceLocation& sourceLocation) { + bool unresolved = false; if (auto* identifier = FindIdentifier(name)) { // Allow variable shadowing if (identifier->category != IdentifierCategory::Variable) throw ShaderLang::CompilerIdentifierAlreadyUsedError{ sourceLocation, name }; + else if (identifier->isConditional && m_context->inConditionalStatement) + unresolved = true; //< right variable isn't know from this point } std::size_t varIndex; @@ -2642,11 +2694,19 @@ namespace Nz::ShaderAst else varIndex = m_context->variableTypes.RegisterNewIndex(true); - m_context->currentEnv->identifiersInScope.push_back({ - std::move(name), - varIndex, - IdentifierCategory::Variable - }); + if (!unresolved) + { + m_context->currentEnv->identifiersInScope.push_back({ + std::move(name), + { + varIndex, + IdentifierCategory::Variable, + m_context->inConditionalStatement + } + }); + } + else + RegisterUnresolved(std::move(name)); return varIndex; } @@ -2668,8 +2728,13 @@ namespace Nz::ShaderAst for (auto& parameter : pendingFunc.cloneNode->parameters) { - parameter.varIndex = RegisterVariable(parameter.name, parameter.type.GetResultingValue(), parameter.varIndex, parameter.sourceLocation); - SanitizeIdentifier(parameter.name); + if (!m_context->options.allowPartialSanitization || parameter.type.IsResultingValue()) + { + parameter.varIndex = RegisterVariable(parameter.name, parameter.type.GetResultingValue(), parameter.varIndex, parameter.sourceLocation); + SanitizeIdentifier(parameter.name); + } + else + RegisterUnresolved(parameter.name); } CurrentFunctionData tempFuncData;