浏览代码

Add ability to infer variable type from assigned value

Syntax: var x : = 42
Infers the type of "x" to be an integer.
George Marques 7 年之前
父节点
当前提交
3445dca01d
共有 2 个文件被更改,包括 48 次插入3 次删除
  1. 46 3
      modules/gdscript/gdscript_parser.cpp
  2. 2 0
      modules/gdscript/gdscript_parser.h

+ 46 - 3
modules/gdscript/gdscript_parser.cpp

@@ -2684,7 +2684,13 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
 				Node *assigned = NULL;
 				Node *assigned = NULL;
 
 
 				if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
 				if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
-					if (!_parse_type(lv->datatype)) {
+					if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
+						lv->datatype = DataType();
+#ifdef DEBUG_ENABLED
+						lv->datatype.infer_type = true;
+#endif
+						tokenizer->advance();
+					} else if (!_parse_type(lv->datatype)) {
 						_set_error("Expected type for variable.");
 						_set_error("Expected type for variable.");
 						return;
 						return;
 					}
 					}
@@ -4414,7 +4420,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 				rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
 				rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
 
 
 				if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
 				if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
-					if (!_parse_type(member.data_type)) {
+					if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
+						member.data_type = DataType();
+#ifdef DEBUG_ENABLED
+						member.data_type.infer_type = true;
+#endif
+						tokenizer->advance();
+					} else if (!_parse_type(member.data_type)) {
 						_set_error("Expected type for class variable.");
 						_set_error("Expected type for class variable.");
 						return;
 						return;
 					}
 					}
@@ -4606,7 +4618,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 				tokenizer->advance();
 				tokenizer->advance();
 
 
 				if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
 				if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
-					if (!_parse_type(constant.type)) {
+					if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
+						constant.type = DataType();
+#ifdef DEBUG_ENABLED
+						constant.type.infer_type = true;
+#endif
+						tokenizer->advance();
+					} else if (!_parse_type(constant.type)) {
 						_set_error("Expected type for class constant.");
 						_set_error("Expected type for class constant.");
 						return;
 						return;
 					}
 					}
@@ -6464,14 +6482,17 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
 			}
 			}
 
 
 			if (!valid) {
 			if (!valid) {
+#ifdef DEBUG_ENABLED
 				if (p_call->arguments[0]->type == Node::TYPE_SELF) {
 				if (p_call->arguments[0]->type == Node::TYPE_SELF) {
 					_set_error("Method '" + callee_name + "' is not declared in the current class.", p_call->line);
 					_set_error("Method '" + callee_name + "' is not declared in the current class.", p_call->line);
 					return DataType();
 					return DataType();
 				}
 				}
 				_mark_line_as_unsafe(p_call->line);
 				_mark_line_as_unsafe(p_call->line);
+#endif
 				return DataType();
 				return DataType();
 			}
 			}
 
 
+#ifdef DEBUG_ENABLED
 			if (current_function && !for_completion && !is_static && p_call->arguments[0]->type == Node::TYPE_SELF && current_function->_static) {
 			if (current_function && !for_completion && !is_static && p_call->arguments[0]->type == Node::TYPE_SELF && current_function->_static) {
 				if (current_function && current_function->_static && p_call->arguments[0]->type == Node::TYPE_SELF) {
 				if (current_function && current_function->_static && p_call->arguments[0]->type == Node::TYPE_SELF) {
 					_set_error("Can't call non-static function from a static function.", p_call->line);
 					_set_error("Can't call non-static function from a static function.", p_call->line);
@@ -6483,6 +6504,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
 				_set_error("Non-static function '" + String(callee_name) + "' can only be called from an instance.", p_call->line);
 				_set_error("Non-static function '" + String(callee_name) + "' can only be called from an instance.", p_call->line);
 				return DataType();
 				return DataType();
 			}
 			}
+#endif
 		} break;
 		} break;
 	}
 	}
 
 
@@ -6967,6 +6989,15 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
 					v.initial_assignment->arguments[1] = convert_call;
 					v.initial_assignment->arguments[1] = convert_call;
 				}
 				}
 			}
 			}
+
+			if (v.data_type.infer_type) {
+				if (!expr_type.has_type) {
+					_set_error("Assigned value does not have a set type, variable type cannot be inferred.", v.line);
+					return;
+				}
+				v.data_type = expr_type;
+				v.data_type.is_constant = false;
+			}
 		} else if (v.data_type.has_type && v.data_type.kind == DataType::BUILTIN) {
 		} else if (v.data_type.has_type && v.data_type.kind == DataType::BUILTIN) {
 			// Create default value based on the type
 			// Create default value based on the type
 			IdentifierNode *id = alloc_node<IdentifierNode>();
 			IdentifierNode *id = alloc_node<IdentifierNode>();
@@ -7249,6 +7280,14 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
 							lv->assign_op->arguments[1] = convert_call;
 							lv->assign_op->arguments[1] = convert_call;
 						}
 						}
 					}
 					}
+					if (lv->datatype.infer_type) {
+						if (!assign_type.has_type) {
+							_set_error("Assigned value does not have a set type, variable type cannot be inferred.", lv->line);
+							return;
+						}
+						lv->datatype = assign_type;
+						lv->datatype.is_constant = false;
+					}
 					if (lv->datatype.has_type && !assign_type.has_type) {
 					if (lv->datatype.has_type && !assign_type.has_type) {
 						_mark_line_as_unsafe(lv->line);
 						_mark_line_as_unsafe(lv->line);
 					}
 					}
@@ -7514,7 +7553,11 @@ Error GDScriptParser::_parse(const String &p_base_path) {
 	current_class = main_class;
 	current_class = main_class;
 	current_function = NULL;
 	current_function = NULL;
 	current_block = NULL;
 	current_block = NULL;
+#ifdef DEBUG_ENABLED
 	if (for_completion) check_types = false;
 	if (for_completion) check_types = false;
+#else
+	check_types = false;
+#endif
 
 
 	if (check_types) {
 	if (check_types) {
 		// Resolve all class-level stuff before getting into function blocks
 		// Resolve all class-level stuff before getting into function blocks

+ 2 - 0
modules/gdscript/gdscript_parser.h

@@ -56,6 +56,7 @@ public:
 		bool has_type;
 		bool has_type;
 		bool is_constant;
 		bool is_constant;
 		bool is_meta_type; // Whether the value can be used as a type
 		bool is_meta_type; // Whether the value can be used as a type
+		bool infer_type;
 
 
 		Variant::Type builtin_type;
 		Variant::Type builtin_type;
 		StringName native_type;
 		StringName native_type;
@@ -93,6 +94,7 @@ public:
 				has_type(false),
 				has_type(false),
 				is_constant(false),
 				is_constant(false),
 				is_meta_type(false),
 				is_meta_type(false),
+				infer_type(false),
 				builtin_type(Variant::NIL),
 				builtin_type(Variant::NIL),
 				class_type(NULL) {}
 				class_type(NULL) {}
 	};
 	};