// Copyright 2020 The Tint Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef SRC_TINT_RESOLVER_VALIDATOR_H_ #define SRC_TINT_RESOLVER_VALIDATOR_H_ #include #include #include #include "src/tint/ast/pipeline_stage.h" #include "src/tint/program_builder.h" #include "src/tint/resolver/sem_helper.h" #include "src/tint/sem/evaluation_stage.h" #include "src/tint/source.h" #include "src/tint/utils/hash.h" #include "src/tint/utils/hashmap.h" #include "src/tint/utils/vector.h" // Forward declarations namespace tint::ast { class IndexAccessorExpression; class BinaryExpression; class BitcastExpression; class CallExpression; class CallStatement; class CaseStatement; class ForLoopStatement; class Function; class IdentifierExpression; class LoopStatement; class MemberAccessorExpression; class ReturnStatement; class SwitchStatement; class UnaryOpExpression; class Variable; class WhileStatement; } // namespace tint::ast namespace tint::sem { class Array; class Atomic; class BlockStatement; class BreakIfStatement; class Builtin; class Call; class CaseStatement; class ForLoopStatement; class IfStatement; class LoopStatement; class Materialize; class Statement; class SwitchStatement; class TypeInitializer; class WhileStatement; } // namespace tint::sem namespace tint::resolver { /// TypeAndAddressSpace is a pair of type and address space struct TypeAndAddressSpace { /// The type const type::Type* type; /// The address space ast::AddressSpace address_space; /// Equality operator /// @param other the other TypeAndAddressSpace to compare this TypeAndAddressSpace to /// @returns true if the type and address space of this TypeAndAddressSpace is equal to @p other bool operator==(const TypeAndAddressSpace& other) const { return type == other.type && address_space == other.address_space; } }; /// Validation logic for various ast nodes. The validations in general should /// be shallow and depend on the resolver to call on children. The validations /// also assume that sem changes have already been made. The validation checks /// should not alter the AST or SEM trees. class Validator { public: /// Constructor /// @param builder the program builder /// @param helper the SEM helper to validate with /// @param enabled_extensions all the extensions declared in current module /// @param atomic_composite_info atomic composite info of the module /// @param valid_type_storage_layouts a set of validated type layouts by address space Validator(ProgramBuilder* builder, SemHelper& helper, const ast::Extensions& enabled_extensions, const utils::Hashmap& atomic_composite_info, utils::Hashset& valid_type_storage_layouts); ~Validator(); /// Adds the given error message to the diagnostics /// @param msg the error message /// @param source the error source void AddError(const std::string& msg, const Source& source) const; /// Adds the given warning message to the diagnostics /// @param msg the warning message /// @param source the warning source void AddWarning(const std::string& msg, const Source& source) const; /// Adds the given note message to the diagnostics /// @param msg the note message /// @param source the note source void AddNote(const std::string& msg, const Source& source) const; /// @param type the given type /// @returns true if the given type is a plain type bool IsPlain(const type::Type* type) const; /// @param type the given type /// @returns true if the given type is a fixed-footprint type bool IsFixedFootprint(const type::Type* type) const; /// @param type the given type /// @returns true if the given type is storable bool IsStorable(const type::Type* type) const; /// @param type the given type /// @returns true if the given type is host-shareable bool IsHostShareable(const type::Type* type) const; /// Validates pipeline stages /// @param entry_points the entry points to the module /// @returns true on success, false otherwise. bool PipelineStages(utils::VectorRef entry_points) const; /// Validates push_constant variables /// @param entry_points the entry points to the module /// @returns true on success, false otherwise. bool PushConstants(utils::VectorRef entry_points) const; /// Validates aliases /// @param alias the alias to validate /// @returns true on success, false otherwise. bool Alias(const ast::Alias* alias) const; /// Validates the array /// @param arr the array to validate /// @param el_source the source of the array element, or the array if the array does not have a /// locally-declared element AST node. /// @returns true on success, false otherwise. bool Array(const sem::Array* arr, const Source& el_source) const; /// Validates an array stride attribute /// @param attr the stride attribute to validate /// @param el_size the element size /// @param el_align the element alignment /// @returns true on success, false otherwise bool ArrayStrideAttribute(const ast::StrideAttribute* attr, uint32_t el_size, uint32_t el_align) const; /// Validates an atomic type /// @param a the atomic ast node /// @param s the atomic sem node /// @returns true on success, false otherwise. bool Atomic(const ast::Atomic* a, const sem::Atomic* s) const; /// Validates a pointer type /// @param a the pointer ast node /// @param s the pointer sem node /// @returns true on success, false otherwise. bool Pointer(const ast::Pointer* a, const sem::Pointer* s) const; /// Validates an assignment /// @param a the assignment statement /// @param rhs_ty the type of the right hand side /// @returns true on success, false otherwise. bool Assignment(const ast::Statement* a, const type::Type* rhs_ty) const; /// Validates a bitcase /// @param cast the bitcast expression /// @param to the destination type /// @returns true on success, false otherwise bool Bitcast(const ast::BitcastExpression* cast, const type::Type* to) const; /// Validates a break statement /// @param stmt the break statement to validate /// @param current_statement the current statement being resolved /// @returns true on success, false otherwise. bool BreakStatement(const sem::Statement* stmt, sem::Statement* current_statement) const; /// Validates a builtin attribute /// @param attr the attribute to validate /// @param storage_type the attribute storage type /// @param stage the current pipeline stage /// @param is_input true if this is an input attribute /// @returns true on success, false otherwise. bool BuiltinAttribute(const ast::BuiltinAttribute* attr, const type::Type* storage_type, ast::PipelineStage stage, const bool is_input) const; /// Validates a continue statement /// @param stmt the continue statement to validate /// @param current_statement the current statement being resolved /// @returns true on success, false otherwise bool ContinueStatement(const sem::Statement* stmt, sem::Statement* current_statement) const; /// Validates a call /// @param call the call /// @param current_statement the current statement being resolved /// @returns true on success, false otherwise bool Call(const sem::Call* call, sem::Statement* current_statement) const; /// Validates an entry point /// @param func the entry point function to validate /// @param stage the pipeline stage for the entry point /// @returns true on success, false otherwise bool EntryPoint(const sem::Function* func, ast::PipelineStage stage) const; /// Validates that the expression must not be evaluated any later than @p latest_stage /// @param expr the expression to check /// @param latest_stage the latest evaluation stage that the expression can be evaluated /// @param constraint the 'thing' that is imposing the contraint. e.g. "var declaration" /// @returns true if @p expr is evaluated in or before @p latest_stage, false otherwise bool EvaluationStage(const sem::Expression* expr, sem::EvaluationStage latest_stage, std::string_view constraint) const; /// Validates a for loop /// @param stmt the for loop statement to validate /// @returns true on success, false otherwise bool ForLoopStatement(const sem::ForLoopStatement* stmt) const; /// Validates a while loop /// @param stmt the while statement to validate /// @returns true on success, false otherwise bool WhileStatement(const sem::WhileStatement* stmt) const; /// Validates a function /// @param func the function to validate /// @param stage the current pipeline stage /// @returns true on success, false otherwise. bool Function(const sem::Function* func, ast::PipelineStage stage) const; /// Validates a function call /// @param call the function call to validate /// @param current_statement the current statement being resolved /// @returns true on success, false otherwise bool FunctionCall(const sem::Call* call, sem::Statement* current_statement) const; /// Validates a global variable /// @param var the global variable to validate /// @param override_id the set of override ids in the module /// @returns true on success, false otherwise bool GlobalVariable( const sem::GlobalVariable* var, const utils::Hashmap& override_id) const; /// Validates a break-if statement /// @param stmt the statement to validate /// @param current_statement the current statement being resolved /// @returns true on success, false otherwise bool BreakIfStatement(const sem::BreakIfStatement* stmt, sem::Statement* current_statement) const; /// Validates an if statement /// @param stmt the statement to validate /// @returns true on success, false otherwise bool IfStatement(const sem::IfStatement* stmt) const; /// Validates an increment or decrement statement /// @param stmt the statement to validate /// @returns true on success, false otherwise bool IncrementDecrementStatement(const ast::IncrementDecrementStatement* stmt) const; /// Validates an interpolate attribute /// @param attr the interpolation attribute to validate /// @param storage_type the storage type of the attached variable /// @returns true on succes, false otherwise bool InterpolateAttribute(const ast::InterpolateAttribute* attr, const type::Type* storage_type) const; /// Validates a builtin call /// @param call the builtin call to validate /// @returns true on success, false otherwise. bool BuiltinCall(const sem::Call* call) const; /// Validates a local variable /// @param v the variable to validate /// @returns true on success, false otherwise. bool LocalVariable(const sem::Variable* v) const; /// Validates a location attribute /// @param loc_attr the location attribute to validate /// @param location the location value /// @param type the variable type /// @param locations the set of locations in the module /// @param stage the current pipeline stage /// @param source the source of the attribute /// @param is_input true if this is an input variable /// @returns true on success, false otherwise. bool LocationAttribute(const ast::LocationAttribute* loc_attr, uint32_t location, const type::Type* type, utils::Hashset& locations, ast::PipelineStage stage, const Source& source, const bool is_input = false) const; /// Validates a loop statement /// @param stmt the loop statement /// @returns true on success, false otherwise. bool LoopStatement(const sem::LoopStatement* stmt) const; /// Validates a materialize of an abstract numeric value from the type `from` to the type `to`. /// @param to the target type /// @param from the abstract numeric type /// @param source the source of the materialization /// @returns true on success, false otherwise bool Materialize(const type::Type* to, const type::Type* from, const Source& source) const; /// Validates a matrix /// @param ty the matrix to validate /// @param source the source of the matrix /// @returns true on success, false otherwise bool Matrix(const sem::Matrix* ty, const Source& source) const; /// Validates a function parameter /// @param func the function the variable is for /// @param var the variable to validate /// @returns true on success, false otherwise bool Parameter(const ast::Function* func, const sem::Variable* var) const; /// Validates a return /// @param ret the return statement to validate /// @param func_type the return type of the curreunt function /// @param ret_type the return type /// @param current_statement the current statement being resolved /// @returns true on success, false otherwise bool Return(const ast::ReturnStatement* ret, const type::Type* func_type, const type::Type* ret_type, sem::Statement* current_statement) const; /// Validates a list of statements /// @param stmts the statements to validate /// @returns true on success, false otherwise bool Statements(utils::VectorRef stmts) const; /// Validates a storage texture /// @param t the texture to validate /// @returns true on success, false otherwise bool StorageTexture(const ast::StorageTexture* t) const; /// Validates a sampled texture /// @param t the texture to validate /// @param source the source of the texture /// @returns true on success, false otherwise bool SampledTexture(const sem::SampledTexture* t, const Source& source) const; /// Validates a multisampled texture /// @param t the texture to validate /// @param source the source of the texture /// @returns true on success, false otherwise bool MultisampledTexture(const sem::MultisampledTexture* t, const Source& source) const; /// Validates a structure /// @param str the structure to validate /// @param stage the current pipeline stage /// @returns true on success, false otherwise. bool Structure(const sem::Struct* str, ast::PipelineStage stage) const; /// Validates a structure initializer /// @param ctor the call expression to validate /// @param struct_type the type of the structure /// @returns true on success, false otherwise bool StructureInitializer(const ast::CallExpression* ctor, const sem::Struct* struct_type) const; /// Validates a switch statement /// @param s the switch to validate /// @returns true on success, false otherwise bool SwitchStatement(const ast::SwitchStatement* s); /// Validates a 'var' variable declaration /// @param v the variable to validate /// @returns true on success, false otherwise. bool Var(const sem::Variable* v) const; /// Validates a 'let' variable declaration /// @param v the variable to validate /// @returns true on success, false otherwise. bool Let(const sem::Variable* v) const; /// Validates a 'override' variable declaration /// @param v the variable to validate /// @param override_id the set of override ids in the module /// @returns true on success, false otherwise. bool Override(const sem::GlobalVariable* v, const utils::Hashmap& override_id) const; /// Validates a 'const' variable declaration /// @param v the variable to validate /// @returns true on success, false otherwise. bool Const(const sem::Variable* v) const; /// Validates a variable initializer /// @param v the variable to validate /// @param address_space the address space of the variable /// @param storage_type the type of the storage /// @param initializer the RHS initializer expression /// @returns true on succes, false otherwise bool VariableInitializer(const ast::Variable* v, ast::AddressSpace address_space, const type::Type* storage_type, const sem::Expression* initializer) const; /// Validates a vector /// @param ty the vector to validate /// @param source the source of the vector /// @returns true on success, false otherwise bool Vector(const sem::Vector* ty, const Source& source) const; /// Validates an array initializer /// @param ctor the call expresion to validate /// @param arr_type the type of the array /// @returns true on success, false otherwise bool ArrayInitializer(const ast::CallExpression* ctor, const sem::Array* arr_type) const; /// Validates a texture builtin function /// @param call the builtin call to validate /// @returns true on success, false otherwise bool TextureBuiltinFunction(const sem::Call* call) const; /// Validates an optional builtin function and its required extension. /// @param call the builtin call to validate /// @returns true on success, false otherwise bool RequiredExtensionForBuiltinFunction(const sem::Call* call) const; /// Validates there are no duplicate attributes /// @param attributes the list of attributes to validate /// @returns true on success, false otherwise. bool NoDuplicateAttributes(utils::VectorRef attributes) const; /// Validates a address space layout /// @param type the type to validate /// @param sc the address space /// @param source the source of the type /// @returns true on success, false otherwise bool AddressSpaceLayout(const type::Type* type, ast::AddressSpace sc, Source source) const; /// @returns true if the attribute list contains a /// ast::DisableValidationAttribute with the validation mode equal to /// `validation` /// @param attributes the attribute list to check /// @param validation the validation mode to check bool IsValidationDisabled(utils::VectorRef attributes, ast::DisabledValidation validation) const; /// @returns true if the attribute list does not contains a /// ast::DisableValidationAttribute with the validation mode equal to /// `validation` /// @param attributes the attribute list to check /// @param validation the validation mode to check bool IsValidationEnabled(utils::VectorRef attributes, ast::DisabledValidation validation) const; private: /// @param ty the type to check /// @returns true if @p ty is an array with an `override` expression element count, otherwise /// false. bool IsArrayWithOverrideCount(const type::Type* ty) const; /// Raises an error about an array type using an `override` expression element count, outside /// the single allowed use of a `var`. /// @param source the source for the error void RaiseArrayWithOverrideCountError(const Source& source) const; /// Searches the current statement and up through parents of the current /// statement looking for a loop or for-loop continuing statement. /// @returns the closest continuing statement to the current statement that /// (transitively) owns the current statement. /// @param stop_at_loop if true then the function will return nullptr if a /// loop or for-loop was found before the continuing. /// @param current_statement the current statement being resolved const ast::Statement* ClosestContinuing(bool stop_at_loop, sem::Statement* current_statement) const; /// Returns a human-readable string representation of the vector type name /// with the given parameters. /// @param size the vector dimension /// @param element_type scalar vector sub-element type /// @return pretty string representation std::string VectorPretty(uint32_t size, const type::Type* element_type) const; /// Raises an error if combination of @p store_ty, @p access and @p address_space are not valid /// for a `var` or `ptr` declaration. /// @param store_ty the store type of the var or pointer /// @param access the var or pointer access /// @param address_space the var or pointer address space /// @param source the source for the error /// @returns true on success, false if an error was raised. bool CheckTypeAccessAddressSpace(const type::Type* store_ty, ast::Access access, ast::AddressSpace address_space, utils::VectorRef attributes, const Source& source) const; SymbolTable& symbols_; diag::List& diagnostics_; SemHelper& sem_; const ast::Extensions& enabled_extensions_; const utils::Hashmap& atomic_composite_info_; utils::Hashset& valid_type_storage_layouts_; }; } // namespace tint::resolver namespace std { /// Custom std::hash specialization for tint::resolver::TypeAndAddressSpace. template <> class hash { public: /// @param tas the TypeAndAddressSpace /// @return the hash value inline std::size_t operator()(const tint::resolver::TypeAndAddressSpace& tas) const { return tint::utils::Hash(tas.type, tas.address_space); } }; } // namespace std #endif // SRC_TINT_RESOLVER_VALIDATOR_H_