OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkSLIRGenerator.h" | 8 #include "SkSLIRGenerator.h" |
9 | 9 |
10 #include "limits.h" | 10 #include "limits.h" |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
197 auto var = std::unique_ptr<Variable>(new Variable(decl.fPosition, modifi
ers, varDecl.fName, | 197 auto var = std::unique_ptr<Variable>(new Variable(decl.fPosition, modifi
ers, varDecl.fName, |
198 *type, storage)); | 198 *type, storage)); |
199 std::unique_ptr<Expression> value; | 199 std::unique_ptr<Expression> value; |
200 if (varDecl.fValue) { | 200 if (varDecl.fValue) { |
201 value = this->convertExpression(*varDecl.fValue); | 201 value = this->convertExpression(*varDecl.fValue); |
202 if (!value) { | 202 if (!value) { |
203 return nullptr; | 203 return nullptr; |
204 } | 204 } |
205 value = this->coerce(std::move(value), *type); | 205 value = this->coerce(std::move(value), *type); |
206 } | 206 } |
207 if ("sk_FragColor" == varDecl.fName && (*fSymbolTable)[varDecl.fName]) { | 207 if (storage == Variable::kGlobal_Storage && "sk_FragColor" == varDecl.fN
ame && |
| 208 (*fSymbolTable)[varDecl.fName]) { |
208 // already defined, ignore | 209 // already defined, ignore |
209 } else if ((*fSymbolTable)[varDecl.fName] && | 210 } else if (storage == Variable::kGlobal_Storage && (*fSymbolTable)[varDe
cl.fName] && |
210 (*fSymbolTable)[varDecl.fName]->fKind == Symbol::kVariable_Ki
nd && | 211 (*fSymbolTable)[varDecl.fName]->fKind == Symbol::kVariable_Ki
nd && |
211 ((Variable*) (*fSymbolTable)[varDecl.fName])->fModifiers.fLay
out.fBuiltin >= 0) { | 212 ((Variable*) (*fSymbolTable)[varDecl.fName])->fModifiers.fLay
out.fBuiltin >= 0) { |
212 // already defined, just update the modifiers | 213 // already defined, just update the modifiers |
213 Variable* old = (Variable*) (*fSymbolTable)[varDecl.fName]; | 214 Variable* old = (Variable*) (*fSymbolTable)[varDecl.fName]; |
214 old->fModifiers = var->fModifiers; | 215 old->fModifiers = var->fModifiers; |
215 } else { | 216 } else { |
216 variables.emplace_back(var.get(), std::move(sizes), std::move(value)
); | 217 variables.emplace_back(var.get(), std::move(sizes), std::move(value)
); |
217 fSymbolTable->add(varDecl.fName, std::move(var)); | 218 fSymbolTable->add(varDecl.fName, std::move(var)); |
218 } | 219 } |
219 } | 220 } |
220 return std::unique_ptr<VarDeclarations>(new VarDeclarations(decl.fPosition, | 221 return std::unique_ptr<VarDeclarations>(new VarDeclarations(decl.fPosition, |
(...skipping 449 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
670 args.push_back(std::move(expr)); | 671 args.push_back(std::move(expr)); |
671 return std::unique_ptr<Expression>(new Constructor(Position(), type, std::mo
ve(args))); | 672 return std::unique_ptr<Expression>(new Constructor(Position(), type, std::mo
ve(args))); |
672 } | 673 } |
673 | 674 |
674 static bool is_matrix_multiply(const Type& left, const Type& right) { | 675 static bool is_matrix_multiply(const Type& left, const Type& right) { |
675 if (left.kind() == Type::kMatrix_Kind) { | 676 if (left.kind() == Type::kMatrix_Kind) { |
676 return right.kind() == Type::kMatrix_Kind || right.kind() == Type::kVect
or_Kind; | 677 return right.kind() == Type::kMatrix_Kind || right.kind() == Type::kVect
or_Kind; |
677 } | 678 } |
678 return left.kind() == Type::kVector_Kind && right.kind() == Type::kMatrix_Ki
nd; | 679 return left.kind() == Type::kVector_Kind && right.kind() == Type::kMatrix_Ki
nd; |
679 } | 680 } |
| 681 |
| 682 static bool is_assignment(Token::Kind op) { |
| 683 switch (op) { |
| 684 case Token::EQ: // fall through |
| 685 case Token::PLUSEQ: // fall through |
| 686 case Token::MINUSEQ: // fall through |
| 687 case Token::STAREQ: // fall through |
| 688 case Token::SLASHEQ: // fall through |
| 689 case Token::PERCENTEQ: // fall through |
| 690 case Token::SHLEQ: // fall through |
| 691 case Token::SHREQ: // fall through |
| 692 case Token::BITWISEOREQ: // fall through |
| 693 case Token::BITWISEXOREQ: // fall through |
| 694 case Token::BITWISEANDEQ: // fall through |
| 695 case Token::LOGICALOREQ: // fall through |
| 696 case Token::LOGICALXOREQ: // fall through |
| 697 case Token::LOGICALANDEQ: |
| 698 return true; |
| 699 default: |
| 700 return false; |
| 701 } |
| 702 } |
| 703 |
680 /** | 704 /** |
681 * Determines the operand and result types of a binary expression. Returns true
if the expression is | 705 * Determines the operand and result types of a binary expression. Returns true
if the expression is |
682 * legal, false otherwise. If false, the values of the out parameters are undefi
ned. | 706 * legal, false otherwise. If false, the values of the out parameters are undefi
ned. |
683 */ | 707 */ |
684 static bool determine_binary_type(const Context& context, | 708 static bool determine_binary_type(const Context& context, |
685 Token::Kind op, | 709 Token::Kind op, |
686 const Type& left, | 710 const Type& left, |
687 const Type& right, | 711 const Type& right, |
688 const Type** outLeftType, | 712 const Type** outLeftType, |
689 const Type** outRightType, | 713 const Type** outRightType, |
690 const Type** outResultType, | 714 const Type** outResultType, |
691 bool tryFlipped) { | 715 bool tryFlipped) { |
692 bool isLogical; | 716 bool isLogical; |
| 717 bool validMatrixOrVectorOp; |
693 switch (op) { | 718 switch (op) { |
| 719 case Token::EQ: |
| 720 *outLeftType = &left; |
| 721 *outRightType = &left; |
| 722 *outResultType = &left; |
| 723 return right.canCoerceTo(left); |
694 case Token::EQEQ: // fall through | 724 case Token::EQEQ: // fall through |
695 case Token::NEQ: // fall through | 725 case Token::NEQ: |
| 726 isLogical = true; |
| 727 validMatrixOrVectorOp = true; |
| 728 break; |
696 case Token::LT: // fall through | 729 case Token::LT: // fall through |
697 case Token::GT: // fall through | 730 case Token::GT: // fall through |
698 case Token::LTEQ: // fall through | 731 case Token::LTEQ: // fall through |
699 case Token::GTEQ: | 732 case Token::GTEQ: |
700 isLogical = true; | 733 isLogical = true; |
| 734 validMatrixOrVectorOp = false; |
701 break; | 735 break; |
702 case Token::LOGICALOR: // fall through | 736 case Token::LOGICALOR: // fall through |
703 case Token::LOGICALAND: // fall through | 737 case Token::LOGICALAND: // fall through |
704 case Token::LOGICALXOR: // fall through | 738 case Token::LOGICALXOR: // fall through |
705 case Token::LOGICALOREQ: // fall through | 739 case Token::LOGICALOREQ: // fall through |
706 case Token::LOGICALANDEQ: // fall through | 740 case Token::LOGICALANDEQ: // fall through |
707 case Token::LOGICALXOREQ: | 741 case Token::LOGICALXOREQ: |
708 *outLeftType = context.fBool_Type.get(); | 742 *outLeftType = context.fBool_Type.get(); |
709 *outRightType = context.fBool_Type.get(); | 743 *outRightType = context.fBool_Type.get(); |
710 *outResultType = context.fBool_Type.get(); | 744 *outResultType = context.fBool_Type.get(); |
(...skipping 30 matching lines...) Expand all Loading... |
741 } else { | 775 } else { |
742 // result was a column vector, transpose it back to a ro
w | 776 // result was a column vector, transpose it back to a ro
w |
743 *outResultType = &(*outResultType)->toCompound(context,
leftRows, | 777 *outResultType = &(*outResultType)->toCompound(context,
leftRows, |
744 rightColu
mns); | 778 rightColu
mns); |
745 } | 779 } |
746 return leftColumns == rightRows; | 780 return leftColumns == rightRows; |
747 } else { | 781 } else { |
748 return false; | 782 return false; |
749 } | 783 } |
750 } | 784 } |
751 // fall through | 785 isLogical = false; |
| 786 validMatrixOrVectorOp = true; |
| 787 break; |
| 788 case Token::PLUS: // fall through |
| 789 case Token::PLUSEQ: // fall through |
| 790 case Token::MINUS: // fall through |
| 791 case Token::MINUSEQ: // fall through |
| 792 case Token::SLASH: // fall through |
| 793 case Token::SLASHEQ: |
| 794 isLogical = false; |
| 795 validMatrixOrVectorOp = true; |
| 796 break; |
752 default: | 797 default: |
753 isLogical = false; | 798 isLogical = false; |
| 799 validMatrixOrVectorOp = false; |
754 } | 800 } |
755 // FIXME: need to disallow illegal operations like vec3 > vec3. Also do not
currently have | 801 bool isVectorOrMatrix = left.kind() == Type::kVector_Kind || left.kind() ==
Type::kMatrix_Kind; |
756 // full support for numbers other than float. | 802 // FIXME: incorrect for shift |
757 if (left == right) { | 803 if (right.canCoerceTo(left) && (left.kind() == Type::kScalar_Kind || |
| 804 (isVectorOrMatrix && validMatrixOrVectorOp)))
{ |
758 *outLeftType = &left; | 805 *outLeftType = &left; |
759 *outRightType = &left; | 806 *outRightType = &left; |
760 if (isLogical) { | 807 if (isLogical) { |
761 *outResultType = context.fBool_Type.get(); | 808 *outResultType = context.fBool_Type.get(); |
762 } else { | 809 } else { |
763 *outResultType = &left; | 810 *outResultType = &left; |
764 } | 811 } |
765 return true; | 812 return true; |
766 } | 813 } |
767 // FIXME: incorrect for shift operations | |
768 if (left.canCoerceTo(right)) { | |
769 *outLeftType = &right; | |
770 *outRightType = &right; | |
771 if (isLogical) { | |
772 *outResultType = context.fBool_Type.get(); | |
773 } else { | |
774 *outResultType = &right; | |
775 } | |
776 return true; | |
777 } | |
778 if ((left.kind() == Type::kVector_Kind || left.kind() == Type::kMatrix_Kind)
&& | 814 if ((left.kind() == Type::kVector_Kind || left.kind() == Type::kMatrix_Kind)
&& |
779 (right.kind() == Type::kScalar_Kind)) { | 815 (right.kind() == Type::kScalar_Kind)) { |
780 if (determine_binary_type(context, op, left.componentType(), right, outL
eftType, | 816 if (determine_binary_type(context, op, left.componentType(), right, outL
eftType, |
781 outRightType, outResultType, false)) { | 817 outRightType, outResultType, false)) { |
782 *outLeftType = &(*outLeftType)->toCompound(context, left.columns(),
left.rows()); | 818 *outLeftType = &(*outLeftType)->toCompound(context, left.columns(),
left.rows()); |
783 if (!isLogical) { | 819 if (!isLogical) { |
784 *outResultType = &(*outResultType)->toCompound(context, left.col
umns(), | 820 *outResultType = &(*outResultType)->toCompound(context, left.col
umns(), |
785 left.rows()); | 821 left.rows()); |
786 } | 822 } |
787 return true; | 823 return true; |
(...skipping 14 matching lines...) Expand all Loading... |
802 return nullptr; | 838 return nullptr; |
803 } | 839 } |
804 std::unique_ptr<Expression> right = this->convertExpression(*expression.fRig
ht); | 840 std::unique_ptr<Expression> right = this->convertExpression(*expression.fRig
ht); |
805 if (!right) { | 841 if (!right) { |
806 return nullptr; | 842 return nullptr; |
807 } | 843 } |
808 const Type* leftType; | 844 const Type* leftType; |
809 const Type* rightType; | 845 const Type* rightType; |
810 const Type* resultType; | 846 const Type* resultType; |
811 if (!determine_binary_type(fContext, expression.fOperator, left->fType, righ
t->fType, &leftType, | 847 if (!determine_binary_type(fContext, expression.fOperator, left->fType, righ
t->fType, &leftType, |
812 &rightType, &resultType, true)) { | 848 &rightType, &resultType, !is_assignment(expressio
n.fOperator))) { |
813 fErrors.error(expression.fPosition, "type mismatch: '" + | 849 fErrors.error(expression.fPosition, "type mismatch: '" + |
814 Token::OperatorName(expression.fOper
ator) + | 850 Token::OperatorName(expression.fOper
ator) + |
815 "' cannot operate on '" + left->fTyp
e.fName + | 851 "' cannot operate on '" + left->fTyp
e.fName + |
816 "', '" + right->fType.fName + "'"); | 852 "', '" + right->fType.fName + "'"); |
817 return nullptr; | 853 return nullptr; |
818 } | 854 } |
819 switch (expression.fOperator) { | 855 if (is_assignment(expression.fOperator)) { |
820 case Token::EQ: // fall through | 856 this->markWrittenTo(*left); |
821 case Token::PLUSEQ: // fall through | 857 } |
822 case Token::MINUSEQ: // fall through | 858 left = this->coerce(std::move(left), *leftType); |
823 case Token::STAREQ: // fall through | 859 right = this->coerce(std::move(right), *rightType); |
824 case Token::SLASHEQ: // fall through | 860 if (!left || !right) { |
825 case Token::PERCENTEQ: // fall through | 861 return nullptr; |
826 case Token::SHLEQ: // fall through | |
827 case Token::SHREQ: // fall through | |
828 case Token::BITWISEOREQ: // fall through | |
829 case Token::BITWISEXOREQ: // fall through | |
830 case Token::BITWISEANDEQ: // fall through | |
831 case Token::LOGICALOREQ: // fall through | |
832 case Token::LOGICALXOREQ: // fall through | |
833 case Token::LOGICALANDEQ: | |
834 this->markWrittenTo(*left); | |
835 default: | |
836 break; | |
837 } | 862 } |
838 return std::unique_ptr<Expression>(new BinaryExpression(expression.fPosition
, | 863 return std::unique_ptr<Expression>(new BinaryExpression(expression.fPosition
, |
839 this->coerce(std::mo
ve(left), | 864 std::move(left), |
840 *leftTy
pe), | |
841 expression.fOperator
, | 865 expression.fOperator
, |
842 this->coerce(std::mo
ve(right), | 866 std::move(right), |
843 *rightT
ype), | |
844 *resultType)); | 867 *resultType)); |
845 } | 868 } |
846 | 869 |
847 std::unique_ptr<Expression> IRGenerator::convertTernaryExpression( | 870 std::unique_ptr<Expression> IRGenerator::convertTernaryExpression( |
848 const ASTTernaryExpre
ssion& expression) { | 871 const ASTTernaryExpre
ssion& expression) { |
849 std::unique_ptr<Expression> test = this->coerce(this->convertExpression(*exp
ression.fTest), | 872 std::unique_ptr<Expression> test = this->coerce(this->convertExpression(*exp
ression.fTest), |
850 *fContext.fBool_Type); | 873 *fContext.fBool_Type); |
851 if (!test) { | 874 if (!test) { |
852 return nullptr; | 875 return nullptr; |
853 } | 876 } |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
887 " argument"; | 910 " argument"; |
888 if (function.fParameters.size() != 1) { | 911 if (function.fParameters.size() != 1) { |
889 msg += "s"; | 912 msg += "s"; |
890 } | 913 } |
891 msg += ", but found " + to_string((uint64_t) arguments.size()); | 914 msg += ", but found " + to_string((uint64_t) arguments.size()); |
892 fErrors.error(position, msg); | 915 fErrors.error(position, msg); |
893 return nullptr; | 916 return nullptr; |
894 } | 917 } |
895 for (size_t i = 0; i < arguments.size(); i++) { | 918 for (size_t i = 0; i < arguments.size(); i++) { |
896 arguments[i] = this->coerce(std::move(arguments[i]), function.fParameter
s[i]->fType); | 919 arguments[i] = this->coerce(std::move(arguments[i]), function.fParameter
s[i]->fType); |
| 920 if (!arguments[i]) { |
| 921 return nullptr; |
| 922 } |
897 if (arguments[i] && (function.fParameters[i]->fModifiers.fFlags & Modifi
ers::kOut_Flag)) { | 923 if (arguments[i] && (function.fParameters[i]->fModifiers.fFlags & Modifi
ers::kOut_Flag)) { |
898 this->markWrittenTo(*arguments[i]); | 924 this->markWrittenTo(*arguments[i]); |
899 } | 925 } |
900 } | 926 } |
901 return std::unique_ptr<FunctionCall>(new FunctionCall(position, function, | 927 return std::unique_ptr<FunctionCall>(new FunctionCall(position, function, |
902 std::move(arguments)))
; | 928 std::move(arguments)))
; |
903 } | 929 } |
904 | 930 |
905 /** | 931 /** |
906 * Determines the cost of coercing the arguments of a function to the required t
ypes. Returns true | 932 * Determines the cost of coercing the arguments of a function to the required t
ypes. Returns true |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
984 } | 1010 } |
985 if (args.size() == 1 && args[0]->fType == type) { | 1011 if (args.size() == 1 && args[0]->fType == type) { |
986 // argument is already the right type, just return it | 1012 // argument is already the right type, just return it |
987 return std::move(args[0]); | 1013 return std::move(args[0]); |
988 } | 1014 } |
989 if (type.isNumber()) { | 1015 if (type.isNumber()) { |
990 if (args.size() != 1) { | 1016 if (args.size() != 1) { |
991 fErrors.error(position, "invalid arguments to '" + type.description(
) + | 1017 fErrors.error(position, "invalid arguments to '" + type.description(
) + |
992 "' constructor, (expected exactly 1 argument
, but found " + | 1018 "' constructor, (expected exactly 1 argument
, but found " + |
993 to_string((uint64_t) args.size()) + ")"); | 1019 to_string((uint64_t) args.size()) + ")"); |
| 1020 return nullptr; |
994 } | 1021 } |
995 if (args[0]->fType == *fContext.fBool_Type) { | 1022 if (args[0]->fType == *fContext.fBool_Type) { |
996 std::unique_ptr<IntLiteral> zero(new IntLiteral(fContext, position,
0)); | 1023 std::unique_ptr<IntLiteral> zero(new IntLiteral(fContext, position,
0)); |
997 std::unique_ptr<IntLiteral> one(new IntLiteral(fContext, position, 1
)); | 1024 std::unique_ptr<IntLiteral> one(new IntLiteral(fContext, position, 1
)); |
998 return std::unique_ptr<Expression>( | 1025 return std::unique_ptr<Expression>( |
999 new TernaryExpression(position, std::mo
ve(args[0]), | 1026 new TernaryExpression(position, std::mo
ve(args[0]), |
1000 this->coerce(std:
:move(one), type), | 1027 this->coerce(std:
:move(one), type), |
1001 this->coerce(std:
:move(zero), | 1028 this->coerce(std:
:move(zero), |
1002 type
))); | 1029 type
))); |
1003 } else if (!args[0]->fType.isNumber()) { | 1030 } else if (!args[0]->fType.isNumber()) { |
1004 fErrors.error(position, "invalid argument to '" + type.description()
+ | 1031 fErrors.error(position, "invalid argument to '" + type.description()
+ |
1005 "' constructor (expected a number or bool, b
ut found '" + | 1032 "' constructor (expected a number or bool, b
ut found '" + |
1006 args[0]->fType.description() + "')"); | 1033 args[0]->fType.description() + "')"); |
1007 } | 1034 } |
1008 if (args[0]->fKind == Expression::kIntLiteral_Kind && (type == *fContext
.fInt_Type || | 1035 if (args[0]->fKind == Expression::kIntLiteral_Kind && (type == *fContext
.fInt_Type || |
1009 type == *fContext.fUInt_Type)) { | 1036 type == *fContext.fUInt_Type)) { |
1010 return std::unique_ptr<Expression>(new IntLiteral(fContext, | 1037 return std::unique_ptr<Expression>(new IntLiteral(fContext, |
1011 position, | 1038 position, |
1012 ((IntLiteral&) *ar
gs[0]).fValue, | 1039 ((IntLiteral&) *ar
gs[0]).fValue, |
1013 &type)); | 1040 &type)); |
1014 } | 1041 } |
1015 } else if (kind == Type::kArray_Kind) { | 1042 } else if (kind == Type::kArray_Kind) { |
1016 const Type& base = type.componentType(); | 1043 const Type& base = type.componentType(); |
1017 for (size_t i = 0; i < args.size(); i++) { | 1044 for (size_t i = 0; i < args.size(); i++) { |
1018 args[i] = this->coerce(std::move(args[i]), base); | 1045 args[i] = this->coerce(std::move(args[i]), base); |
| 1046 if (!args[i]) { |
| 1047 return nullptr; |
| 1048 } |
1019 } | 1049 } |
1020 } else { | 1050 } else { |
1021 ASSERT(kind == Type::kVector_Kind || kind == Type::kMatrix_Kind); | 1051 ASSERT(kind == Type::kVector_Kind || kind == Type::kMatrix_Kind); |
1022 int actual = 0; | 1052 int actual = 0; |
1023 for (size_t i = 0; i < args.size(); i++) { | 1053 for (size_t i = 0; i < args.size(); i++) { |
1024 if (args[i]->fType.kind() == Type::kVector_Kind || | 1054 if (args[i]->fType.kind() == Type::kVector_Kind || |
1025 args[i]->fType.kind() == Type::kMatrix_Kind) { | 1055 args[i]->fType.kind() == Type::kMatrix_Kind) { |
1026 int columns = args[i]->fType.columns(); | 1056 int columns = args[i]->fType.columns(); |
1027 int rows = args[i]->fType.rows(); | 1057 int rows = args[i]->fType.rows(); |
1028 args[i] = this->coerce(std::move(args[i]), | 1058 args[i] = this->coerce(std::move(args[i]), |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1292 | 1322 |
1293 void IRGenerator::checkValid(const Expression& expr) { | 1323 void IRGenerator::checkValid(const Expression& expr) { |
1294 switch (expr.fKind) { | 1324 switch (expr.fKind) { |
1295 case Expression::kFunctionReference_Kind: | 1325 case Expression::kFunctionReference_Kind: |
1296 fErrors.error(expr.fPosition, "expected '(' to begin function call")
; | 1326 fErrors.error(expr.fPosition, "expected '(' to begin function call")
; |
1297 break; | 1327 break; |
1298 case Expression::kTypeReference_Kind: | 1328 case Expression::kTypeReference_Kind: |
1299 fErrors.error(expr.fPosition, "expected '(' to begin constructor inv
ocation"); | 1329 fErrors.error(expr.fPosition, "expected '(' to begin constructor inv
ocation"); |
1300 break; | 1330 break; |
1301 default: | 1331 default: |
1302 ASSERT(expr.fType != *fContext.fInvalid_Type); | 1332 if (expr.fType == *fContext.fInvalid_Type) { |
1303 break; | 1333 fErrors.error(expr.fPosition, "invalid expression"); |
| 1334 } |
1304 } | 1335 } |
1305 } | 1336 } |
1306 | 1337 |
1307 void IRGenerator::markReadFrom(const Variable& var) { | 1338 void IRGenerator::markReadFrom(const Variable& var) { |
1308 var.fIsReadFrom = true; | 1339 var.fIsReadFrom = true; |
1309 } | 1340 } |
1310 | 1341 |
1311 static bool has_duplicates(const Swizzle& swizzle) { | 1342 static bool has_duplicates(const Swizzle& swizzle) { |
1312 int bits = 0; | 1343 int bits = 0; |
1313 for (int idx : swizzle.fComponents) { | 1344 for (int idx : swizzle.fComponents) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1345 case Expression::kIndex_Kind: | 1376 case Expression::kIndex_Kind: |
1346 this->markWrittenTo(*((IndexExpression&) expr).fBase); | 1377 this->markWrittenTo(*((IndexExpression&) expr).fBase); |
1347 break; | 1378 break; |
1348 default: | 1379 default: |
1349 fErrors.error(expr.fPosition, "cannot assign to '" + expr.descriptio
n() + "'"); | 1380 fErrors.error(expr.fPosition, "cannot assign to '" + expr.descriptio
n() + "'"); |
1350 break; | 1381 break; |
1351 } | 1382 } |
1352 } | 1383 } |
1353 | 1384 |
1354 } | 1385 } |
OLD | NEW |