Browse Source

Merge pull request #20468 from vnen/typed-gds-fixes

General GDScript fixes
Rémi Verschelde 7 years ago
parent
commit
96d37769d9

+ 4 - 4
modules/gdscript/gdscript_compiler.cpp

@@ -140,7 +140,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
 		} break;
 		} break;
 		case GDScriptParser::DataType::CLASS: {
 		case GDScriptParser::DataType::CLASS: {
 			result.kind = GDScriptDataType::GDSCRIPT;
 			result.kind = GDScriptDataType::GDSCRIPT;
-			if (p_datatype.class_type->name == StringName()) {
+			if (!p_datatype.class_type->owner) {
 				result.script_type = Ref<GDScript>(main_script);
 				result.script_type = Ref<GDScript>(main_script);
 			} else {
 			} else {
 				result.script_type = class_map[p_datatype.class_type->name];
 				result.script_type = class_map[p_datatype.class_type->name];
@@ -482,7 +482,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
 
 
 					Variant script;
 					Variant script;
 					int idx = -1;
 					int idx = -1;
-					if (cn->cast_type.class_type->name == StringName()) {
+					if (!cn->cast_type.class_type->owner) {
 						script = codegen.script;
 						script = codegen.script;
 					} else {
 					} else {
 						StringName name = cn->cast_type.class_type->name;
 						StringName name = cn->cast_type.class_type->name;
@@ -1181,7 +1181,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
 
 
 									Variant script;
 									Variant script;
 									int idx = -1;
 									int idx = -1;
-									if (assign_type.class_type->name == StringName()) {
+									if (!assign_type.class_type->owner) {
 										script = codegen.script;
 										script = codegen.script;
 									} else {
 									} else {
 										StringName name = assign_type.class_type->name;
 										StringName name = assign_type.class_type->name;
@@ -1994,7 +1994,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
 		p_script->_signals[name] = p_class->_signals[i].arguments;
 		p_script->_signals[name] = p_class->_signals[i].arguments;
 	}
 	}
 
 
-	if (p_class->name != StringName()) {
+	if (!p_class->owner) {
 		parsed_classes.insert(p_class->name);
 		parsed_classes.insert(p_class->name);
 	}
 	}
 
 

+ 25 - 9
modules/gdscript/gdscript_function.cpp

@@ -742,13 +742,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 
 
 				GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
 				GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
 
 
+#ifdef DEBUG_ENABLED
 				if (src->get_type() != var_type) {
 				if (src->get_type() != var_type) {
-					err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
-							   "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
-					OPCODE_BREAK;
+					if (Variant::can_convert_strict(src->get_type(), var_type)) {
+						Variant::CallError ce;
+						*dst = Variant::construct(var_type, const_cast<const Variant **>(&src), 1, ce);
+					} else {
+						err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+								   "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
+						OPCODE_BREAK;
+					}
+				} else {
+#endif // DEBUG_ENABLED
+					*dst = *src;
+#ifdef DEBUG_ENABLED
 				}
 				}
-
-				*dst = *src;
+#endif // DEBUG_ENABLED
 
 
 				ip += 4;
 				ip += 4;
 			}
 			}
@@ -761,17 +770,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 				GET_VARIANT_PTR(dst, 2);
 				GET_VARIANT_PTR(dst, 2);
 				GET_VARIANT_PTR(src, 3);
 				GET_VARIANT_PTR(src, 3);
 
 
+#ifdef DEBUG_ENABLED
 				GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
 				GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
 				GD_ERR_BREAK(!nc);
 				GD_ERR_BREAK(!nc);
+				if (!src->get_type() != Variant::OBJECT && !src->get_type() != Variant::NIL) {
+					err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+							   "' to a variable of type '" + nc->get_name() + "'.";
+					OPCODE_BREAK;
+				}
 				Object *src_obj = src->operator Object *();
 				Object *src_obj = src->operator Object *();
-				GD_ERR_BREAK(!src_obj);
 
 
-				if (!ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+				if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
 					err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
 					err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
 							   "' to a variable of type '" + nc->get_name() + "'.";
 							   "' to a variable of type '" + nc->get_name() + "'.";
 					OPCODE_BREAK;
 					OPCODE_BREAK;
 				}
 				}
-
+#endif // DEBUG_ENABLED
 				*dst = *src;
 				*dst = *src;
 
 
 				ip += 4;
 				ip += 4;
@@ -785,6 +799,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 				GET_VARIANT_PTR(dst, 2);
 				GET_VARIANT_PTR(dst, 2);
 				GET_VARIANT_PTR(src, 3);
 				GET_VARIANT_PTR(src, 3);
 
 
+#ifdef DEBUG_ENABLED
 				Script *base_type = Object::cast_to<Script>(type->operator Object *());
 				Script *base_type = Object::cast_to<Script>(type->operator Object *());
 
 
 				GD_ERR_BREAK(!base_type);
 				GD_ERR_BREAK(!base_type);
@@ -820,6 +835,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 						OPCODE_BREAK;
 						OPCODE_BREAK;
 					}
 					}
 				}
 				}
+#endif // DEBUG_ENABLED
 
 
 				*dst = *src;
 				*dst = *src;
 
 
@@ -1579,7 +1595,7 @@ StringName GDScriptFunction::get_global_name(int p_idx) const {
 
 
 int GDScriptFunction::get_default_argument_count() const {
 int GDScriptFunction::get_default_argument_count() const {
 
 
-	return default_arguments.size();
+	return _default_arg_count;
 }
 }
 int GDScriptFunction::get_default_argument_addr(int p_idx) const {
 int GDScriptFunction::get_default_argument_addr(int p_idx) const {
 
 

+ 1 - 1
modules/gdscript/gdscript_functions.cpp

@@ -1412,7 +1412,7 @@ bool GDScriptFunctions::is_deterministic(Function p_func) {
 
 
 MethodInfo GDScriptFunctions::get_info(Function p_func) {
 MethodInfo GDScriptFunctions::get_info(Function p_func) {
 
 
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
 	//using a switch, so the compiler generates a jumptable
 	//using a switch, so the compiler generates a jumptable
 
 
 	switch (p_func) {
 	switch (p_func) {

+ 80 - 36
modules/gdscript/gdscript_parser.cpp

@@ -3332,6 +3332,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 
 
 		switch (token) {
 		switch (token) {
 
 
+			case GDScriptTokenizer::TK_CURSOR: {
+				tokenizer->advance();
+			} break;
 			case GDScriptTokenizer::TK_EOF:
 			case GDScriptTokenizer::TK_EOF:
 				p_class->end_line = tokenizer->get_token_line();
 				p_class->end_line = tokenizer->get_token_line();
 			case GDScriptTokenizer::TK_ERROR: {
 			case GDScriptTokenizer::TK_ERROR: {
@@ -3552,7 +3555,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 
 
 						DataType argtype;
 						DataType argtype;
 						if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
 						if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
-							if (!_parse_type(argtype)) {
+							if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
+								argtype.infer_type = true;
+								tokenizer->advance();
+							} else if (!_parse_type(argtype)) {
 								_set_error("Expected type for argument.");
 								_set_error("Expected type for argument.");
 								return;
 								return;
 							}
 							}
@@ -4187,6 +4193,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 								current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
 								current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
 
 
 								current_export.hint_string = native_class->get_name();
 								current_export.hint_string = native_class->get_name();
+								current_export.class_name = native_class->get_name();
 
 
 							} else {
 							} else {
 								current_export = PropertyInfo();
 								current_export = PropertyInfo();
@@ -4546,6 +4553,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 							member._export.type = Variant::OBJECT;
 							member._export.type = Variant::OBJECT;
 							member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
 							member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
 							member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
 							member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
+							member._export.hint_string = member.data_type.native_type;
 							member._export.class_name = member.data_type.native_type;
 							member._export.class_name = member.data_type.native_type;
 						} else {
 						} else {
 							_set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line);
 							_set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line);
@@ -5433,6 +5441,9 @@ GDScriptParser::DataType GDScriptParser::_get_operation_type(const Variant::Oper
 	if (b_type == Variant::INT || b_type == Variant::REAL) {
 	if (b_type == Variant::INT || b_type == Variant::REAL) {
 		Variant::evaluate(Variant::OP_ADD, b, 1, b, r_valid);
 		Variant::evaluate(Variant::OP_ADD, b, 1, b, r_valid);
 	}
 	}
+	if (a_type == Variant::STRING) {
+		a = "%s"; // Work around for formatting operator (%)
+	}
 
 
 	Variant ret;
 	Variant ret;
 	Variant::evaluate(p_op, a, b, ret, r_valid);
 	Variant::evaluate(p_op, a, b, ret, r_valid);
@@ -6770,7 +6781,26 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType
 	// Check classes in current file
 	// Check classes in current file
 	ClassNode *base = NULL;
 	ClassNode *base = NULL;
 	if (!p_base_type) {
 	if (!p_base_type) {
-		// Possibly this is a global, check first
+		base = current_class;
+		base_type.has_type = true;
+		base_type.is_constant = true;
+		base_type.kind = DataType::CLASS;
+		base_type.class_type = base;
+	} else {
+		base_type = DataType(*p_base_type);
+		if (base_type.kind == DataType::CLASS) {
+			base = base_type.class_type;
+		}
+	}
+
+	DataType member_type;
+
+	if (_get_member_type(base_type, p_identifier, member_type)) {
+		return member_type;
+	}
+
+	if (!p_base_type) {
+		// Possibly this is a global, check before failing
 
 
 		if (ClassDB::class_exists(p_identifier) || ClassDB::class_exists("_" + p_identifier.operator String())) {
 		if (ClassDB::class_exists(p_identifier) || ClassDB::class_exists("_" + p_identifier.operator String())) {
 			DataType result;
 			DataType result;
@@ -6796,6 +6826,9 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType
 				result.class_type = outer_class;
 				result.class_type = outer_class;
 				return result;
 				return result;
 			}
 			}
+			if (outer_class->constant_expressions.has(p_identifier)) {
+				return outer_class->constant_expressions[p_identifier].type;
+			}
 			for (int i = 0; i < outer_class->subclasses.size(); i++) {
 			for (int i = 0; i < outer_class->subclasses.size(); i++) {
 				if (outer_class->subclasses[i] == current_class) {
 				if (outer_class->subclasses[i] == current_class) {
 					continue;
 					continue;
@@ -6885,27 +6918,6 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType
 			}
 			}
 		}
 		}
 
 
-		// Nothing found, keep looking in local scope
-
-		base = current_class;
-		base_type.has_type = true;
-		base_type.is_constant = true;
-		base_type.kind = DataType::CLASS;
-		base_type.class_type = base;
-	} else {
-		base_type = *p_base_type;
-		if (base_type.kind == DataType::CLASS) {
-			base = base_type.class_type;
-		}
-	}
-
-	DataType member_type;
-
-	if (_get_member_type(base_type, p_identifier, member_type)) {
-		return member_type;
-	}
-
-	if (!p_base_type) {
 		// This means looking in the current class, which type is always known
 		// This means looking in the current class, which type is always known
 		_set_error("Identifier '" + p_identifier.operator String() + "' is not declared in the current scope.", p_line);
 		_set_error("Identifier '" + p_identifier.operator String() + "' is not declared in the current scope.", p_line);
 	}
 	}
@@ -7131,11 +7143,9 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
 	// Arguments
 	// Arguments
 	int defaults_ofs = p_function->arguments.size() - p_function->default_values.size();
 	int defaults_ofs = p_function->arguments.size() - p_function->default_values.size();
 	for (int i = 0; i < p_function->arguments.size(); i++) {
 	for (int i = 0; i < p_function->arguments.size(); i++) {
-
-		// Resolve types
-		p_function->argument_types.write[i] = _resolve_type(p_function->argument_types[i], p_function->line);
-
-		if (i >= defaults_ofs) {
+		if (i < defaults_ofs) {
+			p_function->argument_types.write[i] = _resolve_type(p_function->argument_types[i], p_function->line);
+		} else {
 			if (p_function->default_values[i - defaults_ofs]->type != Node::TYPE_OPERATOR) {
 			if (p_function->default_values[i - defaults_ofs]->type != Node::TYPE_OPERATOR) {
 				_set_error("Parser bug: invalid argument default value.", p_function->line, p_function->column);
 				_set_error("Parser bug: invalid argument default value.", p_function->line, p_function->column);
 				return;
 				return;
@@ -7150,17 +7160,25 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
 
 
 			DataType def_type = _reduce_node_type(op->arguments[1]);
 			DataType def_type = _reduce_node_type(op->arguments[1]);
 
 
-			if (!_is_type_compatible(p_function->argument_types[i], def_type, true)) {
-				String arg_name = p_function->arguments[i];
-				_set_error("Value type (" + def_type.to_string() + ") doesn't match the type of argument '" +
-								   arg_name + "' (" + p_function->arguments[i] + ")",
-						p_function->line);
+			if (p_function->argument_types[i].infer_type) {
+				def_type.is_constant = false;
+				p_function->argument_types.write[i] = def_type;
+			} else {
+				p_function->return_type = _resolve_type(p_function->return_type, p_function->line);
+
+				if (!_is_type_compatible(p_function->argument_types[i], def_type, true)) {
+					String arg_name = p_function->arguments[i];
+					_set_error("Value type (" + def_type.to_string() + ") doesn't match the type of argument '" +
+									   arg_name + "' (" + p_function->arguments[i] + ")",
+							p_function->line);
+				}
 			}
 			}
 		}
 		}
 	}
 	}
 
 
 	if (!(p_function->name == "_init")) {
 	if (!(p_function->name == "_init")) {
 		// Signature for the initializer may vary
 		// Signature for the initializer may vary
+#ifdef DEBUG_ENABLED
 		DataType return_type;
 		DataType return_type;
 		List<DataType> arg_types;
 		List<DataType> arg_types;
 		int default_arg_count = 0;
 		int default_arg_count = 0;
@@ -7171,18 +7189,44 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
 		if (_get_function_signature(base_type, p_function->name, return_type, arg_types, default_arg_count, _static, vararg)) {
 		if (_get_function_signature(base_type, p_function->name, return_type, arg_types, default_arg_count, _static, vararg)) {
 			bool valid = _static == p_function->_static;
 			bool valid = _static == p_function->_static;
 			valid = valid && return_type == p_function->return_type;
 			valid = valid && return_type == p_function->return_type;
-			valid = valid && p_function->default_values.size() >= default_arg_count;
-			valid = valid && arg_types.size() == p_function->arguments.size();
+			int argsize_diff = p_function->arguments.size() - arg_types.size();
+			valid = valid && argsize_diff >= 0;
+			valid = valid && p_function->default_values.size() >= default_arg_count + argsize_diff;
 			int i = 0;
 			int i = 0;
 			for (List<DataType>::Element *E = arg_types.front(); valid && E; E = E->next()) {
 			for (List<DataType>::Element *E = arg_types.front(); valid && E; E = E->next()) {
 				valid = valid && E->get() == p_function->argument_types[i++];
 				valid = valid && E->get() == p_function->argument_types[i++];
 			}
 			}
 
 
 			if (!valid) {
 			if (!valid) {
-				_set_error("Function signature doesn't match the parent.", p_function->line);
+				String parent_signature = return_type.has_type ? return_type.to_string() : "Variant";
+				if (parent_signature == "null") {
+					parent_signature = "void";
+				}
+				parent_signature += " " + p_function->name + "(";
+				if (arg_types.size()) {
+					int i = 0;
+					for (List<DataType>::Element *E = arg_types.front(); E; E = E->next()) {
+						if (E != arg_types.front()) {
+							parent_signature += ", ";
+						}
+						String arg = E->get().to_string();
+						if (arg == "null" || arg == "var") {
+							arg = "Variant";
+						}
+						parent_signature += arg;
+						if (i == arg_types.size() - default_arg_count) {
+							parent_signature += "=default";
+						}
+
+						i++;
+					}
+				}
+				parent_signature += ")";
+				_set_error("Function signature doesn't match the parent. Parent signature is: '" + parent_signature + "'.", p_function->line);
 				return;
 				return;
 			}
 			}
 		}
 		}
+#endif // DEBUG_ENABLED
 	} else {
 	} else {
 		if (p_function->return_type.has_type && (p_function->return_type.kind != DataType::BUILTIN || p_function->return_type.builtin_type != Variant::NIL)) {
 		if (p_function->return_type.has_type && (p_function->return_type.kind != DataType::BUILTIN || p_function->return_type.builtin_type != Variant::NIL)) {
 			_set_error("Constructor cannot return a value.", p_function->line);
 			_set_error("Constructor cannot return a value.", p_function->line);