See More

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace LCompilers::LPython { int save_pyc_files(const ASR::TranslationUnit_t &u, std::string infile) { diag::Diagnostics diagnostics; LCOMPILERS_ASSERT(asr_verify(u, true, diagnostics)); std::string modfile_binary = save_pycfile(u); while( infile.back() != '.' ) { infile.pop_back(); } std::string modfile = infile + "pyc"; { std::ofstream out; out.open(modfile, std::ofstream::out | std::ofstream::binary); out << modfile_binary; } return 0; } // Does a CPython style lookup for a module: // * First the current directory (this is incorrect, we need to do it relative to the current file) // * Then the LPython runtime directory Result<:string> get_full_path(const std::string &filename, const std::string &runtime_library_dir, std::string& input, bool &lpython, bool& enum_py) { lpython = false; enum_py = false; std::string file_path; if( *(runtime_library_dir.rbegin()) == '/' ) { file_path = runtime_library_dir + filename; } else { file_path = runtime_library_dir + "/" + filename; } bool status = read_file(file_path, input); if (status) { return file_path; } else { // If this is `lpython`, do a special lookup if (filename == "lpython.py") { file_path = runtime_library_dir + "/lpython/" + filename; status = read_file(file_path, input); if (status) { lpython = true; return file_path; } else { return Error(); } } else if (startswith(filename, "numpy.py")) { file_path = runtime_library_dir + "/lpython_intrinsic_" + filename; status = read_file(file_path, input); if (status) { return file_path; } else { return Error(); } } else if (startswith(filename, "enum.py")) { enum_py = true; return Error(); } else { return Error(); } } } bool set_module_path(std::string infile0, std::vector<:string> &rl_path, std::string& infile, std::string& path_used, std::string& input, bool& lpython, bool& enum_py) { for (auto path: rl_path) { Result<:string> rinfile = get_full_path(infile0, path, input, lpython, enum_py); if (rinfile.ok) { infile = rinfile.result; path_used = path; return true; } } return false; } ASR::TranslationUnit_t* compile_module_till_asr( Allocator& al, SymbolTable* symtab, std::string module_name, std::vector<:string> &rl_path, std::string infile, const Location &loc, diag::Diagnostics &diagnostics, LocationManager &lm, const std::function err, bool allow_implicit_casting) { { LocationManager::FileLocations fl; fl.in_filename = infile; lm.files.push_back(fl); std::string input = read_file(infile); lm.file_ends.push_back(lm.file_ends.back() + input.size()); lm.init_simple(input); } Result<:ast_t> r = parse_python_file(al, rl_path[0], infile, diagnostics, lm.file_ends.end()[-2], false); if (!r.ok) { err("The file '" + infile + "' failed to parse", loc); } LCompilers::LPython::AST::ast_t* ast = r.result; // Convert the module from AST to ASR CompilerOptions compiler_options; compiler_options.disable_main = true; compiler_options.symtab_only = false; Result<:translationunit_t> r2 = python_ast_to_asr(al, lm, symtab, *ast, diagnostics, compiler_options, false, module_name, infile, allow_implicit_casting); // TODO: Uncomment once a check is added for ensuring // that module.py file hasn't changed between // builds. // save_pyc_files(*r2.result, infile + "c"); if (!r2.ok) { LCOMPILERS_ASSERT(diagnostics.has_error()) return nullptr; // Error } return r2.result; } void fill_module_dependencies(SymbolTable* symtab, SetChar& mod_deps, Allocator& al) { if( symtab == nullptr ) { return ; } for( auto& itr: symtab->get_scope() ) { if( ASR::is_a<:externalsymbol_t>(*itr.second) ) { ASR::ExternalSymbol_t* ext_sym = ASR::down_cast<:externalsymbol_t>(itr.second); mod_deps.push_back(al, ext_sym->m_module_name); } else { SymbolTable* sym_symtab = ASRUtils::symbol_symtab(itr.second); fill_module_dependencies(sym_symtab, mod_deps, al); } } } ASR::Module_t* load_module(Allocator &al, SymbolTable *symtab, const std::string &module_name, const Location &loc, diag::Diagnostics &diagnostics, LocationManager &lm, bool intrinsic, std::vector<:string> &rl_path, bool &lpython, bool& enum_py, bool& copy, bool& sympy, const std::function err, bool allow_implicit_casting) { lpython = false; enum_py = false; copy = false; sympy = false; if( module_name == "copy" ) { copy = true; return nullptr; } if (module_name == "sympy") { sympy = true; return nullptr; } LCOMPILERS_ASSERT(symtab); if (symtab->get_scope().find(module_name) != symtab->get_scope().end()) { ASR::symbol_t *m = symtab->get_symbol(module_name); if (ASR::is_a<:module_t>(*m)) { return ASR::down_cast<:module_t>(m); } else { err("The symbol '" + module_name + "' is not a module", loc); } } LCOMPILERS_ASSERT(symtab->parent == nullptr); // Parse the module `module_name`.py to AST std::string infile0 = module_name + ".py"; std::string infile0c = infile0 + "c"; std::string path_used = "", infile; bool compile_module = true; ASR::TranslationUnit_t* mod1 = nullptr; std::string input; std::string mod1_name = ""; bool found = set_module_path(infile0c, rl_path, infile, path_used, input, lpython, enum_py); if( !found ) { input.clear(); found = set_module_path(infile0, rl_path, infile, path_used, input, lpython, enum_py); } else { mod1 = load_pycfile(al, input, false); fix_external_symbols(*mod1, *ASRUtils::get_tu_symtab(symtab)); diag::Diagnostics diagnostics; LCOMPILERS_ASSERT(asr_verify(*mod1, true, diagnostics)); compile_module = false; } if( enum_py ) { return nullptr; } if (!found) { err("Could not find the module '" + module_name + "'. If an import path " "is available, please use the `-I` option to specify it", loc); } if (lpython) return nullptr; if( compile_module ) { diagnostics.add(diag::Diagnostic( "The module '" + module_name + "' located in " + infile +" cannot be loaded", diag::Level::Warning, diag::Stage::Semantic, { diag::Label("imported here", {loc}) }) ); if (module_name == "__init__") { std::string module_dir_name = infile.substr(0, infile.find_last_of('/')); // assign module directory name mod1_name = module_dir_name.substr(module_dir_name.find_last_of('/') + 1); } else { mod1_name = module_name; } mod1 = compile_module_till_asr(al, symtab, mod1_name, rl_path, infile, loc, diagnostics, lm, err, allow_implicit_casting); if (mod1 == nullptr) { throw SemanticAbort(); } else { diagnostics.diagnostics.pop_back(); } } ASR::symbol_t* mod1_sym = symtab->resolve_symbol(mod1_name); if (!mod1_sym) { throw SemanticError("Module `" + module_name + "` not found", loc); } ASR::Module_t* mod1_mod = ASR::down_cast<:module_t>(mod1_sym); if (intrinsic) { mod1_mod->m_intrinsic = true; // TODO: I think we should just store intrinsic once, in the module // itself // Mark each function as intrinsic also for (auto &item : mod1_mod->m_symtab->get_scope()) { if (ASR::is_a<:function_t>(*item.second)) { ASR::Function_t *s = ASR::down_cast<:function_t>(item.second); if (ASRUtils::get_FunctionType(s)->m_abi == ASR::abiType::Source) { ASRUtils::get_FunctionType(s)->m_abi = ASR::abiType::Intrinsic; } if (s->n_body == 0) { std::string name = s->m_name; if (name == "ubound" || name == "lbound") { ASRUtils::get_FunctionType(s)->m_deftype = ASR::deftypeType::Interface; } } } } } // and return it return mod1_mod; } // Here, we call the global_initializer & global_statements to // initialize and execute the global symbols void get_calls_to_global_init_and_stmts(Allocator &al, const Location &loc, SymbolTable* scope, ASR::Module_t* mod, std::vector<:asr_t> &tmp_vec) { std::string mod_name = mod->m_name; std::string g_func_name = mod_name + "global_init"; ASR::symbol_t *g_func = mod->m_symtab->get_symbol(g_func_name); if (g_func && !scope->get_symbol(g_func_name)) { ASR::symbol_t *es = ASR::down_cast<:symbol_t>( ASR::make_ExternalSymbol_t(al, mod->base.base.loc, scope, s2c(al, g_func_name), g_func, s2c(al, mod_name), nullptr, 0, s2c(al, g_func_name), ASR::accessType::Public)); scope->add_symbol(g_func_name, es); tmp_vec.push_back(ASRUtils::make_SubroutineCall_t_util(al, loc, es, g_func, nullptr, 0, nullptr, nullptr, false)); } g_func_name = mod_name + "global_stmts"; g_func = mod->m_symtab->get_symbol(g_func_name); if (g_func && !scope->get_symbol(g_func_name)) { ASR::symbol_t *es = ASR::down_cast<:symbol_t>( ASR::make_ExternalSymbol_t(al, mod->base.base.loc, scope, s2c(al, g_func_name), g_func, s2c(al, mod_name), nullptr, 0, s2c(al, g_func_name), ASR::accessType::Public)); scope->add_symbol(g_func_name, es); tmp_vec.push_back(ASRUtils::make_SubroutineCall_t_util(al, loc, es, g_func, nullptr, 0, nullptr, nullptr, false)); } } template class CommonVisitor : public AST::BaseVisitor { public: diag::Diagnostics &diag; ASR::asr_t *tmp; /* If `tmp` is not null, then `tmp_vec` is ignored and `tmp` is used as the only result (statement or expression). If `tmp` is null, then `tmp_vec` is used to return any number of statements: 0 (no statement returned), 1 (redundant, one should use `tmp` for that), 2, 3, ... etc. */ std::vector<:asr_t> tmp_vec; // Used to store the initializer for the global variables like list, ... Vec<:asr_t> global_init; Allocator &al; LocationManager &lm; SymbolTable *current_scope; SetChar current_module_dependencies; // True for the main module, false for every other one // The main module is stored directly in TranslationUnit, other modules are Modules bool main_module; // This is the current module name that we are compiling from AST to ASR // It is an empty string for main_module std::string module_name; PythonIntrinsicProcedures intrinsic_procedures; ProceduresDatabase procedures_db; AttributeHandler attr_handler; IntrinsicNodeHandler intrinsic_node_handler; std::map &ast_overload; std::string parent_dir; std::vector<:string> import_paths; /* current_body exists only for Functions, For, If (& its Else part), While. current_body does not exist for Modules, ClassDef/Structs. */ Vec<:stmt_t> *current_body; ASR::expr_t* assign_asr_target; std::map<:string int> generic_func_nums; std::map<:string std::map asr::ttype_t>> generic_func_subs; std::vector<:symbol_t> rt_vec; std::map<:string std::string> context_map; SetChar dependencies; bool allow_implicit_casting; // Stores the name of imported functions and the modules they are imported from std::map<:string std::string> imported_functions; std::map<:string std::string> numpy2lpythontypes = { {"bool", "bool"}, {"bool_", "bool"}, {"int8", "i8"}, {"int16", "i16"}, {"int32", "i32"}, {"int64", "i64"}, {"uint8", "u8"}, {"uint16", "u16"}, {"uint32", "u32"}, {"uint64", "u64"}, {"float32", "f32"}, {"float64", "f64"}, {"float_", "f64"}, {"complex64", "c32"}, {"complex128", "c64"}, {"complex_", "c64"}, {"object", "T"} }; CommonVisitor(Allocator &al, LocationManager &lm, SymbolTable *symbol_table, diag::Diagnostics &diagnostics, bool main_module, std::string module_name, std::map &ast_overload, std::string parent_dir, std::vector<:string> import_paths, bool allow_implicit_casting_) : diag{diagnostics}, al{al}, lm{lm}, current_scope{symbol_table}, main_module{main_module}, module_name{module_name}, ast_overload{ast_overload}, parent_dir{parent_dir}, import_paths{import_paths}, current_body{nullptr}, assign_asr_target{nullptr}, allow_implicit_casting{allow_implicit_casting_} { current_module_dependencies.reserve(al, 4); global_init.reserve(al, 1); } ASR::asr_t* resolve_variable(const Location &loc, const std::string &var_name) { SymbolTable *scope = current_scope; ASR::symbol_t *v = scope->resolve_symbol(var_name); if (!v) { diag.semantic_error_label("Variable '" + var_name + "' is not declared", {loc}, "'" + var_name + "' is undeclared"); throw SemanticAbort(); } return ASR::make_Var_t(al, loc, v); } ASR::symbol_t* resolve_intrinsic_function(const Location &loc, const std::string &remote_sym) { LCOMPILERS_ASSERT(intrinsic_procedures.is_intrinsic(remote_sym)); std::string module_name = intrinsic_procedures.get_module(remote_sym, loc); SymbolTable *tu_symtab = ASRUtils::get_tu_symtab(current_scope); std::string rl_path = get_runtime_library_dir(); std::vector<:string> paths = {rl_path, parent_dir}; bool lpython, enum_py, copy, sympy; ASR::Module_t *m = load_module(al, tu_symtab, module_name, loc, diag, lm, true, paths, lpython, enum_py, copy, sympy, [&](const std::string &msg, const Location &loc) { throw SemanticError(msg, loc); }, allow_implicit_casting); LCOMPILERS_ASSERT(!lpython && !enum_py) ASR::symbol_t *t = m->m_symtab->resolve_symbol(remote_sym); if (!t) { throw SemanticError("The symbol '" + remote_sym + "' not found in the module '" + module_name + "'", loc); } else if (! (ASR::is_a<:genericprocedure_t>(*t) || ASR::is_a<:function_t>(*t) )) { throw SemanticError("The symbol '" + remote_sym + "' found in the module '" + module_name + "', " + "but it is not a function, subroutine or a generic procedure.", loc); } char *fn_name = ASRUtils::symbol_name(t); std::string sym = fn_name; ASR::symbol_t *v = nullptr; if( current_scope->get_symbol(sym) == nullptr ) { ASR::asr_t *fn = ASR::make_ExternalSymbol_t( al, t->base.loc, /* a_symtab */ current_scope, /* a_name */ fn_name, t, m->m_name, nullptr, 0, fn_name, ASR::accessType::Private ); current_scope->add_symbol(sym, ASR::down_cast<:symbol_t>(fn)); v = ASR::down_cast<:symbol_t>(fn); } else { v = current_scope->get_symbol(sym); } current_module_dependencies.push_back(al, m->m_name); return v; } void handle_builtin_attribute(ASR::expr_t *s, std::string attr_name, const Location &loc, Vec<:expr_t> &args) { tmp = attr_handler.get_attribute(s, attr_name, al, loc, args, diag); return; } void handle_symbolic_attribute(ASR::expr_t *s, std::string attr_name, const Location &loc, Vec<:expr_t> &args) { tmp = attr_handler.get_symbolic_attribute(s, attr_name, al, loc, args, diag); return; } void fill_expr_in_ttype_t(std::vector<:expr_t>& exprs, ASR::dimension_t* dims, size_t n_dims) { for( size_t i = 0; i < n_dims; i++ ) { exprs.push_back(dims[i].m_start); exprs.push_back(dims[i].m_length); } } void fix_exprs_ttype_t(std::vector<:expr_t>& exprs, Vec<:call_arg_t>& orig_args, ASR::Function_t* orig_func=nullptr) { ASRUtils::ExprStmtDuplicator expr_duplicator(al); expr_duplicator.allow_procedure_calls = true; ASRUtils::ReplaceArgVisitor arg_replacer(al, current_scope, orig_func, orig_args, dependencies, current_module_dependencies); for( size_t i = 0; i < exprs.size(); i++ ) { ASR::expr_t* expri = exprs[i]; if (expri) { expr_duplicator.success = true; ASR::expr_t* expri_copy = expr_duplicator.duplicate_expr(expri); LCOMPILERS_ASSERT(expr_duplicator.success); arg_replacer.current_expr = &expri_copy; arg_replacer.replace_expr(expri_copy); exprs[i] = expri_copy; } } } void fill_new_dims(ASR::Array_t* t, const std::vector<:expr_t>& func_calls, Vec<:dimension_t>& new_dims) { new_dims.reserve(al, t->n_dims); for( size_t i = 0, j = 0; i < func_calls.size(); i += 2, j++ ) { ASR::dimension_t new_dim; if (func_calls[i] != nullptr) { new_dim.loc = func_calls[i]->base.loc; new_dim.m_start = func_calls[i]; new_dim.m_length = func_calls[i + 1]; new_dims.push_back(al, new_dim); } else { new_dims.push_back(al, t->m_dims[j]); } } } ASR::ttype_t* handle_return_type(ASR::ttype_t *return_type, const Location &loc, Vec<:call_arg_t>& args, ASR::Function_t* f=nullptr) { // Rebuild the return type if needed and make FunctionCalls use ExternalSymbol std::vector<:expr_t> func_calls; switch( return_type->type ) { case ASR::ttypeType::Allocatable: { ASR::Allocatable_t* allocatable_t = ASR::down_cast<:allocatable_t>(return_type); return ASRUtils::TYPE(ASR::make_Allocatable_t(al, loc, ASRUtils::type_get_past_allocatable( ASRUtils::type_get_past_pointer( handle_return_type(allocatable_t->m_type, loc, args, f))))); } case ASR::ttypeType::Pointer: { ASR::Pointer_t* pointer_t = ASR::down_cast<:pointer_t>(return_type); return ASRUtils::TYPE(ASR::make_Pointer_t(al, loc, ASRUtils::type_get_past_allocatable( ASRUtils::type_get_past_pointer( handle_return_type(pointer_t->m_type, loc, args, f))))); } case ASR::ttypeType::Array: { ASR::Array_t* t = ASR::down_cast<:array_t>(return_type); ASR::ttype_t* t_m_type = handle_return_type(t->m_type, loc, args, f); fill_expr_in_ttype_t(func_calls, t->m_dims, t->n_dims); fix_exprs_ttype_t(func_calls, args, f); Vec<:dimension_t> new_dims; fill_new_dims(t, func_calls, new_dims); return ASRUtils::make_Array_t_util(al, loc, t_m_type, new_dims.p, new_dims.size()); } case ASR::ttypeType::Character: { ASR::Character_t *t = ASR::down_cast<:character_t>(return_type); func_calls.push_back(t->m_len_expr); fix_exprs_ttype_t(func_calls, args, f); int64_t a_len = t->m_len; if( func_calls[0] ) { a_len = ASRUtils::extract_len(func_calls[0], loc); } return ASRUtils::TYPE(ASR::make_Character_t(al, loc, t->m_kind, a_len, func_calls[0])); } case ASR::ttypeType::Struct: { ASR::Struct_t* struct_t_type = ASR::down_cast<:struct_t>(return_type); ASR::symbol_t *sym = struct_t_type->m_derived_type; ASR::symbol_t *es_s = current_scope->resolve_symbol( ASRUtils::symbol_name(sym)); if (es_s == nullptr) { ASR::StructType_t *st = ASR::down_cast<:structtype_t>(sym); ASR::Module_t* sym_module = ASRUtils::get_sym_module(sym); LCOMPILERS_ASSERT(sym_module != nullptr); std::string st_name = "1_" + std::string(st->m_name); if (current_scope->get_symbol(st_name)) { sym = current_scope->get_symbol(st_name); } else { sym = ASR::down_cast<:symbol_t>(ASR::make_ExternalSymbol_t( al, st->base.base.loc, current_scope, s2c(al, st_name), sym, sym_module->m_name, nullptr, 0, st->m_name, ASR::accessType::Public)); current_scope->add_symbol(st_name, sym); } } else { sym = es_s; } return ASRUtils::TYPE(ASR::make_Struct_t(al, loc, sym)); } default: { return return_type; } } return nullptr; } void visit_expr_list(AST::expr_t** exprs, size_t n, Vec<:expr_t>& exprs_vec) { LCOMPILERS_ASSERT(exprs_vec.reserve_called); for( size_t i = 0; i < n; i++ ) { this->visit_expr(*exprs[i]); exprs_vec.push_back(al, ASRUtils::EXPR(tmp)); } } void visit_expr_list(AST::expr_t** exprs, size_t n, Vec<:call_arg_t>& call_args_vec) { LCOMPILERS_ASSERT(call_args_vec.reserve_called); for( size_t i = 0; i < n; i++ ) { this->visit_expr(*exprs[i]); ASR::expr_t* expr = nullptr; ASR::call_arg_t arg; arg.loc.first = -1; arg.loc.last = -1; if (tmp) { expr = ASRUtils::EXPR(tmp); arg.loc = expr->base.loc; } arg.m_value = expr; call_args_vec.push_back(al, arg); } } int64_t find_argument_position_from_name(ASR::Function_t* orig_func, std::string arg_name, const Location& call_loc, bool raise_error) { int64_t arg_position = -1; for( size_t i = 0; i < orig_func->n_args; i++ ) { ASR::Var_t* arg_Var = ASR::down_cast<:var_t>(orig_func->m_args[i]); std::string original_arg_name = ASRUtils::symbol_name(arg_Var->m_v); if( original_arg_name == arg_name ) { return i; } } for ( size_t i = 0; i < ASRUtils::get_FunctionType(orig_func)->n_restrictions; i++ ) { ASR::symbol_t* rt = ASRUtils::get_FunctionType(orig_func)->m_restrictions[i]; std::string rt_name = ASRUtils::symbol_name(rt); if ( rt_name == arg_name ) { return -2; } } if( raise_error && arg_position == -1 ) { throw SemanticError("Function " + std::string(orig_func->m_name) + " doesn't have an argument named '" + arg_name + "'", call_loc); } return arg_position; } bool visit_expr_list(AST::expr_t** pos_args, size_t n_pos_args, AST::keyword_t* kwargs, size_t n_kwargs, Vec<:call_arg_t>& call_args_vec, std::map<:string asr::symbol_t>& rt_subs, ASR::Function_t* orig_func, const Location& call_loc, bool raise_error=true) { LCOMPILERS_ASSERT(call_args_vec.reserve_called); // Fill the whole call_args_vec with nullptr // This is for error handling later on. for( size_t i = 0; i < n_pos_args + n_kwargs; i++ ) { ASR::call_arg_t call_arg; Location loc; loc.first = loc.last = 1; call_arg.m_value = nullptr; call_arg.loc = loc; call_args_vec.push_back(al, call_arg); } // Now handle positional arguments in the following loop for( size_t i = 0; i < n_pos_args; i++ ) { this->visit_expr(*pos_args[i]); ASR::expr_t* expr = ASRUtils::EXPR(tmp); call_args_vec.p[i].loc = expr->base.loc; call_args_vec.p[i].m_value = expr; } // Now handle keyword arguments in the following loop for( size_t i = 0; i < n_kwargs; i++ ) { this->visit_expr(*kwargs[i].m_value); ASR::expr_t* expr = ASRUtils::EXPR(tmp); std::string arg_name = std::string(kwargs[i].m_arg); int64_t arg_pos = find_argument_position_from_name(orig_func, arg_name, call_loc, raise_error); if( arg_pos == -1 ) { return false; } // Special treatment for argument to generic function's restriction if (arg_pos == -2) { if (ASR::is_a<:var_t>(*expr)) { ASR::Var_t* var = ASR::down_cast<:var_t>(expr); rt_subs[arg_name] = var->m_v; } continue; } if( call_args_vec[arg_pos].m_value != nullptr ) { if( !raise_error ) { return false; } throw SemanticError(std::string(orig_func->m_name) + "() got multiple values for argument '" + arg_name + "'", call_loc); } call_args_vec.p[arg_pos].loc = expr->base.loc; call_args_vec.p[arg_pos].m_value = expr; } return true; } int64_t find_argument_position_from_name(ASR::StructType_t* orig_struct, std::string arg_name) { for( size_t i = 0; i < orig_struct->n_members; i++ ) { std::string original_arg_name = std::string(orig_struct->m_members[i]); if( original_arg_name == arg_name ) { return i; } } return -1; } void visit_expr_list(AST::expr_t** pos_args, size_t n_pos_args, AST::keyword_t* kwargs, size_t n_kwargs, Vec<:call_arg_t>& call_args_vec, ASR::StructType_t* orig_struct, const Location &loc) { LCOMPILERS_ASSERT(call_args_vec.reserve_called); // Fill the whole call_args_vec with nullptr // This is for error handling later on. for( size_t i = 0; i < n_pos_args + n_kwargs; i++ ) { ASR::call_arg_t call_arg; Location loc; loc.first = loc.last = 1; call_arg.m_value = nullptr; call_arg.loc = loc; call_args_vec.push_back(al, call_arg); } // Now handle positional arguments in the following loop for( size_t i = 0; i < n_pos_args; i++ ) { this->visit_expr(*pos_args[i]); ASR::expr_t* expr = ASRUtils::EXPR(tmp); call_args_vec.p[i].loc = expr->base.loc; call_args_vec.p[i].m_value = expr; } // Now handle keyword arguments in the following loop for( size_t i = 0; i < n_kwargs; i++ ) { this->visit_expr(*kwargs[i].m_value); ASR::expr_t* expr = ASRUtils::EXPR(tmp); std::string arg_name = std::string(kwargs[i].m_arg); int64_t arg_pos = find_argument_position_from_name(orig_struct, arg_name); if( arg_pos == -1 ) { throw SemanticError("Member '" + arg_name + "' not found in struct", kwargs[i].loc); } else if (arg_pos >= (int64_t)call_args_vec.size()) { throw SemanticError("Not enough arguments to " + std::string(orig_struct->m_name) + "(), expected " + std::to_string(orig_struct->n_members), loc); } if( call_args_vec[arg_pos].m_value != nullptr ) { throw SemanticError(std::string(orig_struct->m_name) + "() got multiple values for argument '" + arg_name + "'", kwargs[i].loc); } call_args_vec.p[arg_pos].loc = expr->base.loc; call_args_vec.p[arg_pos].m_value = expr; } } void visit_expr_list_with_cast(ASR::expr_t** m_args, size_t n_args, Vec<:call_arg_t>& call_args_vec, Vec<:call_arg_t>& args, bool check_type_equality=true) { LCOMPILERS_ASSERT(call_args_vec.reserve_called); for (size_t i = 0; i < n_args; i++) { ASR::call_arg_t c_arg; c_arg.loc = args[i].loc; c_arg.m_value = args[i].m_value; cast_helper(m_args[i], c_arg.m_value, true); ASR::ttype_t* left_type = ASRUtils::expr_type(m_args[i]); ASR::ttype_t* right_type = ASRUtils::expr_type(c_arg.m_value); if( check_type_equality && !ASRUtils::check_equal_type(left_type, right_type) ) { std::string ltype = ASRUtils::type_to_str_python(left_type); std::string rtype = ASRUtils::type_to_str_python(right_type); diag.add(diag::Diagnostic( "Type mismatch in procedure call; the types must be compatible", diag::Level::Error, diag::Stage::Semantic, { diag::Label("type mismatch (passed argument type is " + rtype + " but required type is " + ltype + ")", { c_arg.loc, left_type->base.loc}) }) ); throw SemanticAbort(); } call_args_vec.push_back(al, c_arg); } } ASR::ttype_t* get_type_from_var_annotation(std::string var_annotation, const Location& loc, Vec<:dimension_t>& dims, AST::expr_t** m_args=nullptr, [[maybe_unused]] size_t n_args=0, bool raise_error=true, ASR::abiType abi=ASR::abiType::Source, bool is_argument=false) { ASR::ttype_t* type = nullptr; ASR::symbol_t *s = current_scope->resolve_symbol(var_annotation); if (s) { if (ASR::is_a<:variable_t>(*s)) { ASR::Variable_t *var_sym = ASR::down_cast<:variable_t>(s); if (var_sym->m_type->type == ASR::ttypeType::TypeParameter) { ASR::TypeParameter_t *type_param = ASR::down_cast<:typeparameter_t>(var_sym->m_type); type = ASRUtils::TYPE(ASR::make_TypeParameter_t(al, loc, type_param->m_param)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } } else { ASR::symbol_t *der_sym = ASRUtils::symbol_get_past_external(s); if( der_sym ) { if ( ASR::is_a<:structtype_t>(*der_sym) ) { type = ASRUtils::TYPE(ASR::make_Struct_t(al, loc, s)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if( ASR::is_a<:enumtype_t>(*der_sym) ) { type = ASRUtils::TYPE(ASR::make_Enum_t(al, loc, s)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if( ASR::is_a<:uniontype_t>(*der_sym) ) { type = ASRUtils::TYPE(ASR::make_Union_t(al, loc, s)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } } } } else if (var_annotation == "i8") { type = ASRUtils::TYPE(ASR::make_Integer_t(al, loc, 1)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "i16") { type = ASRUtils::TYPE(ASR::make_Integer_t(al, loc, 2)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "i32") { type = ASRUtils::TYPE(ASR::make_Integer_t(al, loc, 4)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "i64") { type = ASRUtils::TYPE(ASR::make_Integer_t(al, loc, 8)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "u8") { type = ASRUtils::TYPE(ASR::make_UnsignedInteger_t(al, loc, 1)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "u16") { type = ASRUtils::TYPE(ASR::make_UnsignedInteger_t(al, loc, 2)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "u32") { type = ASRUtils::TYPE(ASR::make_UnsignedInteger_t(al, loc, 4)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "u64") { type = ASRUtils::TYPE(ASR::make_UnsignedInteger_t(al, loc, 8)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "f32") { type = ASRUtils::TYPE(ASR::make_Real_t(al, loc, 4)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "f64") { type = ASRUtils::TYPE(ASR::make_Real_t(al, loc, 8)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "c32") { type = ASRUtils::TYPE(ASR::make_Complex_t(al, loc, 4)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "c64") { type = ASRUtils::TYPE(ASR::make_Complex_t(al, loc, 8)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "str") { type = ASRUtils::TYPE(ASR::make_Character_t(al, loc, 1, -2, nullptr)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "bool" || var_annotation == "i1") { type = ASRUtils::TYPE(ASR::make_Logical_t(al, loc, 4)); type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size(), abi, is_argument); } else if (var_annotation == "CPtr") { type = ASRUtils::TYPE(ASR::make_CPtr_t(al, loc)); } else if (var_annotation == "pointer") { LCOMPILERS_ASSERT(n_args == 1); AST::expr_t* underlying_type = m_args[0]; bool is_allocatable = false; type = ast_expr_to_asr_type(underlying_type->base.loc, *underlying_type, is_allocatable); type = ASRUtils::TYPE(ASR::make_Pointer_t(al, loc, type)); } else if (var_annotation == "S") { type = ASRUtils::TYPE(ASR::make_SymbolicExpression_t(al, loc)); } if( !type && raise_error ) { throw SemanticError("Unsupported type annotation: " + var_annotation, loc); } return type; } ASR::symbol_t* import_from_module(Allocator &al, ASR::Module_t *m, SymbolTable *current_scope, std::string /*mname*/, std::string cur_sym_name, std::string& new_sym_name, const Location &loc, bool skip_current_scope_check=false) { new_sym_name = ASRUtils::get_mangled_name(m, new_sym_name); ASR::symbol_t *t = m->m_symtab->resolve_symbol(cur_sym_name); if (!t) { throw SemanticError("The symbol '" + cur_sym_name + "' not found in the module '" + std::string(m->m_name) + "'", loc); } if (!skip_current_scope_check && current_scope->get_scope().find(new_sym_name) != current_scope->get_scope().end()) { throw SemanticError(new_sym_name + " already defined", loc); } if (ASR::is_a<:function_t>(*t)) { ASR::Function_t *mfn = ASR::down_cast<:function_t>(t); // `mfn` is the Function in a module. Now we construct // an ExternalSymbol that points to it. Str name; name.from_str(al, new_sym_name); char *cname = name.c_str(al); ASR::asr_t *fn = ASR::make_ExternalSymbol_t( al, loc, /* a_symtab */ current_scope, /* a_name */ cname, (ASR::symbol_t*)mfn, m->m_name, nullptr, 0, mfn->m_name, ASR::accessType::Public ); current_module_dependencies.push_back(al, m->m_name); return ASR::down_cast<:symbol_t>(fn); } else if (ASR::is_a<:structtype_t>(*t)) { ASR::StructType_t *st = ASR::down_cast<:structtype_t>(t); // `st` is the StructType in a module. Now we construct // an ExternalSymbol that points to it. Str name; name.from_str(al, new_sym_name); char *cname = name.c_str(al); ASR::asr_t *est = ASR::make_ExternalSymbol_t( al, loc, /* a_symtab */ current_scope, /* a_name */ cname, (ASR::symbol_t*)st, m->m_name, nullptr, 0, st->m_name, ASR::accessType::Public ); current_module_dependencies.push_back(al, m->m_name); return ASR::down_cast<:symbol_t>(est); } else if (ASR::is_a<:enumtype_t>(*t)) { ASR::EnumType_t *et = ASR::down_cast<:enumtype_t>(t); Str name; name.from_str(al, new_sym_name); char *cname = name.c_str(al); ASR::asr_t *est = ASR::make_ExternalSymbol_t( al, loc, /* a_symtab */ current_scope, /* a_name */ cname, (ASR::symbol_t*)et, m->m_name, nullptr, 0, et->m_name, ASR::accessType::Public ); current_module_dependencies.push_back(al, m->m_name); return ASR::down_cast<:symbol_t>(est); } else if (ASR::is_a<:uniontype_t>(*t)) { ASR::UnionType_t *ut = ASR::down_cast<:uniontype_t>(t); Str name; name.from_str(al, new_sym_name); char *cname = name.c_str(al); ASR::asr_t *est = ASR::make_ExternalSymbol_t( al, loc, /* a_symtab */ current_scope, /* a_name */ cname, (ASR::symbol_t*)ut, m->m_name, nullptr, 0, ut->m_name, ASR::accessType::Public ); current_module_dependencies.push_back(al, m->m_name); return ASR::down_cast<:symbol_t>(est); } else if (ASR::is_a<:variable_t>(*t)) { ASR::Variable_t *mv = ASR::down_cast<:variable_t>(t); // `mv` is the Variable in a module. Now we construct // an ExternalSymbol that points to it. Str name; name.from_str(al, new_sym_name); char *cname = name.c_str(al); ASR::asr_t *v = ASR::make_ExternalSymbol_t( al, loc, /* a_symtab */ current_scope, /* a_name */ cname, (ASR::symbol_t*)mv, m->m_name, nullptr, 0, mv->m_name, ASR::accessType::Public ); current_module_dependencies.push_back(al, m->m_name); return ASR::down_cast<:symbol_t>(v); } else if (ASR::is_a<:genericprocedure_t>(*t)) { ASR::GenericProcedure_t *gt = ASR::down_cast<:genericprocedure_t>(t); Str name; name.from_str(al, new_sym_name); char *cname = name.c_str(al); ASR::asr_t *v = ASR::make_ExternalSymbol_t( al, loc, /* a_symtab */ current_scope, /* a_name */ cname, (ASR::symbol_t*)gt, m->m_name, nullptr, 0, gt->m_name, ASR::accessType::Public ); current_module_dependencies.push_back(al, m->m_name); return ASR::down_cast<:symbol_t>(v); } else if (ASR::is_a<:externalsymbol_t>(*t)) { ASR::ExternalSymbol_t *es = ASR::down_cast<:externalsymbol_t>(t); SymbolTable *symtab = current_scope; // while (symtab->parent != nullptr) symtab = symtab->parent; ASR::symbol_t *sym = symtab->resolve_symbol(es->m_module_name); ASR::Module_t *m = ASR::down_cast<:module_t>(sym); return import_from_module(al, m, symtab, es->m_module_name, cur_sym_name, new_sym_name, loc); } else if (ASR::is_a<:module_t>(*t)) { ASR::Module_t* mt = ASR::down_cast<:module_t>(t); std::string mangled_cur_sym_name = ASRUtils::get_mangled_name(mt, new_sym_name); if( cur_sym_name == mangled_cur_sym_name ) { throw SemanticError("Importing modules isn't supported yet.", loc); } return import_from_module(al, mt, current_scope, std::string(mt->m_name), cur_sym_name, new_sym_name, loc); } else { throw SemanticError("Only Subroutines, Functions, StructType, Variables and " "ExternalSymbol are currently supported in 'import'", loc); } LCOMPILERS_ASSERT(false); return nullptr; } ASR::asr_t* make_dummy_assignment(ASR::expr_t* expr) { ASR::ttype_t* type = ASRUtils::expr_type(expr); std::string dummy_ret_name = current_scope->get_unique_name("__lcompilers_dummy", false); SetChar variable_dependencies_vec; variable_dependencies_vec.reserve(al, 1); ASRUtils::collect_variable_dependencies(al, variable_dependencies_vec, type); ASR::asr_t* variable_asr = ASR::make_Variable_t(al, expr->base.loc, current_scope, s2c(al, dummy_ret_name), variable_dependencies_vec.p, variable_dependencies_vec.size(), ASR::intentType::Local, nullptr, nullptr, ASR::storage_typeType::Default, type, nullptr, ASR::abiType::Source, ASR::accessType::Public, ASR::presenceType::Required, false); ASR::symbol_t* variable_sym = ASR::down_cast<:symbol_t>(variable_asr); current_scope->add_symbol(dummy_ret_name, variable_sym); ASR::expr_t* variable_var = ASRUtils::EXPR(ASR::make_Var_t(al, expr->base.loc, variable_sym)); return ASR::make_Assignment_t(al, expr->base.loc, variable_var, expr, nullptr); } // Function to create appropriate call based on symbol type. If it is external // generic symbol then it changes the name accordingly. ASR::asr_t* make_call_helper(Allocator &al, ASR::symbol_t* s, SymbolTable *current_scope, Vec<:call_arg_t> args, std::string call_name, const Location &loc, AST::expr_t** pos_args=nullptr, size_t n_pos_args=0, AST::keyword_t* kwargs=nullptr, size_t n_kwargs=0) { if (intrinsic_node_handler.is_present(call_name)) { return intrinsic_node_handler.get_intrinsic_node(call_name, al, loc, args); } if (call_name == "list" && (args.size() == 0 || args[0].m_value == nullptr)) { if (assign_asr_target) { ASR::ttype_t *type = ASRUtils::get_contained_type( ASRUtils::type_get_past_const(ASRUtils::expr_type(assign_asr_target))); ASR::ttype_t* list_type = ASRUtils::TYPE(ASR::make_List_t(al, loc, type)); Vec<:expr_t> list; list.reserve(al, 1); return ASR::make_ListConstant_t(al, loc, list.p, list.size(), list_type); } return nullptr; } ASR::symbol_t *s_generic = nullptr, *stemp = s; // Type map for generic functions std::map<:string asr::ttype_t> subs; std::map<:string asr::symbol_t> rt_subs; // handling ExternalSymbol s = ASRUtils::symbol_get_past_external(s); bool is_generic_procedure = ASR::is_a<:genericprocedure_t>(*s); if (ASR::is_a<:genericprocedure_t>(*s)) { s_generic = stemp; ASR::GenericProcedure_t *p = ASR::down_cast<:genericprocedure_t>(s); int idx = -1; if( n_kwargs > 0 ) { args.reserve(al, n_pos_args + n_kwargs); for( size_t iproc = 0; iproc < p->n_procs; iproc++ ) { args.n = 0; ASR::Function_t* orig_func = ASR::down_cast<:function_t>(p->m_procs[iproc]); if( !visit_expr_list(pos_args, n_pos_args, kwargs, n_kwargs, args, rt_subs, orig_func, loc, false) ) { continue; } idx = ASRUtils::select_generic_procedure(args, *p, loc, [&](const std::string &msg, const Location &loc) { throw SemanticError(msg, loc); }, false); if( idx == (int) iproc ) { break; } } if( idx == -1 ) { throw SemanticError("Arguments do not match for any generic procedure, " + std::string(p->m_name), loc); } } else { idx = ASRUtils::select_generic_procedure(args, *p, loc, [&](const std::string &msg, const Location &loc) { throw SemanticError(msg, loc); }); } s = p->m_procs[idx]; std::string remote_sym = ASRUtils::symbol_name(s); std::string local_sym = ASRUtils::symbol_name(s); if (ASR::is_a<:externalsymbol_t>(*stemp)) { local_sym = std::string(p->m_name) + "@" + local_sym; } SymbolTable *symtab = current_scope; if (symtab->resolve_symbol(local_sym) == nullptr) { LCOMPILERS_ASSERT(ASR::is_a<:externalsymbol_t>(*stemp)); std::string mod_name = ASR::down_cast<:externalsymbol_t>(stemp)->m_module_name; ASR::symbol_t *mt = symtab->resolve_symbol(mod_name); ASR::Module_t *m = ASR::down_cast<:module_t>(mt); local_sym = ASRUtils::get_mangled_name(m, local_sym); stemp = import_from_module(al, m, symtab, mod_name, remote_sym, local_sym, loc); LCOMPILERS_ASSERT(ASR::is_a<:externalsymbol_t>(*stemp)); symtab->add_symbol(local_sym, stemp); s = ASRUtils::symbol_get_past_external(stemp); } else { stemp = symtab->resolve_symbol(local_sym); } } if (ASR::is_a<:function_t>(*s)) { ASR::Function_t *func = ASR::down_cast<:function_t>(s); if( n_kwargs > 0 && !is_generic_procedure ) { args.reserve(al, n_pos_args + n_kwargs); visit_expr_list(pos_args, n_pos_args, kwargs, n_kwargs, args, rt_subs, func, loc); } if (ASRUtils::get_FunctionType(func)->m_is_restriction) { rt_vec.push_back(s); } else if (ASRUtils::is_generic_function(s)) { if (n_pos_args != func->n_args) { std::string fnd = std::to_string(n_pos_args); std::string org = std::to_string(func->n_args); diag.add(diag::Diagnostic( "Number of arguments does not match in the function call", diag::Level::Error, diag::Stage::Semantic, { diag::Label("(found: '" + fnd + "', expected: '" + org + "')", {loc}) }) ); throw SemanticAbort(); } for (size_t i=0; im_args[i]); ASR::ttype_t *arg_type = ASRUtils::expr_type(args[i].m_value); check_type_substitution(subs, param_type, arg_type, loc); } for (size_t i=0; i<:get_functiontype>n_restrictions; i++) { ASR::Function_t* rt = ASR::down_cast<:function_t>( ASRUtils::get_FunctionType(func)->m_restrictions[i]); check_type_restriction(subs, rt_subs, rt, loc); } //ASR::symbol_t *t = get_generic_function(subs, rt_subs, func); ASR::symbol_t *t = get_generic_function(subs, rt_subs, s); std::string new_call_name = (ASR::down_cast<:function_t>(t))->m_name; // Currently ignoring keyword arguments for generic function calls Vec<:call_arg_t> new_args; new_args.reserve(al, n_pos_args); for (size_t i = 0; in_args) { std::string fnd = std::to_string(args.size()); std::string org = std::to_string(func->n_args); diag.add(diag::Diagnostic( "Number of arguments does not match in the function call", diag::Level::Error, diag::Stage::Semantic, { diag::Label("(found: '" + fnd + "', expected: '" + org + "')", {loc}) }) ); throw SemanticAbort(); } if (ASR::down_cast<:function_t>(s)->m_return_var != nullptr) { ASR::ttype_t *a_type = nullptr; if( ASRUtils::get_FunctionType(func)->m_elemental && args.size() == 1 && ASRUtils::is_array(ASRUtils::expr_type(args[0].m_value)) ) { a_type = ASRUtils::expr_type(args[0].m_value); } else { a_type = ASRUtils::expr_type(func->m_return_var); a_type = handle_return_type(a_type, loc, args, func); } ASR::expr_t *value = nullptr; if (ASRUtils::is_intrinsic_function2(func)) { value = intrinsic_procedures.comptime_eval(call_name, al, loc, args); } Vec<:call_arg_t> args_new; args_new.reserve(al, func->n_args); visit_expr_list_with_cast(func->m_args, func->n_args, args_new, args, !ASRUtils::is_intrinsic_function2(func)); dependencies.push_back(al, ASRUtils::symbol_name(stemp)); return ASRUtils::make_FunctionCall_t_util(al, loc, stemp, s_generic, args_new.p, args_new.size(), a_type, value, nullptr); } else { Vec<:call_arg_t> args_new; args_new.reserve(al, func->n_args); visit_expr_list_with_cast(func->m_args, func->n_args, args_new, args); dependencies.push_back(al, ASRUtils::symbol_name(stemp)); return ASRUtils::make_SubroutineCall_t_util(al, loc, stemp, s_generic, args_new.p, args_new.size(), nullptr, nullptr, false); } } else if(ASR::is_a<:structtype_t>(*s)) { ASR::StructType_t* StructType = ASR::down_cast<:structtype_t>(s); if (n_kwargs > 0) { args.reserve(al, n_pos_args + n_kwargs); visit_expr_list(pos_args, n_pos_args, kwargs, n_kwargs, args, StructType, loc); } if (args.size() > 0 && args.size() > StructType->n_members) { throw SemanticError("Struct constructor has more arguments than the number of struct members", loc); } for( size_t i = 0; i < args.size(); i++ ) { std::string member_name = StructType->m_members[i]; ASR::Variable_t* member_var = ASR::down_cast<:variable_t>( StructType->m_symtab->resolve_symbol(member_name)); ASR::expr_t* arg_new_i = args[i].m_value; cast_helper(member_var->m_type, arg_new_i, arg_new_i->base.loc); ASR::ttype_t* left_type = member_var->m_type; ASR::ttype_t* right_type = ASRUtils::expr_type(arg_new_i); if( !ASRUtils::check_equal_type(left_type, right_type) ) { std::string ltype = ASRUtils::type_to_str_python(left_type); std::string rtype = ASRUtils::type_to_str_python(right_type); diag.add(diag::Diagnostic( "Type mismatch in procedure call; the types must be compatible", diag::Level::Error, diag::Stage::Semantic, { diag::Label("type mismatch (passed argument type is " + rtype + " but required type is " + ltype + ")", { arg_new_i->base.loc, left_type->base.loc}) }) ); throw SemanticAbort(); } args.p[i].m_value = arg_new_i; } for (size_t i = args.size(); i < StructType->n_members; i++) { args.push_back(al, StructType->m_initializers[i]); } ASR::ttype_t* der_type = ASRUtils::TYPE(ASR::make_Struct_t(al, loc, stemp)); return ASR::make_StructTypeConstructor_t(al, loc, stemp, args.p, args.size(), der_type, nullptr); } else if( ASR::is_a<:enumtype_t>(*s) ) { Vec<:expr_t> args_new; args_new.reserve(al, args.size()); ASRUtils::visit_expr_list(al, args, args_new); ASR::EnumType_t* enumtype = ASR::down_cast<:enumtype_t>(s); for( size_t i = 0; i < std::min(args.size(), enumtype->n_members); i++ ) { std::string member_name = enumtype->m_members[i]; ASR::Variable_t* member_var = ASR::down_cast<:variable_t>( enumtype->m_symtab->resolve_symbol(member_name)); ASR::expr_t* arg_new_i = args_new[i]; cast_helper(member_var->m_type, arg_new_i, arg_new_i->base.loc); args_new.p[i] = arg_new_i; } ASR::ttype_t* der_type = ASRUtils::TYPE(ASR::make_Enum_t(al, loc, s)); return ASR::make_EnumTypeConstructor_t(al, loc, stemp, args_new.p, args_new.size(), der_type, nullptr); } else if( ASR::is_a<:uniontype_t>(*s) ) { if( args.size() != 0 ) { throw SemanticError("Union constructors do not accept any argument as of now.", loc); } ASR::ttype_t* union_ = ASRUtils::TYPE(ASR::make_Union_t(al, loc, stemp)); return ASR::make_UnionTypeConstructor_t(al, loc, stemp, nullptr, 0, union_, nullptr); } else { throw SemanticError("Unsupported call type for " + call_name, loc); } } /** * @brief Check if the type of the argument given does not contradict * with previously checked type substitution. */ void check_type_substitution(std::map<:string asr::ttype_t>& subs, ASR::ttype_t *param_type, ASR::ttype_t *arg_type, const Location &loc) { if (ASR::is_a<:list_t>(*param_type)) { if (ASR::is_a<:list_t>(*arg_type)) { ASR::ttype_t *param_elem = ASR::down_cast<:list_t>(param_type)->m_type; ASR::ttype_t *arg_elem = ASR::down_cast<:list_t>(arg_type)->m_type; return check_type_substitution(subs, param_elem, arg_elem, loc); } else { throw SemanticError("The parameter is a list while the argument is not a list", loc); } } ASR::ttype_t* param_type_ = ASRUtils::type_get_past_array(param_type); if (ASR::is_a<:typeparameter_t>(*param_type_)) { ASR::TypeParameter_t *tp = ASR::down_cast<:typeparameter_t>(param_type_); std::string param_name = tp->m_param; if (subs.find(param_name) != subs.end()) { if (!ASRUtils::check_equal_type(subs[param_name], arg_type)) { throw SemanticError("Inconsistent type variable for the function call", loc); } } else { if (ASRUtils::is_array(param_type) && ASRUtils::is_array(arg_type)) { ASR::dimension_t* dims = nullptr; int param_dims = ASRUtils::extract_dimensions_from_ttype(param_type, dims); int arg_dims = ASRUtils::extract_dimensions_from_ttype(arg_type, dims); if (param_dims == arg_dims) { subs[param_name] = ASRUtils::duplicate_type_without_dims(al, arg_type, arg_type->base.loc); } else { throw SemanticError("Inconsistent type subsititution for array type", loc); } } else { subs[param_name] = ASRUtils::duplicate_type(al, arg_type); } } } } /** * @brief Check if the given function and the type substitution satisfies * the restriction */ void check_type_restriction(std::map<:string asr::ttype_t> subs, std::map<:string asr::symbol_t> rt_subs, ASR::Function_t* rt, const Location& loc) { std::string rt_name = rt->m_name; if (rt_subs.find(rt_name) != rt_subs.end()) { ASR::symbol_t* rt_arg = rt_subs[rt_name]; if (!ASR::is_a<:function_t>(*rt_arg)) { std::string msg = "The restriction " + rt_name + " requires a " + "function as an argument"; throw SemanticError(msg, loc); } ASR::Function_t* rt_arg_func = ASR::down_cast<:function_t>(rt_arg); /** @brief different argument number between the function given and the * restriction result in error **/ if (rt->n_args != rt_arg_func->n_args) { std::string msg = "The function " + std::string(rt_arg_func->m_name) + " provided for the restriction " + std::string(rt->m_name) + " have different number of arguments"; throw SemanticError(msg, rt_arg->base.loc); } for (size_t j=0; jn_args; j++) { ASR::ttype_t* rt_type = ASRUtils::expr_type(rt->m_args[j]); ASR::ttype_t* rt_arg_type = ASRUtils::expr_type(rt_arg_func->m_args[j]); if (ASRUtils::is_type_parameter(*rt_type)) { std::string rt_type_param = ASR::down_cast<:typeparameter_t>( ASRUtils::get_type_parameter(rt_type))->m_param; /** * @brief if the type of the function given for the restriction does not * satisfy the type substitution from the function argument, it * results in error **/ if (!ASRUtils::check_equal_type(subs[rt_type_param], rt_arg_type)) { throw SemanticError("Restriction mismatch with provided arguments", rt_arg->base.loc); } } } if (rt->m_return_var) { if (!rt_arg_func->m_return_var) { throw SemanticError("The function provided to the restriction should " "have a return value", rt_arg->base.loc); } ASR::ttype_t* rt_return = ASRUtils::expr_type(rt->m_return_var); ASR::ttype_t* rt_arg_return = ASRUtils::expr_type(rt_arg_func->m_return_var); if (ASRUtils::is_type_parameter(*rt_return)) { std::string rt_return_param = ASR::down_cast<:typeparameter_t>( ASRUtils::get_type_parameter(rt_return))->m_param; if (!ASRUtils::check_equal_type(subs[rt_return_param], rt_arg_return)) { throw SemanticError("Restriction mismatch with provided arguments", rt_arg->base.loc); } } } else { if (rt_arg_func->m_return_var) { throw SemanticError("The function provided to the restriction should " "not have a return value", rt_arg->base.loc); } } return; } /** * @brief Check if there is not argument given to the restriction * plus : integer x integer -> integer, real x real -> real * zero : integer -> integer, real -> real * div : integer x i32 -> f64, real x i32 -> f64 */ if (rt_name == "add" && rt->n_args == 2) { ASR::ttype_t* left_type = ASRUtils::expr_type(rt->m_args[0]); ASR::ttype_t* right_type = ASRUtils::expr_type(rt->m_args[1]); left_type = ASR::is_a<:typeparameter_t>(*left_type) ? subs[ASR::down_cast<:typeparameter_t>(left_type)->m_param] : left_type; right_type = ASR::is_a<:typeparameter_t>(*right_type) ? subs[ASR::down_cast<:typeparameter_t>(right_type)->m_param] : right_type; if ((ASRUtils::is_integer(*left_type) && ASRUtils::is_integer(*right_type)) || (ASRUtils::is_real(*left_type) && ASRUtils::is_real(*right_type))) { return; } } else if (rt_name == "zero" && rt->n_args == 1) { ASR::ttype_t* type = ASRUtils::expr_type(rt->m_args[0]); type = ASR::is_a<:typeparameter_t>(*type) ? subs[ASR::down_cast<:typeparameter_t>(type)->m_param] : type; if (ASRUtils::is_integer(*type) || ASRUtils::is_real(*type)) { return; } } else if (rt_name == "div" && rt->n_args == 2) { ASR::ttype_t* left_type = ASRUtils::expr_type(rt->m_args[0]); ASR::ttype_t* right_type = ASRUtils::expr_type(rt->m_args[1]); left_type = ASR::is_a<:typeparameter_t>(*left_type) ? subs[ASR::down_cast<:typeparameter_t>(left_type)->m_param] : left_type; if ((ASRUtils::is_integer(*left_type) && ASRUtils::is_integer(*right_type)) || (ASRUtils::is_real(*left_type) && ASRUtils::is_integer(*right_type))) { return; } } throw SemanticError("No applicable argument to the restriction " + rt_name , loc); } /** * @brief Check if the generic function has been instantiated with similar * arguments. If not, then instantiate a new function. */ ASR::symbol_t* get_generic_function(std::map<:string asr::ttype_t> subs, std::map<:string asr::symbol_t>& rt_subs, ASR::symbol_t *sym) { int new_function_num; ASR::symbol_t *t; std::string func_name = ASRUtils::symbol_name(sym); if (generic_func_nums.find(func_name) != generic_func_nums.end()) { new_function_num = generic_func_nums[func_name]; for (int i=0; i subs_check = generic_func_subs[generic_func_name]; if (subs_check.size() != subs.size()) { continue; } bool defined = true; for (auto const &subs_check_pair: subs_check) { if (subs.find(subs_check_pair.first) == subs.end()) { defined = false; break; } ASR::ttype_t* subs_type = subs[subs_check_pair.first]; ASR::ttype_t* subs_check_type = subs_check_pair.second; if (!ASRUtils::check_equal_type(subs_type, subs_check_type)) { defined = false; break; } } if (defined) { t = current_scope->resolve_symbol(generic_func_name); return t; } } } } else { new_function_num = 0; } generic_func_nums[func_name] = new_function_num + 1; std::string new_func_name = "__asr_generic_" + func_name + "_" + std::to_string(new_function_num); generic_func_subs[new_func_name] = subs; SymbolTable *target_scope = ASRUtils::symbol_parent_symtab(sym); t = pass_instantiate_symbol(al, context_map, subs, rt_subs, target_scope, target_scope, new_func_name, sym); if (ASR::is_a<:function_t>(*sym)) { ASR::Function_t *f = ASR::down_cast<:function_t>(sym); ASR::Function_t *new_f = ASR::down_cast<:function_t>(t); t = pass_instantiate_function_body(al, context_map, subs, rt_subs, target_scope, target_scope, new_f, f); } dependencies.erase(s2c(al, func_name)); dependencies.push_back(al, s2c(al, new_func_name)); return t; } bool contains_local_variable(ASR::expr_t* value) { if( ASR::is_a<:var_t>(*value) ) { ASR::Var_t* var_value = ASR::down_cast<:var_t>(value); ASR::symbol_t* var_value_sym = var_value->m_v; ASR::Variable_t* var_value_variable = ASR::down_cast<:variable_t>( ASRUtils::symbol_get_past_external(var_value_sym)); return var_value_variable->m_intent == ASR::intentType::Local; } // TODO: Let any other expression pass through // Will be handled later return false; } void fill_dims_for_asr_type(Vec<:dimension_t>& dims, ASR::expr_t* value, const Location& loc, bool is_allocatable=false) { ASR::dimension_t dim; dim.loc = loc; if (ASR::is_a<:integerconstant_t>(*value) || ASR::is_a<:var_t>(*value) || ASR::is_a<:integerbinop_t>(*value)) { ASR::ttype_t *itype = ASRUtils::expr_type(value); ASR::expr_t* one = ASRUtils::EXPR(ASR::make_IntegerConstant_t(al, loc, 1, itype)); ASR::expr_t* zero = ASRUtils::EXPR(ASR::make_IntegerConstant_t(al, loc, 0, itype)); ASR::expr_t* comptime_val = nullptr; int64_t value_int = -1; if( !ASRUtils::extract_value(ASRUtils::expr_value(value), value_int) && contains_local_variable(value) && !is_allocatable) { throw SemanticError("Only those local variables which can be reduced to compile " "time constant should be used in dimensions of an array.", value->base.loc); } if (value_int != -1) { comptime_val = ASRUtils::EXPR(ASR::make_IntegerConstant_t(al, loc, value_int - 1, itype)); } dim.m_start = zero; dim.m_length = ASRUtils::compute_length_from_start_end(al, dim.m_start, ASRUtils::EXPR(ASR::make_IntegerBinOp_t(al, value->base.loc, value, ASR::binopType::Sub, one, itype, comptime_val))); dims.push_back(al, dim); } else if(ASR::is_a<:tupleconstant_t>(*value)) { ASR::TupleConstant_t* tuple_constant = ASR::down_cast<:tupleconstant_t>(value); for( size_t i = 0; i < tuple_constant->n_elements; i++ ) { ASR::expr_t *value = tuple_constant->m_elements[i]; fill_dims_for_asr_type(dims, value, loc, is_allocatable); } } else if(ASR::is_a<:listconstant_t>(*value)) { ASR::ListConstant_t* list_constant = ASR::down_cast<:listconstant_t>(value); for( size_t i = 0; i < list_constant->n_args; i++ ) { ASR::expr_t *value = list_constant->m_args[i]; fill_dims_for_asr_type(dims, value, loc, is_allocatable); } } else if(ASR::is_a<:enumvalue_t>(*value)) { ASR::expr_t* enum_value = ASRUtils::expr_value( ASR::down_cast<:enumvalue_t>(value)->m_value); if (!enum_value) { throw SemanticError("Only constant enumeration values are " "supported as array dimensions.", loc); } fill_dims_for_asr_type(dims, enum_value, loc, is_allocatable); } else { throw SemanticError("Only Integer, `:` or identifier in [] in " "Subscript supported for now in annotation " "found, " + std::to_string(value->type), loc); } } bool is_runtime_array(AST::expr_t* m_slice) { if( AST::is_a<:tuple_t>(*m_slice) ) { AST::Tuple_t* multidim = AST::down_cast<:tuple_t>(m_slice); for( size_t i = 0; i < multidim->n_elts; i++ ) { if( AST::is_a<:slice_t>(*multidim->m_elts[i]) ) { return true; } } } return false; } void raise_error_when_dict_key_is_float_or_complex(ASR::ttype_t* key_type, const Location& loc) { if(ASR::is_a<:real_t>(*key_type) || ASR::is_a<:complex_t>(*key_type)) { throw SemanticError("'dict' key type cannot be float/complex because resolving collisions " "by exact comparison of float/complex values will result in unexpected " "behaviours. In addition fuzzy equality checks with a certain tolerance " "does not follow transitivity with float/complex values.", loc); } } AST::expr_t* get_var_intent_and_annotation(AST::expr_t *annotation, ASR::intentType &intent) { if (AST::is_a<:subscript_t>(*annotation)) { AST::Subscript_t *s = AST::down_cast<:subscript_t>(annotation); if (AST::is_a<:name_t>(*s->m_value)) { std::string ann_name = AST::down_cast<:name_t>(s->m_value)->m_id; if (ann_name == "In") { intent = ASRUtils::intent_in; return s->m_slice; } else if (ann_name == "InOut") { intent = ASRUtils::intent_inout; return s->m_slice; } else if (ann_name == "Out") { intent = ASRUtils::intent_out; return s->m_slice; } return annotation; } else { throw SemanticError("Only Name in Subscript supported for now in annotation", annotation->base.loc); } } return annotation; } // Convert Python AST type annotation to an ASR type // Examples: // i32, i64, f32, f64 // f64[256], i32[:] ASR::ttype_t * ast_expr_to_asr_type(const Location &loc, const AST::expr_t &annotation, bool &is_allocatable, bool raise_error=true, ASR::abiType abi=ASR::abiType::Source, bool is_argument=false) { Vec<:dimension_t> dims; dims.reserve(al, 4); AST::expr_t** m_args = nullptr; size_t n_args = 0; std::string var_annotation; if (AST::is_a<:name_t>(annotation)) { AST::Name_t *n = AST::down_cast<:name_t>(&annotation); var_annotation = n->m_id; return get_type_from_var_annotation(var_annotation, annotation.base.loc, dims, m_args, n_args, raise_error, abi, is_argument); } if (AST::is_a<:constantnone_t>(annotation)) { return nullptr; } if (AST::is_a<:subscript_t>(annotation)) { AST::Subscript_t *s = AST::down_cast<:subscript_t>(&annotation); if (AST::is_a<:name_t>(*s->m_value)) { AST::Name_t *n = AST::down_cast<:name_t>(s->m_value); var_annotation = n->m_id; } else { throw SemanticError("Only Name in Subscript supported for now in annotation", loc); } if (var_annotation == "In" || var_annotation == "InOut" || var_annotation == "Out") { throw SemanticError("Intent annotation '" + var_annotation + "' cannot be used here", s->base.base.loc); } if (var_annotation == "tuple") { Vec<:ttype_t> types; types.reserve(al, 4); if (AST::is_a<:name_t>(*s->m_slice)) { types.push_back(al, ast_expr_to_asr_type(loc, *s->m_slice, is_allocatable, raise_error, abi, is_argument)); } else if (AST::is_a<:tuple_t>(*s->m_slice)) { AST::Tuple_t *t = AST::down_cast<:tuple_t>(s->m_slice); for (size_t i=0; in_elts; i++) { types.push_back(al, ast_expr_to_asr_type(loc, *t->m_elts[i], is_allocatable, raise_error, abi, is_argument)); } } else { throw SemanticError("Only Name or Tuple in Subscript supported for now in `tuple` annotation", loc); } ASR::ttype_t *type = ASRUtils::TYPE(ASR::make_Tuple_t(al, loc, types.p, types.size())); return type; } else if (var_annotation == "Callable") { LCOMPILERS_ASSERT(AST::is_a<:tuple_t>(*s->m_slice)); AST::Tuple_t *t = AST::down_cast<:tuple_t>(s->m_slice); LCOMPILERS_ASSERT(t->n_elts <= 2 && t->n_elts >= 1); Vec<:ttype_t> arg_types; LCOMPILERS_ASSERT(AST::is_a<:list_t>(*t->m_elts[0])); AST::List_t *arg_list = AST::down_cast<:list_t>(t->m_elts[0]); if (arg_list->n_elts > 0) { arg_types.reserve(al, arg_list->n_elts); for (size_t i=0; in_elts; i++) { arg_types.push_back(al, ast_expr_to_asr_type(loc, *arg_list->m_elts[i], is_allocatable, raise_error, abi, is_argument)); } } else { arg_types.reserve(al, 1); } ASR::ttype_t* ret_type = nullptr; if (t->n_elts == 2) { ret_type = ast_expr_to_asr_type(loc, *t->m_elts[1], is_allocatable, raise_error, abi, is_argument); } ASR::ttype_t *type = ASRUtils::TYPE(ASR::make_FunctionType_t(al, loc, arg_types.p, arg_types.size(), ret_type, ASR::abiType::Source, ASR::deftypeType::Interface, nullptr, false, false, false, false, false, nullptr, 0, false)); return type; } else if (var_annotation == "set") { if (AST::is_a<:name_t>(*s->m_slice) || AST::is_a<:subscript_t>(*s->m_slice)) { ASR::ttype_t *type = ast_expr_to_asr_type(loc, *s->m_slice, is_allocatable, raise_error, abi, is_argument); return ASRUtils::TYPE(ASR::make_Set_t(al, loc, type)); } else { throw SemanticError("Only Name in Subscript supported for now in `set`" " annotation", loc); } } else if (var_annotation == "list") { ASR::ttype_t *type = nullptr; if (AST::is_a<:name_t>(*s->m_slice) || AST::is_a<:subscript_t>(*s->m_slice)) { type = ast_expr_to_asr_type(loc, *s->m_slice, is_allocatable, raise_error, abi, is_argument); return ASRUtils::TYPE(ASR::make_List_t(al, loc, type)); } else { throw SemanticError("Only Name or Subscript inside Subscript supported for now in `list`" " annotation", loc); } } else if (var_annotation == "Allocatable") { ASR::ttype_t *type = nullptr; if (AST::is_a<:name_t>(*s->m_slice) || AST::is_a<:subscript_t>(*s->m_slice)) { type = ast_expr_to_asr_type(loc, *s->m_slice, is_allocatable, raise_error, abi, is_argument); is_allocatable = true; return type; } else { throw SemanticError("Only Name or Subscript inside Subscript supported for now in `list`" " annotation", loc); } } else if (var_annotation == "dict") { if (AST::is_a<:tuple_t>(*s->m_slice)) { AST::Tuple_t *t = AST::down_cast<:tuple_t>(s->m_slice); if (t->n_elts != 2) { throw SemanticError("`dict` annotation must have 2 elements: types" " of both keys and values", loc); } ASR::ttype_t *key_type = ast_expr_to_asr_type(loc, *t->m_elts[0], is_allocatable, raise_error, abi, is_argument); ASR::ttype_t *value_type = ast_expr_to_asr_type(loc, *t->m_elts[1], is_allocatable, raise_error, abi, is_argument); raise_error_when_dict_key_is_float_or_complex(key_type, loc); return ASRUtils::TYPE(ASR::make_Dict_t(al, loc, key_type, value_type)); } else { throw SemanticError("`dict` annotation must have 2 elements: types of" " both keys and values", loc); } } else if (var_annotation == "Pointer") { ASR::ttype_t *type = ast_expr_to_asr_type(loc, *s->m_slice, is_allocatable, raise_error, abi, is_argument); return ASRUtils::TYPE(ASR::make_Pointer_t(al, loc, type)); } else if (var_annotation == "Const") { ASR::ttype_t *type = ast_expr_to_asr_type(loc, *s->m_slice, is_allocatable, raise_error, abi, is_argument); return ASRUtils::TYPE(ASR::make_Const_t(al, loc, type)); } else { AST::expr_t* dim_info = s->m_slice; if (var_annotation == "Array") { LCOMPILERS_ASSERT(AST::is_a<:tuple_t>(*s->m_slice)); AST::Tuple_t *t = AST::down_cast<:tuple_t>(s->m_slice); LCOMPILERS_ASSERT(t->n_elts >= 2); LCOMPILERS_ASSERT(AST::is_a<:name_t>(*t->m_elts[0])); var_annotation = AST::down_cast<:name_t>(t->m_elts[0])->m_id; Vec<:expr_t> dims; dims.reserve(al, 0); for (size_t i = 1; i < t->n_elts; i++) { dims.push_back(al, t->m_elts[i]); } AST::ast_t* dim_tuple = AST::make_Tuple_t(al, t->base.base.loc, dims.p, dims.size(), AST::expr_contextType::Load); dim_info = AST::down_cast<:expr_t>(dim_tuple); } ASR::ttype_t* type = get_type_from_var_annotation(var_annotation, annotation.base.loc, dims, m_args, n_args, raise_error, abi, is_argument); if (AST::is_a<:slice_t>(*dim_info)) { ASR::dimension_t dim; dim.loc = loc; dim.m_start = nullptr; dim.m_length = nullptr; dims.push_back(al, dim); } else if( is_runtime_array(dim_info) ) { AST::Tuple_t* tuple_multidim = AST::down_cast<:tuple_t>(dim_info); for( size_t i = 0; i < tuple_multidim->n_elts; i++ ) { if( AST::is_a<:slice_t>(*tuple_multidim->m_elts[i]) ) { ASR::dimension_t dim; dim.loc = loc; dim.m_start = nullptr; dim.m_length = nullptr; dims.push_back(al, dim); } } } else { this->visit_expr(*dim_info); ASR::expr_t *value = ASRUtils::EXPR(tmp); fill_dims_for_asr_type(dims, value, loc); } if (ASRUtils::ttype_set_dimensions(&type, dims.p, dims.size(), al, abi, is_argument)) { return type; } throw SemanticError("ICE: Unable to set dimensions for: " + var_annotation, loc); } } else if (AST::is_a<:attribute_t>(annotation)) { AST::Attribute_t* attr_annotation = AST::down_cast<:attribute_t>(&annotation); LCOMPILERS_ASSERT(AST::is_a<:name_t>(*attr_annotation->m_value)); std::string value = AST::down_cast<:name_t>(attr_annotation->m_value)->m_id; ASR::symbol_t *t = current_scope->resolve_symbol(value); if (!t) { throw SemanticError("'" + value + "' is not defined in the scope", attr_annotation->base.base.loc); } LCOMPILERS_ASSERT(ASR::is_a