ValidateAST.cpp (38163B)
1 // 2 // Copyright 2019 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 7 #include "compiler/translator/ValidateAST.h" 8 9 #include "common/utilities.h" 10 #include "compiler/translator/Diagnostics.h" 11 #include "compiler/translator/ImmutableStringBuilder.h" 12 #include "compiler/translator/Symbol.h" 13 #include "compiler/translator/tree_util/IntermTraverse.h" 14 #include "compiler/translator/tree_util/SpecializationConstant.h" 15 #include "compiler/translator/util.h" 16 17 namespace sh 18 { 19 20 namespace 21 { 22 23 class ValidateAST : public TIntermTraverser 24 { 25 public: 26 static bool validate(TIntermNode *root, 27 TDiagnostics *diagnostics, 28 const ValidateASTOptions &options); 29 30 void visitSymbol(TIntermSymbol *node) override; 31 void visitConstantUnion(TIntermConstantUnion *node) override; 32 bool visitSwizzle(Visit visit, TIntermSwizzle *node) override; 33 bool visitBinary(Visit visit, TIntermBinary *node) override; 34 bool visitUnary(Visit visit, TIntermUnary *node) override; 35 bool visitTernary(Visit visit, TIntermTernary *node) override; 36 bool visitIfElse(Visit visit, TIntermIfElse *node) override; 37 bool visitSwitch(Visit visit, TIntermSwitch *node) override; 38 bool visitCase(Visit visit, TIntermCase *node) override; 39 void visitFunctionPrototype(TIntermFunctionPrototype *node) override; 40 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override; 41 bool visitAggregate(Visit visit, TIntermAggregate *node) override; 42 bool visitBlock(Visit visit, TIntermBlock *node) override; 43 bool visitGlobalQualifierDeclaration(Visit visit, 44 TIntermGlobalQualifierDeclaration *node) override; 45 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; 46 bool visitLoop(Visit visit, TIntermLoop *node) override; 47 bool visitBranch(Visit visit, TIntermBranch *node) override; 48 void visitPreprocessorDirective(TIntermPreprocessorDirective *node) override; 49 50 private: 51 ValidateAST(TIntermNode *root, TDiagnostics *diagnostics, const ValidateASTOptions &options); 52 53 // Visit as a generic node 54 void visitNode(Visit visit, TIntermNode *node); 55 // Visit a structure or interface block, and recursively visit its fields of structure type. 56 void visitStructOrInterfaceBlockDeclaration(const TType &type, const TSourceLoc &location); 57 void visitStructUsage(const TType &type, const TSourceLoc &location); 58 // Visit a unary or aggregate node and validate its built-in op against its built-in function. 59 void visitBuiltInFunction(TIntermOperator *op, const TFunction *function); 60 // Visit an aggregate node and validate its function call is to one that's already defined. 61 void visitFunctionCall(TIntermAggregate *node); 62 // Visit a binary node and validate its type against its operands. 63 void validateExpressionTypeBinary(TIntermBinary *node); 64 // Visit a switch node and validate its selector type is integer. 65 void validateExpressionTypeSwitch(TIntermSwitch *node); 66 // Visit a symbol node and validate it's declared previously. 67 void visitVariableNeedingDeclaration(TIntermSymbol *node); 68 // Visit a built-in symbol node and validate it's consistently used across the tree. 69 void visitBuiltInVariable(TIntermSymbol *node); 70 71 void scope(Visit visit); 72 bool isVariableDeclared(const TVariable *variable); 73 bool variableNeedsDeclaration(const TVariable *variable); 74 const TFieldListCollection *getStructOrInterfaceBlock(const TType &type, 75 ImmutableString *typeNameOut); 76 77 void expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count); 78 79 bool validateInternal(); 80 81 ValidateASTOptions mOptions; 82 TDiagnostics *mDiagnostics; 83 84 // For validateSingleParent: 85 std::map<TIntermNode *, TIntermNode *> mParent; 86 bool mSingleParentFailed = false; 87 88 // For validateVariableReferences: 89 std::vector<std::set<const TVariable *>> mDeclaredVariables; 90 std::set<const TInterfaceBlock *> mNamelessInterfaceBlocks; 91 std::map<ImmutableString, const TVariable *> mReferencedBuiltIns; 92 bool mVariableReferencesFailed = false; 93 94 // For validateBuiltInOps: 95 bool mBuiltInOpsFailed = false; 96 97 // For validateFunctionCall: 98 std::set<const TFunction *> mDeclaredFunctions; 99 bool mFunctionCallFailed = false; 100 101 // For validateNoRawFunctionCalls: 102 bool mNoRawFunctionCallsFailed = false; 103 104 // For validateNullNodes: 105 bool mNullNodesFailed = false; 106 107 // For validateQualifiers: 108 bool mQualifiersFailed = false; 109 110 // For validatePrecision: 111 bool mPrecisionFailed = false; 112 113 // For validateStructUsage: 114 std::vector<std::map<ImmutableString, const TFieldListCollection *>> mStructsAndBlocksByName; 115 bool mStructUsageFailed = false; 116 117 // For validateExpressionTypes: 118 bool mExpressionTypesFailed = false; 119 120 // For validateMultiDeclarations: 121 bool mMultiDeclarationsFailed = false; 122 123 // For validateNoSwizzleOfSwizzle: 124 bool mNoSwizzleOfSwizzleFailed = false; 125 126 // For validateNoStatementsAfterBranch: 127 bool mIsBranchVisitedInBlock = false; 128 bool mNoStatementsAfterBranchFailed = false; 129 }; 130 131 bool IsSameType(const TType &a, const TType &b) 132 { 133 return a.getBasicType() == b.getBasicType() && a.getNominalSize() == b.getNominalSize() && 134 a.getSecondarySize() == b.getSecondarySize() && a.getArraySizes() == b.getArraySizes() && 135 a.getStruct() == b.getStruct() && 136 (!a.isInterfaceBlock() || a.getInterfaceBlock() == b.getInterfaceBlock()); 137 } 138 139 bool ValidateAST::validate(TIntermNode *root, 140 TDiagnostics *diagnostics, 141 const ValidateASTOptions &options) 142 { 143 ValidateAST validate(root, diagnostics, options); 144 root->traverse(&validate); 145 return validate.validateInternal(); 146 } 147 148 ValidateAST::ValidateAST(TIntermNode *root, 149 TDiagnostics *diagnostics, 150 const ValidateASTOptions &options) 151 : TIntermTraverser(true, false, true, nullptr), mOptions(options), mDiagnostics(diagnostics) 152 { 153 bool isTreeRoot = root->getAsBlock() && root->getAsBlock()->isTreeRoot(); 154 155 // Some validations are not applicable unless run on the entire tree. 156 if (!isTreeRoot) 157 { 158 mOptions.validateVariableReferences = false; 159 mOptions.validateFunctionCall = false; 160 mOptions.validateStructUsage = false; 161 } 162 163 if (mOptions.validateSingleParent) 164 { 165 mParent[root] = nullptr; 166 } 167 } 168 169 void ValidateAST::visitNode(Visit visit, TIntermNode *node) 170 { 171 if (visit == PreVisit && mOptions.validateSingleParent) 172 { 173 size_t childCount = node->getChildCount(); 174 for (size_t i = 0; i < childCount; ++i) 175 { 176 TIntermNode *child = node->getChildNode(i); 177 if (mParent.find(child) != mParent.end()) 178 { 179 // If child is visited twice but through the same parent, the problem is in one of 180 // the ancestors. 181 if (mParent[child] != node) 182 { 183 mDiagnostics->error(node->getLine(), "Found child with two parents", 184 "<validateSingleParent>"); 185 mSingleParentFailed = true; 186 } 187 } 188 189 mParent[child] = node; 190 } 191 } 192 193 if (visit == PreVisit && mOptions.validateNoStatementsAfterBranch) 194 { 195 // If a branch has already been visited in this block, there should be no statements that 196 // follow. Only expected node visit should be PostVisit of the block. 197 if (mIsBranchVisitedInBlock) 198 { 199 mDiagnostics->error(node->getLine(), "Found dead code after branch", 200 "<validateNoStatementsAfterBranch>"); 201 mNoStatementsAfterBranchFailed = true; 202 } 203 } 204 } 205 206 void ValidateAST::visitStructOrInterfaceBlockDeclaration(const TType &type, 207 const TSourceLoc &location) 208 { 209 if (type.getStruct() == nullptr && type.getInterfaceBlock() == nullptr) 210 { 211 return; 212 } 213 214 // Make sure the structure or interface block is not doubly defined. 215 ImmutableString typeName(""); 216 const TFieldListCollection *namedStructOrBlock = getStructOrInterfaceBlock(type, &typeName); 217 218 // Recurse the fields of the structure or interface block and check members of structure type. 219 // This is done before visiting the struct itself, because if the fields refer to a struct with 220 // the same name, they would be referencing the struct declared in an outer scope. 221 { 222 // Note that structOrBlock was previously only set for named structures, so make sure 223 // nameless structs are also recursed. 224 const TFieldListCollection *structOrBlock = namedStructOrBlock; 225 if (structOrBlock == nullptr) 226 { 227 structOrBlock = type.getStruct(); 228 } 229 ASSERT(structOrBlock != nullptr); 230 231 for (const TField *field : structOrBlock->fields()) 232 { 233 visitStructUsage(*field->type(), field->line()); 234 } 235 } 236 237 if (namedStructOrBlock) 238 { 239 ASSERT(!typeName.empty()); 240 // Structures are not allowed to be doubly defined 241 if (type.getStruct() == nullptr) 242 { 243 // Allow interfaces to be doubly-defined. 244 std::string name(typeName.data()); 245 246 if (IsShaderIn(type.getQualifier())) 247 { 248 typeName = ImmutableString(name + "<input>"); 249 } 250 else if (IsShaderOut(type.getQualifier())) 251 { 252 typeName = ImmutableString(name + "<output>"); 253 } 254 else if (IsStorageBuffer(type.getQualifier())) 255 { 256 typeName = ImmutableString(name + "<buffer>"); 257 } 258 else if (type.getQualifier() == EvqUniform) 259 { 260 typeName = ImmutableString(name + "<uniform>"); 261 } 262 } 263 264 if (mStructsAndBlocksByName.back().find(typeName) != mStructsAndBlocksByName.back().end()) 265 { 266 mDiagnostics->error(location, 267 "Found redeclaration of struct or interface block with the same " 268 "name in the same scope <validateStructUsage>", 269 typeName.data()); 270 mStructUsageFailed = true; 271 } 272 else 273 { 274 // First encounter. 275 mStructsAndBlocksByName.back()[typeName] = namedStructOrBlock; 276 } 277 } 278 } 279 280 void ValidateAST::visitStructUsage(const TType &type, const TSourceLoc &location) 281 { 282 if (type.getStruct() == nullptr) 283 { 284 return; 285 } 286 287 // Make sure the structure being referenced has the same pointer as the closest (in scope) 288 // definition. 289 const TStructure *structure = type.getStruct(); 290 const ImmutableString &typeName = structure->name(); 291 292 bool foundDeclaration = false; 293 for (size_t scopeIndex = mStructsAndBlocksByName.size(); scopeIndex > 0; --scopeIndex) 294 { 295 const std::map<ImmutableString, const TFieldListCollection *> &scopeDecls = 296 mStructsAndBlocksByName[scopeIndex - 1]; 297 298 auto iter = scopeDecls.find(typeName); 299 if (iter != scopeDecls.end()) 300 { 301 foundDeclaration = true; 302 303 if (iter->second != structure) 304 { 305 mDiagnostics->error(location, 306 "Found reference to struct or interface block with doubly " 307 "created type <validateStructUsage>", 308 typeName.data()); 309 mStructUsageFailed = true; 310 } 311 312 break; 313 } 314 } 315 316 if (!foundDeclaration) 317 { 318 mDiagnostics->error(location, 319 "Found reference to struct or interface block with no declaration " 320 "<validateStructUsage>", 321 typeName.data()); 322 mStructUsageFailed = true; 323 } 324 } 325 326 void ValidateAST::visitBuiltInFunction(TIntermOperator *node, const TFunction *function) 327 { 328 const TOperator op = node->getOp(); 329 if (!BuiltInGroup::IsBuiltIn(op)) 330 { 331 return; 332 } 333 334 ImmutableStringBuilder opValueBuilder(16); 335 opValueBuilder << "op: "; 336 opValueBuilder.appendDecimal(op); 337 338 ImmutableString opValue = opValueBuilder; 339 340 if (function == nullptr) 341 { 342 mDiagnostics->error(node->getLine(), 343 "Found node calling built-in without a reference to the built-in " 344 "function <validateBuiltInOps>", 345 opValue.data()); 346 mVariableReferencesFailed = true; 347 } 348 else if (function->getBuiltInOp() != op) 349 { 350 mDiagnostics->error(node->getLine(), 351 "Found node calling built-in with a reference to a different function " 352 "<validateBuiltInOps>", 353 opValue.data()); 354 mVariableReferencesFailed = true; 355 } 356 } 357 358 void ValidateAST::visitFunctionCall(TIntermAggregate *node) 359 { 360 if (node->getOp() != EOpCallFunctionInAST) 361 { 362 return; 363 } 364 365 const TFunction *function = node->getFunction(); 366 367 if (function == nullptr) 368 { 369 mDiagnostics->error(node->getLine(), 370 "Found node calling function without a reference to it", 371 "<validateFunctionCall>"); 372 mFunctionCallFailed = true; 373 } 374 else if (mDeclaredFunctions.find(function) == mDeclaredFunctions.end()) 375 { 376 mDiagnostics->error(node->getLine(), 377 "Found node calling previously undeclared function " 378 "<validateFunctionCall>", 379 function->name().data()); 380 mFunctionCallFailed = true; 381 } 382 } 383 384 void ValidateAST::validateExpressionTypeBinary(TIntermBinary *node) 385 { 386 switch (node->getOp()) 387 { 388 case EOpIndexDirect: 389 case EOpIndexIndirect: 390 { 391 TType expectedType(node->getLeft()->getType()); 392 if (!expectedType.isArray()) 393 { 394 // TODO: Validate matrix column selection and vector component selection. 395 // http://anglebug.com/2733 396 break; 397 } 398 399 expectedType.toArrayElementType(); 400 401 if (!IsSameType(node->getType(), expectedType)) 402 { 403 const TSymbol *symbol = expectedType.getStruct(); 404 if (symbol == nullptr) 405 { 406 symbol = expectedType.getInterfaceBlock(); 407 } 408 const char *name = nullptr; 409 if (symbol) 410 { 411 name = symbol->name().data(); 412 } 413 else if (expectedType.isScalar()) 414 { 415 name = "<scalar array>"; 416 } 417 else if (expectedType.isVector()) 418 { 419 name = "<vector array>"; 420 } 421 else 422 { 423 ASSERT(expectedType.isMatrix()); 424 name = "<matrix array>"; 425 } 426 427 mDiagnostics->error( 428 node->getLine(), 429 "Found index node with type that is inconsistent with the array being indexed " 430 "<validateExpressionTypes>", 431 name); 432 mExpressionTypesFailed = true; 433 } 434 } 435 break; 436 default: 437 // TODO: Validate other expressions. http://anglebug.com/2733 438 break; 439 } 440 441 switch (node->getOp()) 442 { 443 case EOpIndexDirect: 444 case EOpIndexDirectStruct: 445 case EOpIndexDirectInterfaceBlock: 446 if (node->getRight()->getAsConstantUnion() == nullptr) 447 { 448 mDiagnostics->error(node->getLine(), 449 "Found direct index node with a non-constant index", 450 "<validateExpressionTypes>"); 451 mExpressionTypesFailed = true; 452 } 453 break; 454 default: 455 break; 456 } 457 } 458 459 void ValidateAST::validateExpressionTypeSwitch(TIntermSwitch *node) 460 { 461 const TType &selectorType = node->getInit()->getType(); 462 463 if (selectorType.getBasicType() != EbtYuvCscStandardEXT && 464 selectorType.getBasicType() != EbtInt && selectorType.getBasicType() != EbtUInt) 465 { 466 mDiagnostics->error(node->getLine(), "Found switch selector expression that is not integer", 467 "<validateExpressionTypes>"); 468 mExpressionTypesFailed = true; 469 } 470 else if (!selectorType.isScalar()) 471 { 472 mDiagnostics->error(node->getLine(), "Found switch selector expression that is not scalar", 473 "<validateExpressionTypes>"); 474 mExpressionTypesFailed = true; 475 } 476 } 477 478 void ValidateAST::visitVariableNeedingDeclaration(TIntermSymbol *node) 479 { 480 const TVariable *variable = &node->variable(); 481 const TType &type = node->getType(); 482 483 // If it's a reference to a field of a nameless interface block, match it by index and name. 484 if (type.getInterfaceBlock() && !type.isInterfaceBlock()) 485 { 486 const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); 487 const TFieldList &fieldList = interfaceBlock->fields(); 488 const size_t fieldIndex = type.getInterfaceBlockFieldIndex(); 489 490 if (mNamelessInterfaceBlocks.count(interfaceBlock) == 0) 491 { 492 mDiagnostics->error(node->getLine(), 493 "Found reference to undeclared or inconsistenly transformed " 494 "nameless interface block <validateVariableReferences>", 495 node->getName().data()); 496 mVariableReferencesFailed = true; 497 } 498 else if (fieldIndex >= fieldList.size() || node->getName() != fieldList[fieldIndex]->name()) 499 { 500 mDiagnostics->error(node->getLine(), 501 "Found reference to inconsistenly transformed nameless " 502 "interface block field <validateVariableReferences>", 503 node->getName().data()); 504 mVariableReferencesFailed = true; 505 } 506 return; 507 } 508 509 const bool isStructDeclaration = 510 type.isStructSpecifier() && variable->symbolType() == SymbolType::Empty; 511 512 if (!isStructDeclaration && !isVariableDeclared(variable)) 513 { 514 mDiagnostics->error(node->getLine(), 515 "Found reference to undeclared or inconsistently transformed " 516 "variable <validateVariableReferences>", 517 node->getName().data()); 518 mVariableReferencesFailed = true; 519 } 520 } 521 522 void ValidateAST::visitBuiltInVariable(TIntermSymbol *node) 523 { 524 const TVariable *variable = &node->variable(); 525 ImmutableString name = variable->name(); 526 527 if (mOptions.validateVariableReferences) 528 { 529 auto iter = mReferencedBuiltIns.find(name); 530 if (iter == mReferencedBuiltIns.end()) 531 { 532 mReferencedBuiltIns[name] = variable; 533 return; 534 } 535 536 if (variable != iter->second) 537 { 538 mDiagnostics->error( 539 node->getLine(), 540 "Found inconsistent references to built-in variable <validateVariableReferences>", 541 name.data()); 542 mVariableReferencesFailed = true; 543 } 544 } 545 546 if (mOptions.validateQualifiers) 547 { 548 TQualifier qualifier = variable->getType().getQualifier(); 549 550 if ((name == "gl_ClipDistance" && qualifier != EvqClipDistance) || 551 (name == "gl_CullDistance" && qualifier != EvqCullDistance) || 552 (name == "gl_LastFragData" && qualifier != EvqLastFragData)) 553 { 554 mDiagnostics->error( 555 node->getLine(), 556 "Incorrect qualifier applied to redeclared built-in <validateQualifiers>", 557 name.data()); 558 mQualifiersFailed = true; 559 } 560 } 561 } 562 563 void ValidateAST::scope(Visit visit) 564 { 565 if (mOptions.validateVariableReferences) 566 { 567 if (visit == PreVisit) 568 { 569 mDeclaredVariables.push_back({}); 570 } 571 else if (visit == PostVisit) 572 { 573 mDeclaredVariables.pop_back(); 574 } 575 } 576 577 if (mOptions.validateStructUsage) 578 { 579 if (visit == PreVisit) 580 { 581 mStructsAndBlocksByName.push_back({}); 582 } 583 else if (visit == PostVisit) 584 { 585 mStructsAndBlocksByName.pop_back(); 586 } 587 } 588 } 589 590 bool ValidateAST::isVariableDeclared(const TVariable *variable) 591 { 592 ASSERT(mOptions.validateVariableReferences); 593 594 for (const std::set<const TVariable *> &scopeVariables : mDeclaredVariables) 595 { 596 if (scopeVariables.count(variable) > 0) 597 { 598 return true; 599 } 600 } 601 602 return false; 603 } 604 605 bool ValidateAST::variableNeedsDeclaration(const TVariable *variable) 606 { 607 // Don't expect declaration for built-in variables. 608 if (gl::IsBuiltInName(variable->name().data())) 609 { 610 return false; 611 } 612 613 // Additionally, don't expect declaration for Vulkan specialization constants if not enabled. 614 // The declaration of these variables is deferred. 615 if (variable->getType().getQualifier() == EvqSpecConst) 616 { 617 return mOptions.validateSpecConstReferences; 618 } 619 620 return true; 621 } 622 623 const TFieldListCollection *ValidateAST::getStructOrInterfaceBlock(const TType &type, 624 ImmutableString *typeNameOut) 625 { 626 const TStructure *structure = type.getStruct(); 627 const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); 628 629 ASSERT(structure != nullptr || interfaceBlock != nullptr); 630 631 // Make sure the structure or interface block is not doubly defined. 632 const TFieldListCollection *structOrBlock = nullptr; 633 if (structure != nullptr && structure->symbolType() != SymbolType::Empty) 634 { 635 structOrBlock = structure; 636 *typeNameOut = structure->name(); 637 } 638 else if (interfaceBlock != nullptr) 639 { 640 structOrBlock = interfaceBlock; 641 *typeNameOut = interfaceBlock->name(); 642 } 643 644 return structOrBlock; 645 } 646 647 void ValidateAST::expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count) 648 { 649 if (visit == PreVisit && mOptions.validateNullNodes) 650 { 651 size_t childCount = node->getChildCount(); 652 if (childCount < least_count) 653 { 654 mDiagnostics->error(node->getLine(), "Too few children", "<validateNullNodes>"); 655 mNullNodesFailed = true; 656 } 657 658 for (size_t i = 0; i < childCount; ++i) 659 { 660 if (node->getChildNode(i) == nullptr) 661 { 662 mDiagnostics->error(node->getLine(), "Found nullptr child", "<validateNullNodes>"); 663 mNullNodesFailed = true; 664 } 665 } 666 } 667 } 668 669 void ValidateAST::visitSymbol(TIntermSymbol *node) 670 { 671 visitNode(PreVisit, node); 672 673 const TVariable *variable = &node->variable(); 674 675 if (mOptions.validateVariableReferences) 676 { 677 if (variableNeedsDeclaration(variable)) 678 { 679 visitVariableNeedingDeclaration(node); 680 } 681 } 682 683 const bool isBuiltIn = gl::IsBuiltInName(variable->name().data()); 684 if (isBuiltIn) 685 { 686 visitBuiltInVariable(node); 687 } 688 689 if (mOptions.validatePrecision) 690 { 691 if (!isBuiltIn && IsPrecisionApplicableToType(node->getBasicType()) && 692 node->getType().getPrecision() == EbpUndefined) 693 { 694 // Note that some built-ins don't have a precision. 695 mDiagnostics->error(node->getLine(), 696 "Found symbol with undefined precision <validatePrecision>", 697 variable->name().data()); 698 mPrecisionFailed = true; 699 } 700 } 701 } 702 703 void ValidateAST::visitConstantUnion(TIntermConstantUnion *node) 704 { 705 visitNode(PreVisit, node); 706 } 707 708 bool ValidateAST::visitSwizzle(Visit visit, TIntermSwizzle *node) 709 { 710 visitNode(visit, node); 711 712 if (mOptions.validateNoSwizzleOfSwizzle) 713 { 714 if (node->getOperand()->getAsSwizzleNode() != nullptr) 715 { 716 mDiagnostics->error(node->getLine(), "Found swizzle applied to swizzle", 717 "<validateNoSwizzleOfSwizzle>"); 718 mNoSwizzleOfSwizzleFailed = true; 719 } 720 } 721 722 return true; 723 } 724 725 bool ValidateAST::visitBinary(Visit visit, TIntermBinary *node) 726 { 727 visitNode(visit, node); 728 729 if (mOptions.validateExpressionTypes && visit == PreVisit) 730 { 731 validateExpressionTypeBinary(node); 732 } 733 734 return true; 735 } 736 737 bool ValidateAST::visitUnary(Visit visit, TIntermUnary *node) 738 { 739 visitNode(visit, node); 740 741 if (visit == PreVisit && mOptions.validateBuiltInOps) 742 { 743 visitBuiltInFunction(node, node->getFunction()); 744 } 745 746 return true; 747 } 748 749 bool ValidateAST::visitTernary(Visit visit, TIntermTernary *node) 750 { 751 visitNode(visit, node); 752 return true; 753 } 754 755 bool ValidateAST::visitIfElse(Visit visit, TIntermIfElse *node) 756 { 757 visitNode(visit, node); 758 return true; 759 } 760 761 bool ValidateAST::visitSwitch(Visit visit, TIntermSwitch *node) 762 { 763 visitNode(visit, node); 764 765 if (mOptions.validateExpressionTypes && visit == PreVisit) 766 { 767 validateExpressionTypeSwitch(node); 768 } 769 770 return true; 771 } 772 773 bool ValidateAST::visitCase(Visit visit, TIntermCase *node) 774 { 775 // Case is allowed to come after a branch, and for dead-code-elimination purposes acts as if a 776 // new block is started. 777 mIsBranchVisitedInBlock = false; 778 779 visitNode(visit, node); 780 781 return true; 782 } 783 784 void ValidateAST::visitFunctionPrototype(TIntermFunctionPrototype *node) 785 { 786 visitNode(PreVisit, node); 787 788 if (mOptions.validateFunctionCall) 789 { 790 const TFunction *function = node->getFunction(); 791 mDeclaredFunctions.insert(function); 792 } 793 794 const TFunction *function = node->getFunction(); 795 const TType &returnType = function->getReturnType(); 796 if (mOptions.validatePrecision && IsPrecisionApplicableToType(returnType.getBasicType()) && 797 returnType.getPrecision() == EbpUndefined) 798 { 799 mDiagnostics->error( 800 node->getLine(), 801 "Found function with undefined precision on return value <validatePrecision>", 802 function->name().data()); 803 mPrecisionFailed = true; 804 } 805 806 if (mOptions.validateStructUsage) 807 { 808 if (returnType.isStructSpecifier()) 809 { 810 visitStructOrInterfaceBlockDeclaration(returnType, node->getLine()); 811 } 812 else 813 { 814 visitStructUsage(returnType, node->getLine()); 815 } 816 } 817 818 for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex) 819 { 820 const TVariable *param = function->getParam(paramIndex); 821 const TType ¶mType = param->getType(); 822 823 if (mOptions.validateStructUsage) 824 { 825 visitStructUsage(paramType, node->getLine()); 826 } 827 828 if (mOptions.validateQualifiers) 829 { 830 TQualifier qualifier = paramType.getQualifier(); 831 if (qualifier != EvqParamIn && qualifier != EvqParamOut && qualifier != EvqParamInOut && 832 qualifier != EvqParamConst) 833 { 834 mDiagnostics->error(node->getLine(), 835 "Found function prototype with an invalid qualifier " 836 "<validateQualifiers>", 837 param->name().data()); 838 mQualifiersFailed = true; 839 } 840 841 if (IsOpaqueType(paramType.getBasicType()) && qualifier != EvqParamIn) 842 { 843 mDiagnostics->error( 844 node->getLine(), 845 "Found function prototype with an invalid qualifier on opaque parameter " 846 "<validateQualifiers>", 847 param->name().data()); 848 mQualifiersFailed = true; 849 } 850 } 851 852 if (mOptions.validatePrecision && IsPrecisionApplicableToType(paramType.getBasicType()) && 853 paramType.getPrecision() == EbpUndefined) 854 { 855 mDiagnostics->error( 856 node->getLine(), 857 "Found function parameter with undefined precision <validatePrecision>", 858 param->name().data()); 859 mPrecisionFailed = true; 860 } 861 } 862 } 863 864 bool ValidateAST::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) 865 { 866 visitNode(visit, node); 867 scope(visit); 868 869 if (mOptions.validateVariableReferences && visit == PreVisit) 870 { 871 const TFunction *function = node->getFunction(); 872 873 size_t paramCount = function->getParamCount(); 874 for (size_t paramIndex = 0; paramIndex < paramCount; ++paramIndex) 875 { 876 const TVariable *variable = function->getParam(paramIndex); 877 878 if (isVariableDeclared(variable)) 879 { 880 mDiagnostics->error(node->getLine(), 881 "Found two declarations of the same function argument " 882 "<validateVariableReferences>", 883 variable->name().data()); 884 mVariableReferencesFailed = true; 885 break; 886 } 887 888 mDeclaredVariables.back().insert(variable); 889 } 890 } 891 892 return true; 893 } 894 895 bool ValidateAST::visitAggregate(Visit visit, TIntermAggregate *node) 896 { 897 visitNode(visit, node); 898 expectNonNullChildren(visit, node, 0); 899 900 if (visit == PreVisit && mOptions.validateBuiltInOps) 901 { 902 visitBuiltInFunction(node, node->getFunction()); 903 } 904 905 if (visit == PreVisit && mOptions.validateFunctionCall) 906 { 907 visitFunctionCall(node); 908 } 909 910 if (visit == PreVisit && mOptions.validateNoRawFunctionCalls) 911 { 912 if (node->getOp() == EOpCallInternalRawFunction) 913 { 914 mDiagnostics->error(node->getLine(), 915 "Found node calling a raw function (deprecated) " 916 "<validateNoRawFunctionCalls>", 917 node->getFunction()->name().data()); 918 mNoRawFunctionCallsFailed = true; 919 } 920 } 921 922 return true; 923 } 924 925 bool ValidateAST::visitBlock(Visit visit, TIntermBlock *node) 926 { 927 visitNode(visit, node); 928 scope(visit); 929 expectNonNullChildren(visit, node, 0); 930 931 if (visit == PostVisit) 932 { 933 // If the parent is a block and mIsBranchVisitedInBlock is set, this is a nested block 934 // without any condition (like if, loop or switch), so the rest of the parent block is also 935 // dead code. Otherwise the parent block can contain code after this. 936 if (getParentNode() == nullptr || getParentNode()->getAsBlock() == nullptr) 937 { 938 mIsBranchVisitedInBlock = false; 939 } 940 } 941 942 return true; 943 } 944 945 bool ValidateAST::visitGlobalQualifierDeclaration(Visit visit, 946 TIntermGlobalQualifierDeclaration *node) 947 { 948 visitNode(visit, node); 949 950 const TVariable *variable = &node->getSymbol()->variable(); 951 952 if (mOptions.validateVariableReferences && variableNeedsDeclaration(variable)) 953 { 954 if (!isVariableDeclared(variable)) 955 { 956 mDiagnostics->error(node->getLine(), 957 "Found reference to undeclared or inconsistently transformed " 958 "variable <validateVariableReferences>", 959 variable->name().data()); 960 mVariableReferencesFailed = true; 961 } 962 } 963 return true; 964 } 965 966 bool ValidateAST::visitDeclaration(Visit visit, TIntermDeclaration *node) 967 { 968 visitNode(visit, node); 969 expectNonNullChildren(visit, node, 0); 970 971 const TIntermSequence &sequence = *(node->getSequence()); 972 973 if (mOptions.validateMultiDeclarations && sequence.size() > 1) 974 { 975 TIntermSymbol *symbol = sequence[1]->getAsSymbolNode(); 976 if (symbol == nullptr) 977 { 978 TIntermBinary *init = sequence[1]->getAsBinaryNode(); 979 ASSERT(init && init->getOp() == EOpInitialize); 980 symbol = init->getLeft()->getAsSymbolNode(); 981 } 982 ASSERT(symbol); 983 984 mDiagnostics->error(node->getLine(), 985 "Found multiple declarations where SeparateDeclarations should have " 986 "separated them <validateMultiDeclarations>", 987 symbol->variable().name().data()); 988 mMultiDeclarationsFailed = true; 989 } 990 991 if (visit == PreVisit) 992 { 993 bool validateStructUsage = mOptions.validateStructUsage; 994 995 for (TIntermNode *instance : sequence) 996 { 997 TIntermSymbol *symbol = instance->getAsSymbolNode(); 998 if (symbol == nullptr) 999 { 1000 TIntermBinary *init = instance->getAsBinaryNode(); 1001 ASSERT(init && init->getOp() == EOpInitialize); 1002 symbol = init->getLeft()->getAsSymbolNode(); 1003 } 1004 ASSERT(symbol); 1005 1006 const TVariable *variable = &symbol->variable(); 1007 const TType &type = variable->getType(); 1008 1009 if (mOptions.validateVariableReferences) 1010 { 1011 if (isVariableDeclared(variable)) 1012 { 1013 mDiagnostics->error( 1014 node->getLine(), 1015 "Found two declarations of the same variable <validateVariableReferences>", 1016 variable->name().data()); 1017 mVariableReferencesFailed = true; 1018 break; 1019 } 1020 1021 mDeclaredVariables.back().insert(variable); 1022 1023 const TInterfaceBlock *interfaceBlock = variable->getType().getInterfaceBlock(); 1024 1025 if (variable->symbolType() == SymbolType::Empty && interfaceBlock != nullptr) 1026 { 1027 // Nameless interface blocks can only be declared at the top level. Their 1028 // fields are matched by field index, and then verified to match by name. 1029 // Conflict in names should have already generated a compile error. 1030 ASSERT(mDeclaredVariables.size() == 1); 1031 ASSERT(mNamelessInterfaceBlocks.count(interfaceBlock) == 0); 1032 1033 mNamelessInterfaceBlocks.insert(interfaceBlock); 1034 } 1035 } 1036 1037 if (validateStructUsage) 1038 { 1039 // Only declare and/or validate the struct once. 1040 validateStructUsage = false; 1041 1042 if (type.isStructSpecifier() || type.isInterfaceBlock()) 1043 { 1044 visitStructOrInterfaceBlockDeclaration(type, node->getLine()); 1045 } 1046 else 1047 { 1048 visitStructUsage(type, node->getLine()); 1049 } 1050 } 1051 1052 if (gl::IsBuiltInName(variable->name().data())) 1053 { 1054 visitBuiltInVariable(symbol); 1055 } 1056 1057 if (mOptions.validatePrecision && (type.isStructSpecifier() || type.isInterfaceBlock())) 1058 { 1059 const TFieldListCollection *structOrBlock = type.getStruct(); 1060 if (structOrBlock == nullptr) 1061 { 1062 structOrBlock = type.getInterfaceBlock(); 1063 } 1064 1065 for (const TField *field : structOrBlock->fields()) 1066 { 1067 const TType *fieldType = field->type(); 1068 if (IsPrecisionApplicableToType(fieldType->getBasicType()) && 1069 fieldType->getPrecision() == EbpUndefined) 1070 { 1071 mDiagnostics->error( 1072 node->getLine(), 1073 "Found block field with undefined precision <validatePrecision>", 1074 field->name().data()); 1075 mPrecisionFailed = true; 1076 } 1077 } 1078 } 1079 } 1080 } 1081 1082 return true; 1083 } 1084 1085 bool ValidateAST::visitLoop(Visit visit, TIntermLoop *node) 1086 { 1087 visitNode(visit, node); 1088 return true; 1089 } 1090 1091 bool ValidateAST::visitBranch(Visit visit, TIntermBranch *node) 1092 { 1093 visitNode(visit, node); 1094 1095 if (visit == PostVisit) 1096 { 1097 mIsBranchVisitedInBlock = true; 1098 } 1099 1100 return true; 1101 } 1102 1103 void ValidateAST::visitPreprocessorDirective(TIntermPreprocessorDirective *node) 1104 { 1105 visitNode(PreVisit, node); 1106 } 1107 1108 bool ValidateAST::validateInternal() 1109 { 1110 return !mSingleParentFailed && !mVariableReferencesFailed && !mBuiltInOpsFailed && 1111 !mFunctionCallFailed && !mNoRawFunctionCallsFailed && !mNullNodesFailed && 1112 !mQualifiersFailed && !mPrecisionFailed && !mStructUsageFailed && 1113 !mExpressionTypesFailed && !mMultiDeclarationsFailed && !mNoSwizzleOfSwizzleFailed && 1114 !mNoStatementsAfterBranchFailed; 1115 } 1116 1117 } // anonymous namespace 1118 1119 bool ValidateAST(TIntermNode *root, TDiagnostics *diagnostics, const ValidateASTOptions &options) 1120 { 1121 // ValidateAST is called after transformations, so if |validateNoMoreTransformations| is set, 1122 // it's immediately an error. 1123 if (options.validateNoMoreTransformations) 1124 { 1125 diagnostics->error(kNoSourceLoc, "Unexpected transformation after AST post-processing", 1126 "<validateNoMoreTransformations>"); 1127 return false; 1128 } 1129 1130 return ValidateAST::validate(root, diagnostics, options); 1131 } 1132 1133 } // namespace sh