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