1 | // |
2 | // Copyright (C) 2002-2005 3Dlabs Inc. Ltd. |
3 | // Copyright (C) 2016 Google, Inc. |
4 | // |
5 | // All rights reserved. |
6 | // |
7 | // Redistribution and use in source and binary forms, with or without |
8 | // modification, are permitted provided that the following conditions |
9 | // are met: |
10 | // |
11 | // Redistributions of source code must retain the above copyright |
12 | // notice, this list of conditions and the following disclaimer. |
13 | // |
14 | // Redistributions in binary form must reproduce the above |
15 | // copyright notice, this list of conditions and the following |
16 | // disclaimer in the documentation and/or other materials provided |
17 | // with the distribution. |
18 | // |
19 | // Neither the name of 3Dlabs Inc. Ltd. nor the names of its |
20 | // contributors may be used to endorse or promote products derived |
21 | // from this software without specific prior written permission. |
22 | // |
23 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
25 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
26 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
27 | // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
28 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
29 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
30 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
31 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
32 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
33 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
34 | // POSSIBILITY OF SUCH DAMAGE. |
35 | // |
36 | |
37 | // Implement the TParseContextBase class. |
38 | |
39 | #include <cstdarg> |
40 | |
41 | #include "ParseHelper.h" |
42 | |
43 | extern int yyparse(glslang::TParseContext*); |
44 | |
45 | namespace glslang { |
46 | |
47 | // |
48 | // Used to output syntax, parsing, and semantic errors. |
49 | // |
50 | |
51 | void TParseContextBase::outputMessage(const TSourceLoc& loc, const char* szReason, |
52 | const char* szToken, |
53 | const char* , |
54 | TPrefixType prefix, va_list args) |
55 | { |
56 | const int maxSize = MaxTokenLength + 200; |
57 | char [maxSize]; |
58 | |
59 | safe_vsprintf(szExtraInfo, maxSize, szExtraInfoFormat, args); |
60 | |
61 | infoSink.info.prefix(prefix); |
62 | infoSink.info.location(loc); |
63 | infoSink.info << "'" << szToken << "' : " << szReason << " " << szExtraInfo << "\n" ; |
64 | |
65 | if (prefix == EPrefixError) { |
66 | ++numErrors; |
67 | } |
68 | } |
69 | |
70 | void C_DECL TParseContextBase::error(const TSourceLoc& loc, const char* szReason, const char* szToken, |
71 | const char* , ...) |
72 | { |
73 | if (messages & EShMsgOnlyPreprocessor) |
74 | return; |
75 | va_list args; |
76 | va_start(args, szExtraInfoFormat); |
77 | outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); |
78 | va_end(args); |
79 | |
80 | if ((messages & EShMsgCascadingErrors) == 0) |
81 | currentScanner->setEndOfInput(); |
82 | } |
83 | |
84 | void C_DECL TParseContextBase::warn(const TSourceLoc& loc, const char* szReason, const char* szToken, |
85 | const char* , ...) |
86 | { |
87 | if (suppressWarnings()) |
88 | return; |
89 | va_list args; |
90 | va_start(args, szExtraInfoFormat); |
91 | outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args); |
92 | va_end(args); |
93 | } |
94 | |
95 | void C_DECL TParseContextBase::ppError(const TSourceLoc& loc, const char* szReason, const char* szToken, |
96 | const char* , ...) |
97 | { |
98 | va_list args; |
99 | va_start(args, szExtraInfoFormat); |
100 | outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); |
101 | va_end(args); |
102 | |
103 | if ((messages & EShMsgCascadingErrors) == 0) |
104 | currentScanner->setEndOfInput(); |
105 | } |
106 | |
107 | void C_DECL TParseContextBase::ppWarn(const TSourceLoc& loc, const char* szReason, const char* szToken, |
108 | const char* , ...) |
109 | { |
110 | va_list args; |
111 | va_start(args, szExtraInfoFormat); |
112 | outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args); |
113 | va_end(args); |
114 | } |
115 | |
116 | // |
117 | // Both test and if necessary, spit out an error, to see if the node is really |
118 | // an l-value that can be operated on this way. |
119 | // |
120 | // Returns true if there was an error. |
121 | // |
122 | bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) |
123 | { |
124 | TIntermBinary* binaryNode = node->getAsBinaryNode(); |
125 | |
126 | if (binaryNode) { |
127 | switch(binaryNode->getOp()) { |
128 | case EOpIndexDirect: |
129 | case EOpIndexIndirect: // fall through |
130 | case EOpIndexDirectStruct: // fall through |
131 | case EOpVectorSwizzle: |
132 | case EOpMatrixSwizzle: |
133 | return lValueErrorCheck(loc, op, binaryNode->getLeft()); |
134 | default: |
135 | break; |
136 | } |
137 | error(loc, " l-value required" , op, "" , "" ); |
138 | |
139 | return true; |
140 | } |
141 | |
142 | const char* symbol = nullptr; |
143 | TIntermSymbol* symNode = node->getAsSymbolNode(); |
144 | if (symNode != nullptr) |
145 | symbol = symNode->getName().c_str(); |
146 | |
147 | const char* message = nullptr; |
148 | switch (node->getQualifier().storage) { |
149 | case EvqConst: message = "can't modify a const" ; break; |
150 | case EvqConstReadOnly: message = "can't modify a const" ; break; |
151 | case EvqUniform: message = "can't modify a uniform" ; break; |
152 | case EvqBuffer: |
153 | if (node->getQualifier().readonly) |
154 | message = "can't modify a readonly buffer" ; |
155 | #ifdef NV_EXTENSIONS |
156 | if (node->getQualifier().layoutShaderRecordNV) |
157 | message = "can't modify a shaderrecordnv qualified buffer" ; |
158 | #endif |
159 | break; |
160 | #ifdef NV_EXTENSIONS |
161 | case EvqHitAttrNV: |
162 | if (language != EShLangIntersectNV) |
163 | message = "cannot modify hitAttributeNV in this stage" ; |
164 | break; |
165 | #endif |
166 | |
167 | default: |
168 | // |
169 | // Type that can't be written to? |
170 | // |
171 | switch (node->getBasicType()) { |
172 | case EbtSampler: |
173 | message = "can't modify a sampler" ; |
174 | break; |
175 | case EbtAtomicUint: |
176 | message = "can't modify an atomic_uint" ; |
177 | break; |
178 | case EbtVoid: |
179 | message = "can't modify void" ; |
180 | break; |
181 | #ifdef NV_EXTENSIONS |
182 | case EbtAccStructNV: |
183 | message = "can't modify accelerationStructureNV" ; |
184 | break; |
185 | #endif |
186 | default: |
187 | break; |
188 | } |
189 | } |
190 | |
191 | if (message == nullptr && binaryNode == nullptr && symNode == nullptr) { |
192 | error(loc, " l-value required" , op, "" , "" ); |
193 | |
194 | return true; |
195 | } |
196 | |
197 | // |
198 | // Everything else is okay, no error. |
199 | // |
200 | if (message == nullptr) |
201 | return false; |
202 | |
203 | // |
204 | // If we get here, we have an error and a message. |
205 | // |
206 | if (symNode) |
207 | error(loc, " l-value required" , op, "\"%s\" (%s)" , symbol, message); |
208 | else |
209 | error(loc, " l-value required" , op, "(%s)" , message); |
210 | |
211 | return true; |
212 | } |
213 | |
214 | // Test for and give an error if the node can't be read from. |
215 | void TParseContextBase::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) |
216 | { |
217 | if (! node) |
218 | return; |
219 | |
220 | TIntermBinary* binaryNode = node->getAsBinaryNode(); |
221 | if (binaryNode) { |
222 | switch(binaryNode->getOp()) { |
223 | case EOpIndexDirect: |
224 | case EOpIndexIndirect: |
225 | case EOpIndexDirectStruct: |
226 | case EOpVectorSwizzle: |
227 | case EOpMatrixSwizzle: |
228 | rValueErrorCheck(loc, op, binaryNode->getLeft()); |
229 | default: |
230 | break; |
231 | } |
232 | |
233 | return; |
234 | } |
235 | |
236 | TIntermSymbol* symNode = node->getAsSymbolNode(); |
237 | if (symNode && symNode->getQualifier().writeonly) |
238 | error(loc, "can't read from writeonly object: " , op, symNode->getName().c_str()); |
239 | } |
240 | |
241 | // Add 'symbol' to the list of deferred linkage symbols, which |
242 | // are later processed in finish(), at which point the symbol |
243 | // must still be valid. |
244 | // It is okay if the symbol's type will be subsequently edited; |
245 | // the modifications will be tracked. |
246 | // Order is preserved, to avoid creating novel forward references. |
247 | void TParseContextBase::trackLinkage(TSymbol& symbol) |
248 | { |
249 | if (!parsingBuiltins) |
250 | linkageSymbols.push_back(&symbol); |
251 | } |
252 | |
253 | // Ensure index is in bounds, correct if necessary. |
254 | // Give an error if not. |
255 | void TParseContextBase::checkIndex(const TSourceLoc& loc, const TType& type, int& index) |
256 | { |
257 | if (index < 0) { |
258 | error(loc, "" , "[" , "index out of range '%d'" , index); |
259 | index = 0; |
260 | } else if (type.isArray()) { |
261 | if (type.isSizedArray() && index >= type.getOuterArraySize()) { |
262 | error(loc, "" , "[" , "array index out of range '%d'" , index); |
263 | index = type.getOuterArraySize() - 1; |
264 | } |
265 | } else if (type.isVector()) { |
266 | if (index >= type.getVectorSize()) { |
267 | error(loc, "" , "[" , "vector index out of range '%d'" , index); |
268 | index = type.getVectorSize() - 1; |
269 | } |
270 | } else if (type.isMatrix()) { |
271 | if (index >= type.getMatrixCols()) { |
272 | error(loc, "" , "[" , "matrix index out of range '%d'" , index); |
273 | index = type.getMatrixCols() - 1; |
274 | } |
275 | } |
276 | } |
277 | |
278 | // Make a shared symbol have a non-shared version that can be edited by the current |
279 | // compile, such that editing its type will not change the shared version and will |
280 | // effect all nodes already sharing it (non-shallow type), |
281 | // or adopting its full type after being edited (shallow type). |
282 | void TParseContextBase::makeEditable(TSymbol*& symbol) |
283 | { |
284 | // copyUp() does a deep copy of the type. |
285 | symbol = symbolTable.copyUp(symbol); |
286 | |
287 | // Save it (deferred, so it can be edited first) in the AST for linker use. |
288 | if (symbol) |
289 | trackLinkage(*symbol); |
290 | } |
291 | |
292 | // Return a writable version of the variable 'name'. |
293 | // |
294 | // Return nullptr if 'name' is not found. This should mean |
295 | // something is seriously wrong (e.g., compiler asking self for |
296 | // built-in that doesn't exist). |
297 | TVariable* TParseContextBase::getEditableVariable(const char* name) |
298 | { |
299 | bool builtIn; |
300 | TSymbol* symbol = symbolTable.find(name, &builtIn); |
301 | |
302 | assert(symbol != nullptr); |
303 | if (symbol == nullptr) |
304 | return nullptr; |
305 | |
306 | if (builtIn) |
307 | makeEditable(symbol); |
308 | |
309 | return symbol->getAsVariable(); |
310 | } |
311 | |
312 | // Select the best matching function for 'call' from 'candidateList'. |
313 | // |
314 | // Assumptions |
315 | // |
316 | // There is no exact match, so a selection algorithm needs to run. That is, the |
317 | // language-specific handler should check for exact match first, to |
318 | // decide what to do, before calling this selector. |
319 | // |
320 | // Input |
321 | // |
322 | // * list of candidate signatures to select from |
323 | // * the call |
324 | // * a predicate function convertible(from, to) that says whether or not type |
325 | // 'from' can implicitly convert to type 'to' (it includes the case of what |
326 | // the calling language would consider a matching type with no conversion |
327 | // needed) |
328 | // * a predicate function better(from1, from2, to1, to2) that says whether or |
329 | // not a conversion from <-> to2 is considered better than a conversion |
330 | // from <-> to1 (both in and out directions need testing, as declared by the |
331 | // formal parameter) |
332 | // |
333 | // Output |
334 | // |
335 | // * best matching candidate (or none, if no viable candidates found) |
336 | // * whether there was a tie for the best match (ambiguous overload selection, |
337 | // caller's choice for how to report) |
338 | // |
339 | const TFunction* TParseContextBase::selectFunction( |
340 | const TVector<const TFunction*> candidateList, |
341 | const TFunction& call, |
342 | std::function<bool(const TType& from, const TType& to, TOperator op, int arg)> convertible, |
343 | std::function<bool(const TType& from, const TType& to1, const TType& to2)> better, |
344 | /* output */ bool& tie) |
345 | { |
346 | // |
347 | // Operation |
348 | // |
349 | // 1. Prune the input list of candidates down to a list of viable candidates, |
350 | // where each viable candidate has |
351 | // |
352 | // * at least as many parameters as there are calling arguments, with any |
353 | // remaining parameters being optional or having default values |
354 | // * each parameter is true under convertible(A, B), where A is the calling |
355 | // type for in and B is the formal type, and in addition, for out B is the |
356 | // calling type and A is the formal type |
357 | // |
358 | // 2. If there are no viable candidates, return with no match. |
359 | // |
360 | // 3. If there is only one viable candidate, it is the best match. |
361 | // |
362 | // 4. If there are multiple viable candidates, select the first viable candidate |
363 | // as the incumbent. Compare the incumbent to the next viable candidate, and if |
364 | // that candidate is better (bullets below), make it the incumbent. Repeat, with |
365 | // a linear walk through the viable candidate list. The final incumbent will be |
366 | // returned as the best match. A viable candidate is better than the incumbent if |
367 | // |
368 | // * it has a function argument with a better(...) conversion than the incumbent, |
369 | // for all directions needed by in and out |
370 | // * the incumbent has no argument with a better(...) conversion then the |
371 | // candidate, for either in or out (as needed) |
372 | // |
373 | // 5. Check for ambiguity by comparing the best match against all other viable |
374 | // candidates. If any other viable candidate has a function argument with a |
375 | // better(...) conversion than the best candidate (for either in or out |
376 | // directions), return that there was a tie for best. |
377 | // |
378 | |
379 | tie = false; |
380 | |
381 | // 1. prune to viable... |
382 | TVector<const TFunction*> viableCandidates; |
383 | for (auto it = candidateList.begin(); it != candidateList.end(); ++it) { |
384 | const TFunction& candidate = *(*it); |
385 | |
386 | // to even be a potential match, number of arguments must be >= the number of |
387 | // fixed (non-default) parameters, and <= the total (including parameter with defaults). |
388 | if (call.getParamCount() < candidate.getFixedParamCount() || |
389 | call.getParamCount() > candidate.getParamCount()) |
390 | continue; |
391 | |
392 | // see if arguments are convertible |
393 | bool viable = true; |
394 | |
395 | // The call can have fewer parameters than the candidate, if some have defaults. |
396 | const int paramCount = std::min(call.getParamCount(), candidate.getParamCount()); |
397 | for (int param = 0; param < paramCount; ++param) { |
398 | if (candidate[param].type->getQualifier().isParamInput()) { |
399 | if (! convertible(*call[param].type, *candidate[param].type, candidate.getBuiltInOp(), param)) { |
400 | viable = false; |
401 | break; |
402 | } |
403 | } |
404 | if (candidate[param].type->getQualifier().isParamOutput()) { |
405 | if (! convertible(*candidate[param].type, *call[param].type, candidate.getBuiltInOp(), param)) { |
406 | viable = false; |
407 | break; |
408 | } |
409 | } |
410 | } |
411 | |
412 | if (viable) |
413 | viableCandidates.push_back(&candidate); |
414 | } |
415 | |
416 | // 2. none viable... |
417 | if (viableCandidates.size() == 0) |
418 | return nullptr; |
419 | |
420 | // 3. only one viable... |
421 | if (viableCandidates.size() == 1) |
422 | return viableCandidates.front(); |
423 | |
424 | // 4. find best... |
425 | const auto betterParam = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool { |
426 | // is call -> can2 better than call -> can1 for any parameter |
427 | bool hasBetterParam = false; |
428 | for (int param = 0; param < call.getParamCount(); ++param) { |
429 | if (better(*call[param].type, *can1[param].type, *can2[param].type)) { |
430 | hasBetterParam = true; |
431 | break; |
432 | } |
433 | } |
434 | return hasBetterParam; |
435 | }; |
436 | |
437 | const auto equivalentParams = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool { |
438 | // is call -> can2 equivalent to call -> can1 for all the call parameters? |
439 | for (int param = 0; param < call.getParamCount(); ++param) { |
440 | if (better(*call[param].type, *can1[param].type, *can2[param].type) || |
441 | better(*call[param].type, *can2[param].type, *can1[param].type)) |
442 | return false; |
443 | } |
444 | return true; |
445 | }; |
446 | |
447 | const TFunction* incumbent = viableCandidates.front(); |
448 | for (auto it = viableCandidates.begin() + 1; it != viableCandidates.end(); ++it) { |
449 | const TFunction& candidate = *(*it); |
450 | if (betterParam(*incumbent, candidate) && ! betterParam(candidate, *incumbent)) |
451 | incumbent = &candidate; |
452 | } |
453 | |
454 | // 5. ambiguity... |
455 | for (auto it = viableCandidates.begin(); it != viableCandidates.end(); ++it) { |
456 | if (incumbent == *it) |
457 | continue; |
458 | const TFunction& candidate = *(*it); |
459 | |
460 | // In the case of default parameters, it may have an identical initial set, which is |
461 | // also ambiguous |
462 | if (betterParam(*incumbent, candidate) || equivalentParams(*incumbent, candidate)) |
463 | tie = true; |
464 | } |
465 | |
466 | return incumbent; |
467 | } |
468 | |
469 | // |
470 | // Look at a '.' field selector string and change it into numerical selectors |
471 | // for a vector or scalar. |
472 | // |
473 | // Always return some form of swizzle, so the result is always usable. |
474 | // |
475 | void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TString& compString, int vecSize, |
476 | TSwizzleSelectors<TVectorSelector>& selector) |
477 | { |
478 | // Too long? |
479 | if (compString.size() > MaxSwizzleSelectors) |
480 | error(loc, "vector swizzle too long" , compString.c_str(), "" ); |
481 | |
482 | // Use this to test that all swizzle characters are from the same swizzle-namespace-set |
483 | enum { |
484 | exyzw, |
485 | ergba, |
486 | estpq, |
487 | } fieldSet[MaxSwizzleSelectors]; |
488 | |
489 | // Decode the swizzle string. |
490 | int size = std::min(MaxSwizzleSelectors, (int)compString.size()); |
491 | for (int i = 0; i < size; ++i) { |
492 | switch (compString[i]) { |
493 | case 'x': |
494 | selector.push_back(0); |
495 | fieldSet[i] = exyzw; |
496 | break; |
497 | case 'r': |
498 | selector.push_back(0); |
499 | fieldSet[i] = ergba; |
500 | break; |
501 | case 's': |
502 | selector.push_back(0); |
503 | fieldSet[i] = estpq; |
504 | break; |
505 | |
506 | case 'y': |
507 | selector.push_back(1); |
508 | fieldSet[i] = exyzw; |
509 | break; |
510 | case 'g': |
511 | selector.push_back(1); |
512 | fieldSet[i] = ergba; |
513 | break; |
514 | case 't': |
515 | selector.push_back(1); |
516 | fieldSet[i] = estpq; |
517 | break; |
518 | |
519 | case 'z': |
520 | selector.push_back(2); |
521 | fieldSet[i] = exyzw; |
522 | break; |
523 | case 'b': |
524 | selector.push_back(2); |
525 | fieldSet[i] = ergba; |
526 | break; |
527 | case 'p': |
528 | selector.push_back(2); |
529 | fieldSet[i] = estpq; |
530 | break; |
531 | |
532 | case 'w': |
533 | selector.push_back(3); |
534 | fieldSet[i] = exyzw; |
535 | break; |
536 | case 'a': |
537 | selector.push_back(3); |
538 | fieldSet[i] = ergba; |
539 | break; |
540 | case 'q': |
541 | selector.push_back(3); |
542 | fieldSet[i] = estpq; |
543 | break; |
544 | |
545 | default: |
546 | error(loc, "unknown swizzle selection" , compString.c_str(), "" ); |
547 | break; |
548 | } |
549 | } |
550 | |
551 | // Additional error checking. |
552 | for (int i = 0; i < selector.size(); ++i) { |
553 | if (selector[i] >= vecSize) { |
554 | error(loc, "vector swizzle selection out of range" , compString.c_str(), "" ); |
555 | selector.resize(i); |
556 | break; |
557 | } |
558 | |
559 | if (i > 0 && fieldSet[i] != fieldSet[i-1]) { |
560 | error(loc, "vector swizzle selectors not from the same set" , compString.c_str(), "" ); |
561 | selector.resize(i); |
562 | break; |
563 | } |
564 | } |
565 | |
566 | // Ensure it is valid. |
567 | if (selector.size() == 0) |
568 | selector.push_back(0); |
569 | } |
570 | |
571 | // |
572 | // Make the passed-in variable information become a member of the |
573 | // global uniform block. If this doesn't exist yet, make it. |
574 | // |
575 | void TParseContextBase::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) |
576 | { |
577 | // Make the global block, if not yet made. |
578 | if (globalUniformBlock == nullptr) { |
579 | TQualifier blockQualifier; |
580 | blockQualifier.clear(); |
581 | blockQualifier.storage = EvqUniform; |
582 | TType blockType(new TTypeList, *NewPoolTString(getGlobalUniformBlockName()), blockQualifier); |
583 | setUniformBlockDefaults(blockType); |
584 | globalUniformBlock = new TVariable(NewPoolTString("" ), blockType, true); |
585 | firstNewMember = 0; |
586 | } |
587 | |
588 | // Update with binding and set |
589 | globalUniformBlock->getWritableType().getQualifier().layoutBinding = globalUniformBinding; |
590 | globalUniformBlock->getWritableType().getQualifier().layoutSet = globalUniformSet; |
591 | |
592 | // Add the requested member as a member to the global block. |
593 | TType* type = new TType; |
594 | type->shallowCopy(memberType); |
595 | type->setFieldName(memberName); |
596 | if (typeList) |
597 | type->setStruct(typeList); |
598 | TTypeLoc typeLoc = {type, loc}; |
599 | globalUniformBlock->getType().getWritableStruct()->push_back(typeLoc); |
600 | |
601 | // Insert into the symbol table. |
602 | if (firstNewMember == 0) { |
603 | // This is the first request; we need a normal symbol table insert |
604 | if (symbolTable.insert(*globalUniformBlock)) |
605 | trackLinkage(*globalUniformBlock); |
606 | else |
607 | error(loc, "failed to insert the global constant buffer" , "uniform" , "" ); |
608 | } else { |
609 | // This is a follow-on request; we need to amend the first insert |
610 | symbolTable.amend(*globalUniformBlock, firstNewMember); |
611 | } |
612 | |
613 | ++firstNewMember; |
614 | } |
615 | |
616 | void TParseContextBase::finish() |
617 | { |
618 | if (parsingBuiltins) |
619 | return; |
620 | |
621 | // Transfer the linkage symbols to AST nodes, preserving order. |
622 | TIntermAggregate* linkage = new TIntermAggregate; |
623 | for (auto i = linkageSymbols.begin(); i != linkageSymbols.end(); ++i) |
624 | intermediate.addSymbolLinkageNode(linkage, **i); |
625 | intermediate.addSymbolLinkageNodes(linkage, getLanguage(), symbolTable); |
626 | } |
627 | |
628 | } // end namespace glslang |
629 | |