diff options
Diffstat (limited to 'servers/rendering/shader_language.cpp')
| -rw-r--r-- | servers/rendering/shader_language.cpp | 1492 | 
1 files changed, 1070 insertions, 422 deletions
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index cb98a71e86..14b21e1f42 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -33,6 +33,8 @@  #include "core/string/print_string.h"  #include "servers/rendering_server.h" +#define HAS_WARNING(flag) (warning_flags & flag) +  static bool _is_text_char(char32_t c) {  	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';  } @@ -558,13 +560,13 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {  								return _make_token(TK_ERROR, "Invalid numeric constant");  							}  							hexa_found = true; -						} else if (GETCHAR(i) == 'e') { -							if (hexa_found || exponent_found || float_suffix_found) { +						} else if (GETCHAR(i) == 'e' && !hexa_found) { +							if (exponent_found || float_suffix_found) {  								return _make_token(TK_ERROR, "Invalid numeric constant");  							}  							exponent_found = true; -						} else if (GETCHAR(i) == 'f') { -							if (hexa_found || exponent_found) { +						} else if (GETCHAR(i) == 'f' && !hexa_found) { +							if (exponent_found) {  								return _make_token(TK_ERROR, "Invalid numeric constant");  							}  							float_suffix_found = true; @@ -901,6 +903,8 @@ bool ShaderLanguage::is_token_nonvoid_datatype(TokenType p_type) {  void ShaderLanguage::clear() {  	current_function = StringName(); +	last_name = StringName(); +	last_type = IDENTIFIER_MAX;  	completion_type = COMPLETION_NONE;  	completion_block = nullptr; @@ -908,12 +912,22 @@ void ShaderLanguage::clear() {  	completion_class = SubClassTag::TAG_GLOBAL;  	completion_struct = StringName(); +#ifdef DEBUG_ENABLED +	used_constants.clear(); +	used_varyings.clear(); +	used_uniforms.clear(); +	used_functions.clear(); +	used_structs.clear(); +	warnings.clear(); +#endif // DEBUG_ENABLED +  	error_line = 0;  	tk_line = 1;  	char_idx = 0;  	error_set = false;  	error_str = "";  	last_const = false; +	pass_array = false;  	while (nodes) {  		Node *n = nodes;  		nodes = nodes->next; @@ -921,6 +935,35 @@ void ShaderLanguage::clear() {  	}  } +#ifdef DEBUG_ENABLED +void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, IdentifierType p_type) { +	switch (p_type) { +		case IdentifierType::IDENTIFIER_CONSTANT: +			if (HAS_WARNING(ShaderWarning::UNUSED_CONSTANT_FLAG) && used_constants.has(p_identifier)) { +				used_constants[p_identifier].used = true; +			} +			break; +		case IdentifierType::IDENTIFIER_VARYING: +			if (HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG) && used_varyings.has(p_identifier)) { +				used_varyings[p_identifier].used = true; +			} +			break; +		case IdentifierType::IDENTIFIER_UNIFORM: +			if (HAS_WARNING(ShaderWarning::UNUSED_UNIFORM_FLAG) && used_uniforms.has(p_identifier)) { +				used_uniforms[p_identifier].used = true; +			} +			break; +		case IdentifierType::IDENTIFIER_FUNCTION: +			if (HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG) && used_functions.has(p_identifier)) { +				used_functions[p_identifier].used = true; +			} +			break; +		default: +			break; +	} +} +#endif // DEBUG_ENABLED +  bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name, ConstantNode::Value *r_constant_value) {  	if (p_function_info.built_ins.has(p_identifier)) {  		if (r_data_type) { @@ -932,7 +975,6 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea  		if (r_type) {  			*r_type = IDENTIFIER_BUILTIN_VAR;  		} -  		return true;  	} @@ -946,7 +988,6 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea  		if (r_type) {  			*r_type = IDENTIFIER_FUNCTION;  		} -  		return true;  	} @@ -963,16 +1004,15 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea  			if (r_array_size) {  				*r_array_size = p_block->variables[p_identifier].array_size;  			} -			if (r_type) { -				*r_type = IDENTIFIER_LOCAL_VAR; -			}  			if (r_struct_name) {  				*r_struct_name = p_block->variables[p_identifier].struct_name;  			}  			if (r_constant_value) {  				*r_constant_value = p_block->variables[p_identifier].value;  			} - +			if (r_type) { +				*r_type = IDENTIFIER_LOCAL_VAR; +			}  			return true;  		} @@ -994,15 +1034,18 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea  				if (r_data_type) {  					*r_data_type = function->arguments[i].type;  				} -				if (r_type) { -					*r_type = IDENTIFIER_FUNCTION_ARGUMENT; -				}  				if (r_struct_name) {  					*r_struct_name = function->arguments[i].type_str;  				} +				if (r_array_size) { +					*r_array_size = function->arguments[i].array_size; +				}  				if (r_is_const) {  					*r_is_const = function->arguments[i].is_const;  				} +				if (r_type) { +					*r_type = IDENTIFIER_FUNCTION_ARGUMENT; +				}  				return true;  			}  		} @@ -1041,9 +1084,6 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea  		if (r_array_size) {  			*r_array_size = shader->constants[p_identifier].array_size;  		} -		if (r_type) { -			*r_type = IDENTIFIER_CONSTANT; -		}  		if (r_struct_name) {  			*r_struct_name = shader->constants[p_identifier].type_str;  		} @@ -1052,6 +1092,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea  				*r_constant_value = shader->constants[p_identifier].initializer->values[0];  			}  		} +		if (r_type) { +			*r_type = IDENTIFIER_CONSTANT; +		}  		return true;  	} @@ -1064,6 +1107,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea  			if (r_data_type) {  				*r_data_type = shader->functions[i].function->return_type;  			} +			if (r_array_size) { +				*r_array_size = shader->functions[i].function->return_array_size; +			}  			if (r_type) {  				*r_type = IDENTIFIER_FUNCTION;  			} @@ -1074,13 +1120,18 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea  	return false;  } -bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type) { +bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) {  	bool valid = false;  	DataType ret_type = TYPE_VOID; +	int ret_size = 0;  	switch (p_op->op) {  		case OP_EQUAL:  		case OP_NOT_EQUAL: { +			if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { +				break; // don't accept arrays +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			DataType nb = p_op->arguments[1]->get_datatype();  			valid = na == nb; @@ -1090,6 +1141,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  		case OP_LESS_EQUAL:  		case OP_GREATER:  		case OP_GREATER_EQUAL: { +			if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { +				break; // don't accept arrays +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			DataType nb = p_op->arguments[1]->get_datatype(); @@ -1099,6 +1154,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  		} break;  		case OP_AND:  		case OP_OR: { +			if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { +				break; // don't accept arrays +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			DataType nb = p_op->arguments[1]->get_datatype(); @@ -1107,6 +1166,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  		} break;  		case OP_NOT: { +			if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) { +				break; // don't accept arrays +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			valid = na == TYPE_BOOL;  			ret_type = TYPE_BOOL; @@ -1117,6 +1180,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  		case OP_POST_INCREMENT:  		case OP_POST_DECREMENT:  		case OP_NEGATE: { +			if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) { +				break; // don't accept arrays +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			valid = na > TYPE_BOOL && na < TYPE_MAT2;  			ret_type = na; @@ -1125,6 +1192,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  		case OP_SUB:  		case OP_MUL:  		case OP_DIV: { +			if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { +				break; // don't accept arrays +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			DataType nb = p_op->arguments[1]->get_datatype(); @@ -1193,6 +1264,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  			 * component-wise.  			 */ +			if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { +				break; // don't accept arrays +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			DataType nb = p_op->arguments[1]->get_datatype(); @@ -1245,6 +1320,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  		case OP_ASSIGN_SHIFT_RIGHT:  		case OP_SHIFT_LEFT:  		case OP_SHIFT_RIGHT: { +			if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { +				break; // don't accept arrays +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			DataType nb = p_op->arguments[1]->get_datatype(); @@ -1293,6 +1372,18 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  			}  		} break;  		case OP_ASSIGN: { +			int sa = 0; +			int sb = 0; +			if (!p_op->arguments[0]->is_indexed()) { +				sa = p_op->arguments[0]->get_array_size(); +			} +			if (!p_op->arguments[1]->is_indexed()) { +				sb = p_op->arguments[1]->get_array_size(); +			} +			if (sa != sb) { +				break; // don't accept arrays if their sizes are not equal +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			DataType nb = p_op->arguments[1]->get_datatype();  			if (na == TYPE_STRUCT || nb == TYPE_STRUCT) { @@ -1301,11 +1392,24 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  				valid = na == nb;  			}  			ret_type = na; +			ret_size = sa;  		} break;  		case OP_ASSIGN_ADD:  		case OP_ASSIGN_SUB:  		case OP_ASSIGN_MUL:  		case OP_ASSIGN_DIV: { +			int sa = 0; +			int sb = 0; +			if (!p_op->arguments[0]->is_indexed()) { +				sa = p_op->arguments[0]->get_array_size(); +			} +			if (!p_op->arguments[1]->is_indexed()) { +				sb = p_op->arguments[1]->get_array_size(); +			} +			if (sa > 0 || sb > 0) { +				break; // don't accept arrays +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			DataType nb = p_op->arguments[1]->get_datatype(); @@ -1373,6 +1477,18 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  			 * must match.  			 */ +			int sa = 0; +			int sb = 0; +			if (!p_op->arguments[0]->is_indexed()) { +				sa = p_op->arguments[0]->get_array_size(); +			} +			if (!p_op->arguments[1]->is_indexed()) { +				sb = p_op->arguments[1]->get_array_size(); +			} +			if (sa > 0 || sb > 0) { +				break; // don't accept arrays +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			DataType nb = p_op->arguments[1]->get_datatype(); @@ -1427,17 +1543,34 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  			}  		} break;  		case OP_BIT_INVERT: { //unaries +			if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) { +				break; // don't accept arrays +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			valid = na >= TYPE_INT && na < TYPE_FLOAT;  			ret_type = na;  		} break;  		case OP_SELECT_IF: { +			int sa = 0; +			int sb = 0; +			if (!p_op->arguments[1]->is_indexed()) { +				sa = p_op->arguments[1]->get_array_size(); +			} +			if (!p_op->arguments[2]->is_indexed()) { +				sb = p_op->arguments[2]->get_array_size(); +			} +			if (sa != sb) { +				break; // don't accept arrays if their sizes are not equal +			} +  			DataType na = p_op->arguments[0]->get_datatype();  			DataType nb = p_op->arguments[1]->get_datatype();  			DataType nc = p_op->arguments[2]->get_datatype();  			valid = na == TYPE_BOOL && (nb == nc);  			ret_type = nb; +			ret_size = sa;  		} break;  		default: {  			ERR_FAIL_V(false); @@ -1447,6 +1580,9 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type  	if (r_ret_type) {  		*r_ret_type = ret_type;  	} +	if (r_ret_size) { +		*r_ret_size = ret_size; +	}  	return valid;  } @@ -2182,6 +2318,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI  	Vector<DataType> args;  	Vector<StringName> args2; +	Vector<int> args3;  	ERR_FAIL_COND_V(p_func->arguments[0]->type != Node::TYPE_VARIABLE, false); @@ -2190,6 +2327,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI  	for (int i = 1; i < p_func->arguments.size(); i++) {  		args.push_back(p_func->arguments[i]->get_datatype());  		args2.push_back(p_func->arguments[i]->get_datatype_name()); +		args3.push_back(p_func->arguments[i]->get_array_size());  	}  	int argcount = args.size(); @@ -2457,6 +2595,11 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI  				} else {  					func_arg_name = get_datatype_name(pfunc->arguments[j].type);  				} +				if (pfunc->arguments[j].array_size > 0) { +					func_arg_name += "["; +					func_arg_name += itos(pfunc->arguments[j].array_size); +					func_arg_name += "]"; +				}  				arg_list += func_arg_name;  			}  		} @@ -2469,21 +2612,32 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI  		bool fail = false;  		for (int j = 0; j < args.size(); j++) { -			if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) { +			if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && args3[j] == 0 && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) {  				//all good, but it needs implicit conversion later -			} else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str)) { +			} else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str) || args3[j] != pfunc->arguments[j].array_size) {  				String func_arg_name;  				if (pfunc->arguments[j].type == TYPE_STRUCT) {  					func_arg_name = pfunc->arguments[j].type_str;  				} else {  					func_arg_name = get_datatype_name(pfunc->arguments[j].type);  				} +				if (pfunc->arguments[j].array_size > 0) { +					func_arg_name += "["; +					func_arg_name += itos(pfunc->arguments[j].array_size); +					func_arg_name += "]"; +				}  				String arg_name;  				if (args[j] == TYPE_STRUCT) {  					arg_name = args2[j];  				} else {  					arg_name = get_datatype_name(args[j]);  				} +				if (args3[j] > 0) { +					arg_name += "["; +					arg_name += itos(args3[j]); +					arg_name += "]"; +				} +  				_set_error(vformat("Invalid argument for \"%s(%s)\" function: argument %s should be %s but is %s.", String(name), arg_list, j + 1, func_arg_name, arg_name));  				fail = true;  				break; @@ -2529,16 +2683,45 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI  	return false;  } -bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) const { -	if (a->get_datatype() != b->get_datatype()) { -		return false; +bool ShaderLanguage::_compare_datatypes(DataType p_datatype_a, String p_datatype_name_a, int p_array_size_a, DataType p_datatype_b, String p_datatype_name_b, int p_array_size_b) { +	bool result = true; + +	if (p_datatype_a == TYPE_STRUCT || p_datatype_b == TYPE_STRUCT) { +		if (p_datatype_name_a != p_datatype_name_b) { +			result = false; +		} +	} else { +		if (p_datatype_a != p_datatype_b) { +			result = false; +		}  	} -	if (a->get_datatype() == TYPE_STRUCT || b->get_datatype() == TYPE_STRUCT) { -		if (a->get_datatype_name() != b->get_datatype_name()) { -			return false; + +	if (p_array_size_a != p_array_size_b) { +		result = false; +	} + +	if (!result) { +		String type_name = p_datatype_a == TYPE_STRUCT ? p_datatype_name_a : get_datatype_name(p_datatype_a); +		if (p_array_size_a > 0) { +			type_name += "["; +			type_name += itos(p_array_size_a); +			type_name += "]";  		} + +		String type_name2 = p_datatype_b == TYPE_STRUCT ? p_datatype_name_b : get_datatype_name(p_datatype_b); +		if (p_array_size_b > 0) { +			type_name2 += "["; +			type_name2 += itos(p_array_size_b); +			type_name2 += "]"; +		} + +		_set_error("Invalid assignment of '" + type_name2 + "' to '" + type_name + "'");  	} -	return true; +	return result; +} + +bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) { +	return _compare_datatypes(a->get_datatype(), a->get_datatype_name(), a->get_array_size(), b->get_datatype(), b->get_datatype_name(), b->get_array_size());  }  bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg) { @@ -2969,6 +3152,20 @@ void ShaderLanguage::get_keyword_list(List<String> *r_keywords) {  	}  } +bool ShaderLanguage::is_control_flow_keyword(String p_keyword) { +	return p_keyword == "break" || +		   p_keyword == "case" || +		   p_keyword == "continue" || +		   p_keyword == "default" || +		   p_keyword == "do" || +		   p_keyword == "else" || +		   p_keyword == "for" || +		   p_keyword == "if" || +		   p_keyword == "return" || +		   p_keyword == "switch" || +		   p_keyword == "while"; +} +  void ShaderLanguage::get_builtin_funcs(List<String> *r_keywords) {  	Set<String> kws; @@ -3109,20 +3306,20 @@ bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, St  	}  	switch (p_varying.stage) {  		case ShaderNode::Varying::STAGE_UNKNOWN: // first assign -			if (current_function == String("vertex")) { +			if (current_function == varying_function_names.vertex) {  				p_varying.stage = ShaderNode::Varying::STAGE_VERTEX; -			} else if (current_function == String("fragment")) { +			} else if (current_function == varying_function_names.fragment) {  				p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT;  			}  			break;  		case ShaderNode::Varying::STAGE_VERTEX: -			if (current_function == String("fragment")) { +			if (current_function == varying_function_names.fragment) {  				*r_message = RTR("Varyings which assigned in 'vertex' function may not be reassigned in 'fragment' or 'light'.");  				return false;  			}  			break;  		case ShaderNode::Varying::STAGE_FRAGMENT: -			if (current_function == String("vertex")) { +			if (current_function == varying_function_names.vertex) {  				*r_message = RTR("Varyings which assigned in 'fragment' function may not be reassigned in 'vertex' or 'light'.");  				return false;  			} @@ -3139,25 +3336,25 @@ bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, Str  			*r_message = RTR("Varying must be assigned before using!");  			return false;  		case ShaderNode::Varying::STAGE_VERTEX: -			if (current_function == String("fragment")) { +			if (current_function == varying_function_names.fragment) {  				p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT; -			} else if (current_function == String("light")) { +			} else if (current_function == varying_function_names.light) {  				p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_LIGHT;  			}  			break;  		case ShaderNode::Varying::STAGE_FRAGMENT: -			if (current_function == String("light")) { +			if (current_function == varying_function_names.light) {  				p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT;  			}  			break;  		case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT: -			if (current_function == String("light")) { +			if (current_function == varying_function_names.light) {  				*r_message = RTR("Varying must only be used in two different stages, which can be 'vertex' 'fragment' and 'light'");  				return false;  			}  			break;  		case ShaderNode::Varying::STAGE_VERTEX_TO_LIGHT: -			if (current_function == String("fragment")) { +			if (current_function == varying_function_names.fragment) {  				*r_message = RTR("Varying must only be used in two different stages, which can be 'vertex' 'fragment' and 'light'");  				return false;  			} @@ -3168,6 +3365,36 @@ bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, Str  	return true;  } +bool ShaderLanguage::_check_node_constness(const Node *p_node) const { +	switch (p_node->type) { +		case Node::TYPE_OPERATOR: { +			OperatorNode *op_node = (OperatorNode *)p_node; +			for (int i = int(op_node->op == OP_CALL); i < op_node->arguments.size(); i++) { +				if (!_check_node_constness(op_node->arguments[i])) { +					return false; +				} +			} +		} break; +		case Node::TYPE_CONSTANT: +			break; +		case Node::TYPE_VARIABLE: { +			VariableNode *varn = (VariableNode *)p_node; +			if (!varn->is_const) { +				return false; +			} +		} break; +		case Node::TYPE_ARRAY: { +			ArrayNode *arrn = (ArrayNode *)p_node; +			if (!arrn->is_const) { +				return false; +			} +		} break; +		default: +			return false; +	} +	return true; +} +  bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message) {  	if (p_node->type == Node::TYPE_OPERATOR) {  		OperatorNode *op = static_cast<OperatorNode *>(p_node); @@ -3305,11 +3532,12 @@ bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringNa  	ERR_FAIL_V(false); //bug? function not found  } -ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size) { +ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info) {  	DataType type = TYPE_VOID;  	String struct_name = "";  	int array_size = 0;  	bool auto_size = false; +	bool undefined_size = false;  	Token tk = _get_token();  	if (tk.type == TK_CURLY_BRACKET_OPEN) { @@ -3330,6 +3558,137 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc  			TkPos pos = _get_tkpos();  			tk = _get_token();  			if (tk.type == TK_BRACKET_CLOSE) { +				undefined_size = true; +				tk = _get_token(); +			} else { +				_set_tkpos(pos); + +				Node *n = _parse_and_reduce_expression(p_block, p_function_info); +				if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { +					_set_error("Expected single integer constant > 0"); +					return nullptr; +				} + +				ConstantNode *cnode = (ConstantNode *)n; +				if (cnode->values.size() == 1) { +					array_size = cnode->values[0].sint; +					if (array_size <= 0) { +						_set_error("Expected single integer constant > 0"); +						return nullptr; +					} +				} else { +					_set_error("Expected single integer constant > 0"); +					return nullptr; +				} + +				tk = _get_token(); +				if (tk.type != TK_BRACKET_CLOSE) { +					_set_error("Expected ']'"); +					return nullptr; +				} else { +					tk = _get_token(); +				} +			} +		} else { +			_set_error("Expected '['"); +			return nullptr; +		} +	} + +	ArrayConstructNode *an = alloc_node<ArrayConstructNode>(); + +	if (tk.type == TK_PARENTHESIS_OPEN || auto_size) { // initialization +		int idx = 0; +		while (true) { +			Node *n = _parse_and_reduce_expression(p_block, p_function_info); +			if (!n) { +				return nullptr; +			} + +			// define type by using the first member +			if (auto_size && idx == 0) { +				type = n->get_datatype(); +				if (type == TYPE_STRUCT) { +					struct_name = n->get_datatype_name(); +				} +			} else { +				if (!_compare_datatypes(type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) { +					return nullptr; +				} +			} + +			tk = _get_token(); +			if (tk.type == TK_COMMA) { +				an->initializer.push_back(n); +			} else if (!auto_size && tk.type == TK_PARENTHESIS_CLOSE) { +				an->initializer.push_back(n); +				break; +			} else if (auto_size && tk.type == TK_CURLY_BRACKET_CLOSE) { +				an->initializer.push_back(n); +				break; +			} else { +				if (auto_size) { +					_set_error("Expected '}' or ','"); +				} else { +					_set_error("Expected ')' or ','"); +				} +				return nullptr; +			} +			idx++; +		} +		if (!auto_size && !undefined_size && an->initializer.size() != array_size) { +			_set_error("Array size mismatch"); +			return nullptr; +		} +	} else { +		_set_error("Expected array initialization!"); +		return nullptr; +	} + +	an->datatype = type; +	an->struct_name = struct_name; +	return an; +} + +ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size) { +	DataType type = TYPE_VOID; +	String struct_name = ""; +	int array_size = 0; +	bool auto_size = false; +	TkPos prev_pos = _get_tkpos(); +	Token tk = _get_token(); + +	if (tk.type == TK_CURLY_BRACKET_OPEN) { +		auto_size = true; +	} else { +		if (shader->structs.has(tk.text)) { +			type = TYPE_STRUCT; +			struct_name = tk.text; +		} else { +			if (!is_token_variable_datatype(tk.type)) { +				_set_tkpos(prev_pos); + +				pass_array = true; +				Node *n = _parse_and_reduce_expression(p_block, p_function_info); +				pass_array = false; + +				if (!n) { +					_set_error("Invalid data type for array"); +					return nullptr; +				} + +				if (!_compare_datatypes(p_type, p_struct_name, p_array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) { +					return nullptr; +				} +				return n; +			} +			type = get_token_datatype(tk.type); +		} +		tk = _get_token(); +		if (tk.type == TK_BRACKET_OPEN) { +			TkPos pos = _get_tkpos(); +			tk = _get_token(); +			if (tk.type == TK_BRACKET_CLOSE) {  				array_size = p_array_size;  				tk = _get_token();  			} else { @@ -3401,8 +3760,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc  				return nullptr;  			} -			if (p_type != n->get_datatype() || p_struct_name != n->get_datatype_name()) { -				_set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (type == TYPE_STRUCT ? struct_name : get_datatype_name(type)) + "'"); +			if (!_compare_datatypes(p_type, p_struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) {  				return nullptr;  			} @@ -3502,50 +3860,73 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  			//make sure void is not used in expression  			_set_error("Void value not allowed in Expression");  			return nullptr; -		} else if (is_token_nonvoid_datatype(tk.type)) { -			//basic type constructor +		} else if (is_token_nonvoid_datatype(tk.type) || tk.type == TK_CURLY_BRACKET_OPEN) { +			if (tk.type == TK_CURLY_BRACKET_OPEN) { +				//array constructor -			OperatorNode *func = alloc_node<OperatorNode>(); -			func->op = OP_CONSTRUCT; +				_set_tkpos(prepos); +				expr = _parse_array_constructor(p_block, p_function_info); +			} else { +				DataType datatype; +				DataPrecision precision; +				bool precision_defined = false; -			if (is_token_precision(tk.type)) { -				func->return_precision_cache = get_token_precision(tk.type); +				if (is_token_precision(tk.type)) { +					precision = get_token_precision(tk.type); +					precision_defined = true; +					tk = _get_token(); +				} + +				datatype = get_token_datatype(tk.type);  				tk = _get_token(); -			} -			VariableNode *funcname = alloc_node<VariableNode>(); -			funcname->name = get_datatype_name(get_token_datatype(tk.type)); -			func->arguments.push_back(funcname); +				if (tk.type == TK_BRACKET_OPEN) { +					//array constructor -			tk = _get_token(); -			if (tk.type != TK_PARENTHESIS_OPEN) { -				_set_error("Expected '(' after type name"); -				return nullptr; -			} +					_set_tkpos(prepos); +					expr = _parse_array_constructor(p_block, p_function_info); +				} else { +					if (tk.type != TK_PARENTHESIS_OPEN) { +						_set_error("Expected '(' after type name"); +						return nullptr; +					} +					//basic type constructor -			int carg = -1; +					OperatorNode *func = alloc_node<OperatorNode>(); +					func->op = OP_CONSTRUCT; -			bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg); +					if (precision_defined) { +						func->return_precision_cache = precision; +					} -			if (carg >= 0) { -				completion_type = COMPLETION_CALL_ARGUMENTS; -				completion_line = tk_line; -				completion_block = p_block; -				completion_function = funcname->name; -				completion_argument = carg; -			} +					VariableNode *funcname = alloc_node<VariableNode>(); +					funcname->name = get_datatype_name(datatype); +					func->arguments.push_back(funcname); -			if (!ok) { -				return nullptr; -			} +					int carg = -1; -			if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) { -				_set_error("No matching constructor found for: '" + String(funcname->name) + "'"); -				return nullptr; -			} +					bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg); -			expr = _reduce_expression(p_block, func); +					if (carg >= 0) { +						completion_type = COMPLETION_CALL_ARGUMENTS; +						completion_line = tk_line; +						completion_block = p_block; +						completion_function = funcname->name; +						completion_argument = carg; +					} + +					if (!ok) { +						return nullptr; +					} +					if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) { +						_set_error("No matching constructor found for: '" + String(funcname->name) + "'"); +						return nullptr; +					} + +					expr = _reduce_expression(p_block, func); +				} +			}  		} else if (tk.type == TK_IDENTIFIER) {  			_set_tkpos(prepos); @@ -3558,6 +3939,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  			if (shader->structs.has(identifier)) {  				pstruct = shader->structs[identifier].shader_struct; +#ifdef DEBUG_ENABLED +				if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_STRUCT_FLAG) && used_structs.has(identifier)) { +					used_structs[identifier].used = true; +				} +#endif // DEBUG_ENABLED  				struct_init = true;  			} @@ -3588,11 +3974,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  							if (!nexpr) {  								return nullptr;  							} -							Node *node = pstruct->members[i];  							if (!_compare_datatypes_in_nodes(pstruct->members[i], nexpr)) { -								String type_name = nexpr->get_datatype() == TYPE_STRUCT ? nexpr->get_datatype_name() : get_datatype_name(nexpr->get_datatype()); -								String type_name2 = node->get_datatype() == TYPE_STRUCT ? node->get_datatype_name() : get_datatype_name(node->get_datatype()); -								_set_error("Invalid assignment of '" + type_name + "' to '" + type_name2 + "'");  								return nullptr;  							}  						} @@ -3614,7 +3996,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  					expr = func; -				} else { //a function +				} else { //a function call  					const StringName &name = identifier; @@ -3626,7 +4008,9 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  					int carg = -1; +					pass_array = true;  					bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg); +					pass_array = false;  					// Check if block has a variable with the same name as function to prevent shader crash.  					ShaderLanguage::BlockNode *bnode = p_block; @@ -3679,6 +4063,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  						FunctionNode *call_function = shader->functions[function_index].function;  						if (call_function) { +							func->return_cache = call_function->get_datatype(); +							func->struct_name = call_function->get_datatype_name(); +							func->return_array_size = call_function->get_array_size(); +  							//get current base function  							FunctionNode *base_function = nullptr;  							{ @@ -3781,11 +4169,17 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  						}  					}  					expr = func; +#ifdef DEBUG_ENABLED +					if (check_warnings) { +						_parse_used_identifier(name, IdentifierType::IDENTIFIER_FUNCTION); +					} +#endif // DEBUG_ENABLED  				}  			} else {  				//an identifier -				last_const = false; +				last_name = identifier; +				last_type = IDENTIFIER_MAX;  				_set_tkpos(pos);  				DataType data_type; @@ -3830,12 +4224,16 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  							}  						}  					} -					last_const = is_const;  					if (ident_type == IDENTIFIER_FUNCTION) {  						_set_error("Can't use function as identifier: " + String(identifier));  						return nullptr;  					} +					if (is_const) { +						last_type = IDENTIFIER_CONSTANT; +					} else { +						last_type = ident_type; +					}  				}  				Node *index_expression = nullptr; @@ -3843,60 +4241,61 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  				Node *assign_expression = nullptr;  				if (array_size > 0) { -					tk = _get_token(); - -					if (tk.type != TK_BRACKET_OPEN && tk.type != TK_PERIOD && tk.type != TK_OP_ASSIGN) { -						_set_error("Expected '[','.' or '='"); -						return nullptr; -					} +					if (!pass_array) { +						tk = _get_token(); -					if (tk.type == TK_OP_ASSIGN) { -						if (is_const) { -							_set_error("Constants cannot be modified."); +						if (tk.type != TK_BRACKET_OPEN && tk.type != TK_PERIOD && tk.type != TK_OP_ASSIGN) { +							_set_error("Expected '[','.' or '='");  							return nullptr;  						} -						assign_expression = _parse_array_constructor(p_block, p_function_info, data_type, struct_name, array_size); -						if (!assign_expression) { -							return nullptr; -						} -					} else if (tk.type == TK_PERIOD) { -						completion_class = TAG_ARRAY; -						p_block->block_tag = SubClassTag::TAG_ARRAY; -						call_expression = _parse_and_reduce_expression(p_block, p_function_info); -						p_block->block_tag = SubClassTag::TAG_GLOBAL; -						if (!call_expression) { -							return nullptr; -						} -						data_type = call_expression->get_datatype(); -					} else { // indexing -						index_expression = _parse_and_reduce_expression(p_block, p_function_info); -						if (!index_expression) { -							return nullptr; -						} +						if (tk.type == TK_OP_ASSIGN) { +							if (is_const) { +								_set_error("Constants cannot be modified."); +								return nullptr; +							} +							assign_expression = _parse_array_constructor(p_block, p_function_info, data_type, struct_name, array_size); +							if (!assign_expression) { +								return nullptr; +							} +						} else if (tk.type == TK_PERIOD) { +							completion_class = TAG_ARRAY; +							p_block->block_tag = SubClassTag::TAG_ARRAY; +							call_expression = _parse_and_reduce_expression(p_block, p_function_info); +							p_block->block_tag = SubClassTag::TAG_GLOBAL; +							if (!call_expression) { +								return nullptr; +							} +							data_type = call_expression->get_datatype(); +						} else { // indexing +							index_expression = _parse_and_reduce_expression(p_block, p_function_info); +							if (!index_expression) { +								return nullptr; +							} -						if (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT) { -							_set_error("Only integer expressions are allowed for indexing"); -							return nullptr; -						} +							if (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT) { +								_set_error("Only integer expressions are allowed for indexing"); +								return nullptr; +							} -						if (index_expression->type == Node::TYPE_CONSTANT) { -							ConstantNode *cnode = (ConstantNode *)index_expression; -							if (cnode) { -								if (!cnode->values.is_empty()) { -									int value = cnode->values[0].sint; -									if (value < 0 || value >= array_size) { -										_set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1)); -										return nullptr; +							if (index_expression->type == Node::TYPE_CONSTANT) { +								ConstantNode *cnode = (ConstantNode *)index_expression; +								if (cnode) { +									if (!cnode->values.is_empty()) { +										int value = cnode->values[0].sint; +										if (value < 0 || value >= array_size) { +											_set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1)); +											return nullptr; +										}  									}  								}  							} -						} -						tk = _get_token(); -						if (tk.type != TK_BRACKET_CLOSE) { -							_set_error("Expected ']'"); -							return nullptr; +							tk = _get_token(); +							if (tk.type != TK_BRACKET_CLOSE) { +								_set_error("Expected ']'"); +								return nullptr; +							}  						}  					} @@ -3908,8 +4307,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  					arrname->call_expression = call_expression;  					arrname->assign_expression = assign_expression;  					arrname->is_const = is_const; +					arrname->array_size = array_size;  					expr = arrname; -  				} else {  					VariableNode *varname = alloc_node<VariableNode>();  					varname->name = identifier; @@ -3918,6 +4317,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  					varname->struct_name = struct_name;  					expr = varname;  				} +#ifdef DEBUG_ENABLED +				if (check_warnings) { +					_parse_used_identifier(identifier, ident_type); +				} +#endif // DEBUG_ENABLED  			}  		} else if (tk.type == TK_OP_ADD) {  			continue; //this one does nothing @@ -3956,8 +4360,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  		ERR_FAIL_COND_V(!expr, nullptr);  		/* OK now see what's NEXT to the operator.. */ -		/* OK now see what's NEXT to the operator.. */ -		/* OK now see what's NEXT to the operator.. */  		while (true) {  			TkPos pos2 = _get_tkpos(); @@ -3970,6 +4372,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  				DataType dt = expr->get_datatype();  				String st = expr->get_datatype_name(); +				if (!expr->is_indexed() && expr->get_array_size() > 0) { +					completion_class = TAG_ARRAY; +					p_block->block_tag = SubClassTag::TAG_ARRAY; +					Node *call_expression = _parse_and_reduce_expression(p_block, p_function_info); +					p_block->block_tag = SubClassTag::TAG_GLOBAL; +					if (!call_expression) { +						return nullptr; +					} +					expr = call_expression; +					break; +				} +  				StringName identifier;  				if (_get_completable_identifier(p_block, dt == TYPE_STRUCT ? COMPLETION_STRUCT : COMPLETION_INDEX, identifier)) {  					if (dt == TYPE_STRUCT) { @@ -4246,10 +4660,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  				mn->has_swizzling_duplicates = repeated;  				if (array_size > 0) { +					TkPos prev_pos = _get_tkpos();  					tk = _get_token();  					if (tk.type == TK_OP_ASSIGN) { -						if (last_const) { -							last_const = false; +						if (last_type == IDENTIFIER_CONSTANT) {  							_set_error("Constants cannot be modified.");  							return nullptr;  						} @@ -4259,8 +4673,15 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  						}  						mn->assign_expression = assign_expression;  					} else if (tk.type == TK_PERIOD) { -						_set_error("Nested array length() is not yet implemented"); -						return nullptr; +						completion_class = TAG_ARRAY; +						p_block->block_tag = SubClassTag::TAG_ARRAY; +						Node *call_expression = _parse_and_reduce_expression(p_block, p_function_info); +						p_block->block_tag = SubClassTag::TAG_GLOBAL; +						if (!call_expression) { +							return nullptr; +						} +						mn->datatype = call_expression->get_datatype(); +						mn->call_expression = call_expression;  					} else if (tk.type == TK_BRACKET_OPEN) {  						Node *index_expression = _parse_and_reduce_expression(p_block, p_function_info);  						if (!index_expression) { @@ -4291,13 +4712,14 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  							return nullptr;  						}  						mn->index_expression = index_expression; -  					} else { -						_set_error("Expected '[','.' or '='"); -						return nullptr; +						if (!pass_array) { +							_set_error("Expected '[','.' or '='"); +							return nullptr; +						} +						_set_tkpos(prev_pos);  					}  				} -  				expr = mn;  				//todo @@ -4322,117 +4744,130 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  				}  				DataType member_type = TYPE_VOID; +				String member_struct_name; -				switch (expr->get_datatype()) { -					case TYPE_BVEC2: -					case TYPE_VEC2: -					case TYPE_IVEC2: -					case TYPE_UVEC2: -					case TYPE_MAT2: -						if (index->type == Node::TYPE_CONSTANT) { -							uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; -							if (index_constant >= 2) { -								_set_error("Index out of range (0-1)"); -								return nullptr; +				if (expr->get_array_size() > 0) { +					uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; +					if (index_constant >= (uint32_t)expr->get_array_size()) { +						_set_error(vformat("Index [%s] out of range [%s..%s]", index_constant, 0, expr->get_array_size() - 1)); +						return nullptr; +					} +					member_type = expr->get_datatype(); +					if (member_type == TYPE_STRUCT) { +						member_struct_name = expr->get_datatype_name(); +					} +				} else { +					switch (expr->get_datatype()) { +						case TYPE_BVEC2: +						case TYPE_VEC2: +						case TYPE_IVEC2: +						case TYPE_UVEC2: +						case TYPE_MAT2: +							if (index->type == Node::TYPE_CONSTANT) { +								uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; +								if (index_constant >= 2) { +									_set_error("Index out of range (0-1)"); +									return nullptr; +								}  							} -						} -						switch (expr->get_datatype()) { -							case TYPE_BVEC2: -								member_type = TYPE_BOOL; -								break; -							case TYPE_VEC2: -								member_type = TYPE_FLOAT; -								break; -							case TYPE_IVEC2: -								member_type = TYPE_INT; -								break; -							case TYPE_UVEC2: -								member_type = TYPE_UINT; -								break; -							case TYPE_MAT2: -								member_type = TYPE_VEC2; -								break; -							default: -								break; -						} +							switch (expr->get_datatype()) { +								case TYPE_BVEC2: +									member_type = TYPE_BOOL; +									break; +								case TYPE_VEC2: +									member_type = TYPE_FLOAT; +									break; +								case TYPE_IVEC2: +									member_type = TYPE_INT; +									break; +								case TYPE_UVEC2: +									member_type = TYPE_UINT; +									break; +								case TYPE_MAT2: +									member_type = TYPE_VEC2; +									break; +								default: +									break; +							} -						break; -					case TYPE_BVEC3: -					case TYPE_VEC3: -					case TYPE_IVEC3: -					case TYPE_UVEC3: -					case TYPE_MAT3: -						if (index->type == Node::TYPE_CONSTANT) { -							uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; -							if (index_constant >= 3) { -								_set_error("Index out of range (0-2)"); -								return nullptr; +							break; +						case TYPE_BVEC3: +						case TYPE_VEC3: +						case TYPE_IVEC3: +						case TYPE_UVEC3: +						case TYPE_MAT3: +							if (index->type == Node::TYPE_CONSTANT) { +								uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; +								if (index_constant >= 3) { +									_set_error("Index out of range (0-2)"); +									return nullptr; +								}  							} -						} -						switch (expr->get_datatype()) { -							case TYPE_BVEC3: -								member_type = TYPE_BOOL; -								break; -							case TYPE_VEC3: -								member_type = TYPE_FLOAT; -								break; -							case TYPE_IVEC3: -								member_type = TYPE_INT; -								break; -							case TYPE_UVEC3: -								member_type = TYPE_UINT; -								break; -							case TYPE_MAT3: -								member_type = TYPE_VEC3; -								break; -							default: -								break; -						} -						break; -					case TYPE_BVEC4: -					case TYPE_VEC4: -					case TYPE_IVEC4: -					case TYPE_UVEC4: -					case TYPE_MAT4: -						if (index->type == Node::TYPE_CONSTANT) { -							uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; -							if (index_constant >= 4) { -								_set_error("Index out of range (0-3)"); -								return nullptr; +							switch (expr->get_datatype()) { +								case TYPE_BVEC3: +									member_type = TYPE_BOOL; +									break; +								case TYPE_VEC3: +									member_type = TYPE_FLOAT; +									break; +								case TYPE_IVEC3: +									member_type = TYPE_INT; +									break; +								case TYPE_UVEC3: +									member_type = TYPE_UINT; +									break; +								case TYPE_MAT3: +									member_type = TYPE_VEC3; +									break; +								default: +									break; +							} +							break; +						case TYPE_BVEC4: +						case TYPE_VEC4: +						case TYPE_IVEC4: +						case TYPE_UVEC4: +						case TYPE_MAT4: +							if (index->type == Node::TYPE_CONSTANT) { +								uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; +								if (index_constant >= 4) { +									_set_error("Index out of range (0-3)"); +									return nullptr; +								}  							} -						} -						switch (expr->get_datatype()) { -							case TYPE_BVEC4: -								member_type = TYPE_BOOL; -								break; -							case TYPE_VEC4: -								member_type = TYPE_FLOAT; -								break; -							case TYPE_IVEC4: -								member_type = TYPE_INT; -								break; -							case TYPE_UVEC4: -								member_type = TYPE_UINT; -								break; -							case TYPE_MAT4: -								member_type = TYPE_VEC4; -								break; -							default: -								break; +							switch (expr->get_datatype()) { +								case TYPE_BVEC4: +									member_type = TYPE_BOOL; +									break; +								case TYPE_VEC4: +									member_type = TYPE_FLOAT; +									break; +								case TYPE_IVEC4: +									member_type = TYPE_INT; +									break; +								case TYPE_UVEC4: +									member_type = TYPE_UINT; +									break; +								case TYPE_MAT4: +									member_type = TYPE_VEC4; +									break; +								default: +									break; +							} +							break; +						default: { +							_set_error("Object of type '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' can't be indexed"); +							return nullptr;  						} -						break; -					default: { -						_set_error("Object of type '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' can't be indexed"); -						return nullptr;  					}  				} -  				OperatorNode *op = alloc_node<OperatorNode>();  				op->op = OP_INDEX;  				op->return_cache = member_type; +				op->struct_name = member_struct_name;  				op->arguments.push_back(expr);  				op->arguments.push_back(index);  				expr = op; @@ -4448,7 +4883,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  				op->op = tk.type == TK_OP_DECREMENT ? OP_POST_DECREMENT : OP_POST_INCREMENT;  				op->arguments.push_back(expr); -				if (!_validate_operator(op, &op->return_cache)) { +				if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {  					_set_error("Invalid base type for increment/decrement operator");  					return nullptr;  				} @@ -4599,9 +5034,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  			bool unary = false;  			bool ternary = false; +			Operator op = expression[i].op;  			int priority; -			switch (expression[i].op) { +			switch (op) {  				case OP_EQUAL:  					priority = 8;  					break; @@ -4722,6 +5158,12 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  					ERR_FAIL_V(nullptr); //unexpected operator  			} +#if DEBUG_ENABLED +			if (check_warnings && HAS_WARNING(ShaderWarning::FLOAT_COMPARISON_FLAG) && (op == OP_EQUAL || op == OP_NOT_EQUAL) && expression[i - 1].node->get_datatype() == TYPE_FLOAT && expression[i + 1].node->get_datatype() == TYPE_FLOAT) { +				_add_line_warning(ShaderWarning::FLOAT_COMPARISON); +			} +#endif // DEBUG_ENABLED +  			if (priority < min_priority) {  				// < is used for left to right (default)  				// <= is used for right to left @@ -4735,7 +5177,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  		ERR_FAIL_COND_V(next_op == -1, nullptr);  		// OK! create operator.. -		// OK! create operator..  		if (is_unary) {  			int expr_pos = next_op;  			while (expression[expr_pos].is_op) { @@ -4760,13 +5201,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  				expression.write[i].is_op = false;  				expression.write[i].node = op; -				if (!_validate_operator(op, &op->return_cache)) { +				if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {  					String at;  					for (int j = 0; j < op->arguments.size(); j++) {  						if (j > 0) {  							at += " and ";  						}  						at += get_datatype_name(op->arguments[j]->get_datatype()); +						if (!op->arguments[j]->is_indexed() && op->arguments[j]->get_array_size() > 0) { +							at += "["; +							at += itos(op->arguments[j]->get_array_size()); +							at += "]"; +						}  					}  					_set_error("Invalid arguments to unary operator '" + get_operator_text(op->op) + "' :" + at);  					return nullptr; @@ -4793,13 +5239,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  			expression.write[next_op - 1].is_op = false;  			expression.write[next_op - 1].node = op; -			if (!_validate_operator(op, &op->return_cache)) { +			if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {  				String at;  				for (int i = 0; i < op->arguments.size(); i++) {  					if (i > 0) {  						at += " and ";  					}  					at += get_datatype_name(op->arguments[i]->get_datatype()); +					if (!op->arguments[i]->is_indexed() && op->arguments[i]->get_array_size() > 0) { +						at += "["; +						at += itos(op->arguments[i]->get_array_size()); +						at += "]"; +					}  				}  				_set_error("Invalid argument to ternary ?: operator: " + at);  				return nullptr; @@ -4846,7 +5297,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  			//replace all 3 nodes by this operator and make it an expression -			if (!_validate_operator(op, &op->return_cache)) { +			if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {  				String at;  				for (int i = 0; i < op->arguments.size(); i++) {  					if (i > 0) { @@ -4857,6 +5308,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons  					} else {  						at += get_datatype_name(op->arguments[i]->get_datatype());  					} +					if (!op->arguments[i]->is_indexed() && op->arguments[i]->get_array_size() > 0) { +						at += "["; +						at += itos(op->arguments[i]->get_array_size()); +						at += "]"; +					}  				}  				_set_error("Invalid arguments to operator '" + get_operator_text(op->op) + "' :" + at);  				return nullptr; @@ -5113,6 +5569,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  					ArrayDeclarationNode::Declaration decl;  					decl.name = name;  					decl.size = 0U; +					decl.single_expression = false;  					pos = _get_tkpos();  					tk = _get_token(); @@ -5179,179 +5636,205 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  							return ERR_PARSE_ERROR;  						} +						TkPos prev_pos = _get_tkpos();  						tk = _get_token(); -						if (tk.type != TK_CURLY_BRACKET_OPEN) { -							if (unknown_size) { -								_set_error("Expected '{'"); -								return ERR_PARSE_ERROR; -							} - -							full_def = true; +						if (tk.type == TK_IDENTIFIER) { // a function call array initialization +							_set_tkpos(prev_pos); +							pass_array = true; +							Node *n = _parse_and_reduce_expression(p_block, p_function_info); +							pass_array = false; -							DataPrecision precision2 = PRECISION_DEFAULT; -							if (is_token_precision(tk.type)) { -								precision2 = get_token_precision(tk.type); -								tk = _get_token(); -								if (shader->structs.has(tk.text)) { -									_set_error("Precision modifier cannot be used on structs."); -									return ERR_PARSE_ERROR; +							if (!n) { +								_set_error("Expected correct array initializer!"); +								return ERR_PARSE_ERROR; +							} else { +								if (unknown_size) { +									decl.size = n->get_array_size(); +									var.array_size = n->get_array_size();  								} -								if (!is_token_nonvoid_datatype(tk.type)) { -									_set_error("Expected datatype after precision"); + +								if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) {  									return ERR_PARSE_ERROR;  								} -							} -							DataType type2; -							StringName struct_name2 = ""; +								decl.single_expression = true; +								decl.initializer.push_back(n); +							} -							if (shader->structs.has(tk.text)) { -								type2 = TYPE_STRUCT; -								struct_name2 = tk.text; -							} else { -								if (!is_token_variable_datatype(tk.type)) { -									_set_error("Invalid data type for array"); +							tk = _get_token(); +						} else { +							if (tk.type != TK_CURLY_BRACKET_OPEN) { +								if (unknown_size) { +									_set_error("Expected '{'");  									return ERR_PARSE_ERROR;  								} -								type2 = get_token_datatype(tk.type); -							} -							int array_size2 = 0; +								full_def = true; -							tk = _get_token(); -							if (tk.type == TK_BRACKET_OPEN) { -								TkPos pos2 = _get_tkpos(); -								tk = _get_token(); -								if (tk.type == TK_BRACKET_CLOSE) { -									array_size2 = var.array_size; +								DataPrecision precision2 = PRECISION_DEFAULT; +								if (is_token_precision(tk.type)) { +									precision2 = get_token_precision(tk.type);  									tk = _get_token(); -								} else { -									_set_tkpos(pos2); - -									Node *n = _parse_and_reduce_expression(p_block, p_function_info); -									if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { -										_set_error("Expected single integer constant > 0"); +									if (shader->structs.has(tk.text)) { +										_set_error("Precision modifier cannot be used on structs."); +										return ERR_PARSE_ERROR; +									} +									if (!is_token_nonvoid_datatype(tk.type)) { +										_set_error("Expected datatype after precision");  										return ERR_PARSE_ERROR;  									} +								} -									ConstantNode *cnode = (ConstantNode *)n; -									if (cnode->values.size() == 1) { -										array_size2 = cnode->values[0].sint; -										if (array_size2 <= 0) { -											_set_error("Expected single integer constant > 0"); -											return ERR_PARSE_ERROR; -										} -									} else { -										_set_error("Expected single integer constant > 0"); +								DataType type2; +								StringName struct_name2 = ""; + +								if (shader->structs.has(tk.text)) { +									type2 = TYPE_STRUCT; +									struct_name2 = tk.text; +								} else { +									if (!is_token_variable_datatype(tk.type)) { +										_set_error("Invalid data type for array");  										return ERR_PARSE_ERROR;  									} +									type2 = get_token_datatype(tk.type); +								} +								int array_size2 = 0; + +								tk = _get_token(); +								if (tk.type == TK_BRACKET_OPEN) { +									TkPos pos2 = _get_tkpos();  									tk = _get_token(); -									if (tk.type != TK_BRACKET_CLOSE) { -										_set_error("Expected ']'"); -										return ERR_PARSE_ERROR; +									if (tk.type == TK_BRACKET_CLOSE) { +										array_size2 = var.array_size; +										tk = _get_token();  									} else { +										_set_tkpos(pos2); + +										Node *n = _parse_and_reduce_expression(p_block, p_function_info); +										if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { +											_set_error("Expected single integer constant > 0"); +											return ERR_PARSE_ERROR; +										} + +										ConstantNode *cnode = (ConstantNode *)n; +										if (cnode->values.size() == 1) { +											array_size2 = cnode->values[0].sint; +											if (array_size2 <= 0) { +												_set_error("Expected single integer constant > 0"); +												return ERR_PARSE_ERROR; +											} +										} else { +											_set_error("Expected single integer constant > 0"); +											return ERR_PARSE_ERROR; +										} +  										tk = _get_token(); +										if (tk.type != TK_BRACKET_CLOSE) { +											_set_error("Expected ']'"); +											return ERR_PARSE_ERROR; +										} else { +											tk = _get_token(); +										}  									} -								} -							} else { -								_set_error("Expected '['"); -								return ERR_PARSE_ERROR; -							} - -							if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) { -								String error_str = "Cannot convert from '"; -								if (precision2 != PRECISION_DEFAULT) { -									error_str += get_precision_name(precision2); -									error_str += " "; -								} -								if (type2 == TYPE_STRUCT) { -									error_str += struct_name2;  								} else { -									error_str += get_datatype_name(type2); -								} -								error_str += "["; -								error_str += itos(array_size2); -								error_str += "]'"; -								error_str += " to '"; -								if (precision != PRECISION_DEFAULT) { -									error_str += get_precision_name(precision); -									error_str += " "; +									_set_error("Expected '['"); +									return ERR_PARSE_ERROR;  								} -								if (type == TYPE_STRUCT) { -									error_str += struct_name; -								} else { -									error_str += get_datatype_name(type); + +								if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) { +									String error_str = "Cannot convert from '"; +									if (precision2 != PRECISION_DEFAULT) { +										error_str += get_precision_name(precision2); +										error_str += " "; +									} +									if (type2 == TYPE_STRUCT) { +										error_str += struct_name2; +									} else { +										error_str += get_datatype_name(type2); +									} +									error_str += "["; +									error_str += itos(array_size2); +									error_str += "]'"; +									error_str += " to '"; +									if (precision != PRECISION_DEFAULT) { +										error_str += get_precision_name(precision); +										error_str += " "; +									} +									if (type == TYPE_STRUCT) { +										error_str += struct_name; +									} else { +										error_str += get_datatype_name(type); +									} +									error_str += "["; +									error_str += itos(var.array_size); +									error_str += "]'"; +									_set_error(error_str); +									return ERR_PARSE_ERROR;  								} -								error_str += "["; -								error_str += itos(var.array_size); -								error_str += "]'"; -								_set_error(error_str); -								return ERR_PARSE_ERROR;  							} -						} -						bool curly = tk.type == TK_CURLY_BRACKET_OPEN; +							bool curly = tk.type == TK_CURLY_BRACKET_OPEN; -						if (unknown_size) { -							if (!curly) { -								_set_error("Expected '{'"); -								return ERR_PARSE_ERROR; -							} -						} else { -							if (full_def) { -								if (curly) { -									_set_error("Expected '('"); +							if (unknown_size) { +								if (!curly) { +									_set_error("Expected '{'");  									return ERR_PARSE_ERROR;  								} +							} else { +								if (full_def) { +									if (curly) { +										_set_error("Expected '('"); +										return ERR_PARSE_ERROR; +									} +								}  							} -						} -						if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization -							while (true) { -								Node *n = _parse_and_reduce_expression(p_block, p_function_info); -								if (!n) { -									return ERR_PARSE_ERROR; -								} +							if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization +								while (true) { +									Node *n = _parse_and_reduce_expression(p_block, p_function_info); +									if (!n) { +										return ERR_PARSE_ERROR; +									} -								if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { -									_set_error("Expected constant expression"); -									return ERR_PARSE_ERROR; -								} +									if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { +										_set_error("Expected constant expression"); +										return ERR_PARSE_ERROR; +									} -								if (var.type != n->get_datatype() || struct_name != n->get_datatype_name()) { -									_set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? struct_name : get_datatype_name(var.type)) + "'"); -									return ERR_PARSE_ERROR; -								} +									if (!_compare_datatypes(var.type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) { +										return ERR_PARSE_ERROR; +									} -								tk = _get_token(); -								if (tk.type == TK_COMMA) { -									decl.initializer.push_back(n); -									continue; -								} else if (!curly && tk.type == TK_PARENTHESIS_CLOSE) { -									decl.initializer.push_back(n); -									break; -								} else if (curly && tk.type == TK_CURLY_BRACKET_CLOSE) { -									decl.initializer.push_back(n); -									break; -								} else { -									if (curly) { -										_set_error("Expected '}' or ','"); +									tk = _get_token(); +									if (tk.type == TK_COMMA) { +										decl.initializer.push_back(n); +										continue; +									} else if (!curly && tk.type == TK_PARENTHESIS_CLOSE) { +										decl.initializer.push_back(n); +										break; +									} else if (curly && tk.type == TK_CURLY_BRACKET_CLOSE) { +										decl.initializer.push_back(n); +										break;  									} else { -										_set_error("Expected ')' or ','"); +										if (curly) { +											_set_error("Expected '}' or ','"); +										} else { +											_set_error("Expected ')' or ','"); +										} +										return ERR_PARSE_ERROR;  									} +								} +								if (unknown_size) { +									decl.size = decl.initializer.size(); +									var.array_size = decl.initializer.size(); +								} else if (decl.initializer.size() != var.array_size) { +									_set_error("Array size mismatch");  									return ERR_PARSE_ERROR;  								} +								tk = _get_token();  							} -							if (unknown_size) { -								decl.size = decl.initializer.size(); -								var.array_size = decl.initializer.size(); -							} else if (decl.initializer.size() != var.array_size) { -								_set_error("Array size mismatch"); -								return ERR_PARSE_ERROR; -							} -							tk = _get_token();  						}  					} else {  						if (unknown_size) { @@ -5387,8 +5870,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  						return ERR_PARSE_ERROR;  					}  					if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { -						_set_error("Expected constant expression after '='"); -						return ERR_PARSE_ERROR; +						OperatorNode *op = ((OperatorNode *)n); +						for (int i = 1; i < op->arguments.size(); i++) { +							if (!_check_node_constness(op->arguments[i])) { +								_set_error("Expected constant expression for argument '" + itos(i - 1) + "' of function call after '='"); +								return ERR_PARSE_ERROR; +							} +						}  					}  					decl.initializer = n; @@ -5399,8 +5887,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  						}  					} -					if (var.type == TYPE_STRUCT ? (var.struct_name != n->get_datatype_name()) : (var.type != n->get_datatype())) { -						_set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? String(var.struct_name) : get_datatype_name(var.type)) + "'"); +					if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) {  						return ERR_PARSE_ERROR;  					}  					tk = _get_token(); @@ -5430,7 +5917,6 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  				p_block->statements.push_back(vardecl);  				p_block->variables[name] = var; -  				if (tk.type == TK_COMMA) {  					if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR) {  						_set_error("Multiple declarations in 'for' loop are not implemented yet."); @@ -5847,7 +6333,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  			//check return type  			BlockNode *b = p_block; -			if (b && b->parent_function && (b->parent_function->name == "vertex" || b->parent_function->name == "fragment" || b->parent_function->name == "light")) { +			if (b && b->parent_function && p_function_info.main_function) {  				_set_error(vformat("Using 'return' in '%s' processor function results in undefined behavior!", b->parent_function->name));  				return ERR_PARSE_ERROR;  			} @@ -5862,6 +6348,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  			}  			String return_struct_name = String(b->parent_function->return_struct_name); +			String array_size_string; + +			if (b->parent_function->return_array_size > 0) { +				array_size_string = "[" + itos(b->parent_function->return_array_size) + "]"; +			}  			ControlFlowNode *flow = alloc_node<ControlFlowNode>();  			flow->flow_op = FLOW_OP_RETURN; @@ -5871,18 +6362,27 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  			if (tk.type == TK_SEMICOLON) {  				//all is good  				if (b->parent_function->return_type != TYPE_VOID) { -					_set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + "'"); +					_set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string + "'");  					return ERR_PARSE_ERROR;  				}  			} else {  				_set_tkpos(pos); //rollback, wants expression + +				pass_array = true;  				Node *expr = _parse_and_reduce_expression(p_block, p_function_info);  				if (!expr) {  					return ERR_PARSE_ERROR;  				} +				pass_array = false; -				if (b->parent_function->return_type != expr->get_datatype() || return_struct_name != expr->get_datatype_name()) { -					_set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + "'"); +				bool array_size_incorrect = false; + +				if (b->parent_function->return_array_size > 0 && b->parent_function->return_array_size != expr->get_array_size()) { +					array_size_incorrect = true; +				} + +				if (b->parent_function->return_type != expr->get_datatype() || array_size_incorrect || return_struct_name != expr->get_datatype_name()) { +					_set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string + "'");  					return ERR_PARSE_ERROR;  				} @@ -5926,15 +6426,15 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  			pos = _get_tkpos();  			tk = _get_token();  			if (tk.type != TK_SEMICOLON) { -				//all is good  				_set_error("Expected ';' after discard"); +				return ERR_PARSE_ERROR;  			}  			p_block->statements.push_back(flow);  		} else if (tk.type == TK_CF_BREAK) {  			if (!p_can_break) { -				//all is good -				_set_error("Breaking is not allowed here"); +				_set_error("'break' is not allowed outside of a loop or 'switch' statement"); +				return ERR_PARSE_ERROR;  			}  			ControlFlowNode *flow = alloc_node<ControlFlowNode>(); @@ -5943,8 +6443,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  			pos = _get_tkpos();  			tk = _get_token();  			if (tk.type != TK_SEMICOLON) { -				//all is good  				_set_error("Expected ';' after break"); +				return ERR_PARSE_ERROR;  			}  			p_block->statements.push_back(flow); @@ -5959,8 +6459,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  		} else if (tk.type == TK_CF_CONTINUE) {  			if (!p_can_continue) { -				//all is good -				_set_error("Continuing is not allowed here"); +				_set_error("'continue' is not allowed outside of a loop"); +				return ERR_PARSE_ERROR;  			}  			ControlFlowNode *flow = alloc_node<ControlFlowNode>(); @@ -5971,6 +6471,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun  			if (tk.type != TK_SEMICOLON) {  				//all is good  				_set_error("Expected ';' after continue"); +				return ERR_PARSE_ERROR;  			}  			p_block->statements.push_back(flow); @@ -6259,7 +6760,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  				}  				shader->structs[st.name] = st;  				shader->vstructs.push_back(st); // struct's order is important! - +#ifdef DEBUG_ENABLED +				if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_STRUCT_FLAG)) { +					used_structs.insert(st.name, Usage(tk_line)); +				} +#endif // DEBUG_ENABLED  			} break;  			case TK_GLOBAL: {  				tk = _get_token(); @@ -6606,6 +7111,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  					}  					shader->uniforms[name] = uniform2; +#ifdef DEBUG_ENABLED +					if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_UNIFORM_FLAG)) { +						used_uniforms.insert(name, Usage(tk_line)); +					} +#endif // DEBUG_ENABLED +  					//reset scope for next uniform  					uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL; @@ -6649,6 +7160,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  					}  					shader->varyings[name] = varying; +#ifdef DEBUG_ENABLED +					if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG)) { +						used_varyings.insert(name, Usage(tk_line)); +					} +#endif // DEBUG_ENABLED  				}  			} break; @@ -6661,6 +7177,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  				DataPrecision precision = PRECISION_DEFAULT;  				DataType type;  				StringName name; +				int return_array_size = 0;  				if (tk.type == TK_CONST) {  					is_constant = true; @@ -6698,10 +7215,33 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  				}  				TkPos prev_pos = _get_tkpos();  				tk = _get_token(); +  				if (tk.type == TK_BRACKET_OPEN) { -					_set_error("Cannot use arrays as return types"); -					return ERR_PARSE_ERROR; +					bool error = false; +					tk = _get_token(); + +					if (tk.type == TK_INT_CONSTANT) { +						return_array_size = (int)tk.constant; +						if (return_array_size > 0) { +							tk = _get_token(); +							if (tk.type != TK_BRACKET_CLOSE) { +								_set_error("Expected ']'"); +								return ERR_PARSE_ERROR; +							} +						} else { +							error = true; +						} +					} else { +						error = true; +					} +					if (error) { +						_set_error("Expected integer constant > 0"); +						return ERR_PARSE_ERROR; +					} + +					prev_pos = _get_tkpos();  				} +  				_set_tkpos(prev_pos);  				_get_completable_identifier(nullptr, COMPLETION_MAIN_FUNCTION, name); @@ -6824,7 +7364,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  										} else {  											_set_tkpos(pos2); -											Node *n = _parse_and_reduce_expression(NULL, FunctionInfo()); +											Node *n = _parse_and_reduce_expression(nullptr, FunctionInfo());  											if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {  												_set_error("Expected single integer constant > 0");  												return ERR_PARSE_ERROR; @@ -6905,7 +7445,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  								if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization  									while (true) { -										Node *n = _parse_and_reduce_expression(NULL, FunctionInfo()); +										Node *n = _parse_and_reduce_expression(nullptr, FunctionInfo());  										if (!n) {  											return ERR_PARSE_ERROR;  										} @@ -6915,8 +7455,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  											return ERR_PARSE_ERROR;  										} -										if (constant.type != n->get_datatype() || n->get_datatype_name() != struct_name) { -											_set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (is_struct ? String(struct_name) : get_datatype_name(constant.type)) + "'"); +										if (!_compare_datatypes(constant.type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) {  											return ERR_PARSE_ERROR;  										} @@ -6931,10 +7470,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  											decl.initializer.push_back(n);  											break;  										} else { -											if (curly) +											if (curly) {  												_set_error("Expected '}' or ','"); -											else +											} else {  												_set_error("Expected ')' or ','"); +											}  											return ERR_PARSE_ERROR;  										}  									} @@ -6960,18 +7500,23 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  								constant.initializer = static_cast<ConstantNode *>(expr);  							} else {  								//variable created with assignment! must parse an expression -								Node *expr = _parse_and_reduce_expression(NULL, FunctionInfo()); -								if (!expr) +								Node *expr = _parse_and_reduce_expression(nullptr, FunctionInfo()); +								if (!expr) {  									return ERR_PARSE_ERROR; +								}  								if (expr->type == Node::TYPE_OPERATOR && ((OperatorNode *)expr)->op == OP_CALL) { -									_set_error("Expected constant expression after '='"); -									return ERR_PARSE_ERROR; +									OperatorNode *op = ((OperatorNode *)expr); +									for (int i = 1; i < op->arguments.size(); i++) { +										if (!_check_node_constness(op->arguments[i])) { +											_set_error("Expected constant expression for argument '" + itos(i - 1) + "' of function call after '='"); +											return ERR_PARSE_ERROR; +										} +									}  								}  								constant.initializer = static_cast<ConstantNode *>(expr); -								if (type != expr->get_datatype() || expr->get_datatype_name() != struct_name) { -									_set_error("Invalid assignment of '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' to '" + (is_struct ? String(struct_name) : get_datatype_name(type)) + "'"); +								if (!_compare_datatypes(type, struct_name, 0, expr->get_datatype(), expr->get_datatype_name(), 0)) {  									return ERR_PARSE_ERROR;  								}  							} @@ -6988,6 +7533,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  						shader->constants[name] = constant;  						shader->vconstants.push_back(constant); +#ifdef DEBUG_ENABLED +						if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_CONSTANT_FLAG)) { +							used_constants.insert(name, Usage(tk_line)); +						} +#endif // DEBUG_ENABLED  						if (tk.type == TK_COMMA) {  							tk = _get_token(); @@ -7046,9 +7596,16 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  				func_node->return_type = type;  				func_node->return_struct_name = struct_name;  				func_node->return_precision = precision; +				func_node->return_array_size = return_array_size;  				if (p_functions.has(name)) {  					func_node->can_discard = p_functions[name].can_discard; +				} else { +#ifdef DEBUG_ENABLED +					if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG)) { +						used_functions.insert(name, Usage(tk_line)); +					} +#endif // DEBUG_ENABLED  				}  				func_node->body = alloc_node<BlockNode>(); @@ -7093,6 +7650,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  					StringName param_struct_name;  					DataPrecision pprecision = PRECISION_DEFAULT;  					bool use_precision = false; +					int array_size = 0;  					if (is_token_precision(tk.type)) {  						pprecision = get_token_precision(tk.type); @@ -7139,8 +7697,29 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  					tk = _get_token();  					if (tk.type == TK_BRACKET_OPEN) { -						_set_error("Arrays as parameters are not implemented yet"); -						return ERR_PARSE_ERROR; +						bool error = false; +						tk = _get_token(); + +						if (tk.type == TK_INT_CONSTANT) { +							array_size = (int)tk.constant; + +							if (array_size > 0) { +								tk = _get_token(); +								if (tk.type != TK_BRACKET_CLOSE) { +									_set_error("Expected ']'"); +									return ERR_PARSE_ERROR; +								} +							} else { +								error = true; +							} +						} else { +							error = true; +						} +						if (error) { +							_set_error("Expected integer constant > 0"); +							return ERR_PARSE_ERROR; +						} +						tk = _get_token();  					}  					if (tk.type != TK_IDENTIFIER) {  						_set_error("Expected identifier for argument name"); @@ -7174,14 +7753,41 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  					arg.tex_argument_repeat = REPEAT_DEFAULT;  					arg.is_const = is_const; -					func_node->arguments.push_back(arg); -  					tk = _get_token();  					if (tk.type == TK_BRACKET_OPEN) { -						_set_error("Arrays as parameters are not implemented yet"); -						return ERR_PARSE_ERROR; +						if (array_size > 0) { +							_set_error("Array size is already defined!"); +							return ERR_PARSE_ERROR; +						} +						bool error = false; +						tk = _get_token(); + +						if (tk.type == TK_INT_CONSTANT) { +							array_size = (int)tk.constant; + +							if (array_size > 0) { +								tk = _get_token(); +								if (tk.type != TK_BRACKET_CLOSE) { +									_set_error("Expected ']'"); +									return ERR_PARSE_ERROR; +								} +							} else { +								error = true; +							} +						} else { +							error = true; +						} + +						if (error) { +							_set_error("Expected integer constant > 0"); +							return ERR_PARSE_ERROR; +						} +						tk = _get_token();  					} +					arg.array_size = array_size; +					func_node->arguments.push_back(arg); +  					if (tk.type == TK_COMMA) {  						tk = _get_token();  						//do none and go on @@ -7243,26 +7849,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct  }  bool ShaderLanguage::has_builtin(const Map<StringName, ShaderLanguage::FunctionInfo> &p_functions, const StringName &p_name) { -	if (p_functions.has("vertex")) { -		if (p_functions["vertex"].built_ins.has(p_name)) { -			return true; -		} -	} -	if (p_functions.has("fragment")) { -		if (p_functions["fragment"].built_ins.has(p_name)) { -			return true; -		} -	} -	if (p_functions.has("light")) { -		if (p_functions["light"].built_ins.has(p_name)) { -			return true; -		} -	} -	if (p_functions.has("compute")) { -		if (p_functions["compute"].built_ins.has(p_name)) { +	for (Map<StringName, ShaderLanguage::FunctionInfo>::Element *E = p_functions.front(); E; E = E->next()) { +		if (E->get().built_ins.has(p_name)) {  			return true;  		}  	} +  	return false;  } @@ -7396,27 +7988,62 @@ String ShaderLanguage::get_shader_type(const String &p_code) {  	return String();  } -Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) { +#ifdef DEBUG_ENABLED +void ShaderLanguage::_check_warning_accums() { +	for (Map<ShaderWarning::Code, Map<StringName, Usage> *>::Element *E = warnings_check_map.front(); E; E = E->next()) { +		for (const Map<StringName, Usage>::Element *U = (*E->get()).front(); U; U = U->next()) { +			if (!U->get().used) { +				_add_warning(E->key(), U->get().decl_line, U->key()); +			} +		} +	} +} +List<ShaderWarning>::Element *ShaderLanguage::get_warnings_ptr() { +	return warnings.front(); +} +void ShaderLanguage::enable_warning_checking(bool p_enabled) { +	check_warnings = p_enabled; +} +bool ShaderLanguage::is_warning_checking_enabled() const { +	return check_warnings; +} +void ShaderLanguage::set_warning_flags(uint32_t p_flags) { +	warning_flags = p_flags; +} +uint32_t ShaderLanguage::get_warning_flags() const { +	return warning_flags; +} +#endif // DEBUG_ENABLED + +Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) {  	clear();  	code = p_code;  	global_var_get_type_func = p_global_variable_type_func; +	varying_function_names = p_varying_function_names;  	nodes = nullptr;  	shader = alloc_node<ShaderNode>();  	Error err = _parse_shader(p_functions, p_render_modes, p_shader_types); +#ifdef DEBUG_ENABLED +	if (check_warnings) { +		_check_warning_accums(); +	} +#endif // DEBUG_ENABLED +  	if (err != OK) {  		return err;  	}  	return OK;  } -Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint) { +Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint) {  	clear();  	code = p_code; +	varying_function_names = p_varying_function_names;  	nodes = nullptr;  	global_var_get_type_func = p_global_variable_type_func; @@ -7583,6 +8210,13 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct  					String calltip;  					calltip += get_datatype_name(shader->functions[i].function->return_type); + +					if (shader->functions[i].function->return_array_size > 0) { +						calltip += "["; +						calltip += itos(shader->functions[i].function->return_array_size); +						calltip += "]"; +					} +  					calltip += " ";  					calltip += shader->functions[i].name;  					calltip += "("; @@ -7614,6 +8248,12 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct  						calltip += " ";  						calltip += shader->functions[i].function->arguments[j].name; +						if (shader->functions[i].function->arguments[j].array_size > 0) { +							calltip += "["; +							calltip += itos(shader->functions[i].function->arguments[j].array_size); +							calltip += "]"; +						} +  						if (j == completion_argument) {  							calltip += char32_t(0xFFFF);  						} @@ -7815,6 +8455,14 @@ ShaderLanguage::ShaderNode *ShaderLanguage::get_shader() {  ShaderLanguage::ShaderLanguage() {  	nodes = nullptr;  	completion_class = TAG_GLOBAL; + +#if DEBUG_ENABLED +	warnings_check_map.insert(ShaderWarning::UNUSED_CONSTANT, &used_constants); +	warnings_check_map.insert(ShaderWarning::UNUSED_FUNCTION, &used_functions); +	warnings_check_map.insert(ShaderWarning::UNUSED_STRUCT, &used_structs); +	warnings_check_map.insert(ShaderWarning::UNUSED_UNIFORM, &used_uniforms); +	warnings_check_map.insert(ShaderWarning::UNUSED_VARYING, &used_varyings); +#endif // DEBUG_ENABLED  }  ShaderLanguage::~ShaderLanguage() {  |