(&mut self, func: &ast::Expr, args: &ast::Arguments)
| 8446 | } |
| 8447 | |
| 8448 | fn compile_call(&mut self, func: &ast::Expr, args: &ast::Arguments) -> CompileResult<()> { |
| 8449 | // Save the call expression's source range so CALL instructions use the |
| 8450 | // call start line, not the last argument's line. |
| 8451 | let call_range = self.current_source_range; |
| 8452 | let uses_ex_call = self.call_uses_ex_call(args); |
| 8453 | |
| 8454 | // Method call: obj → LOAD_ATTR_METHOD → [method, self_or_null] → args → CALL |
| 8455 | // Regular call: func → PUSH_NULL → args → CALL |
| 8456 | if let ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = &func { |
| 8457 | // Check for super() method call optimization |
| 8458 | if !uses_ex_call |
| 8459 | && let Some(super_type) = self.can_optimize_super_call(value, attr.as_str()) |
| 8460 | { |
| 8461 | // super().method() or super(cls, self).method() optimization |
| 8462 | // Stack: [global_super, class, self] → LOAD_SUPER_METHOD → [method, self] |
| 8463 | // Set source range to the super() call for LOAD_GLOBAL/LOAD_DEREF/etc. |
| 8464 | let super_range = value.range(); |
| 8465 | self.set_source_range(super_range); |
| 8466 | self.load_args_for_super(&super_type)?; |
| 8467 | self.set_source_range(super_range); |
| 8468 | let idx = self.name(attr.as_str()); |
| 8469 | match super_type { |
| 8470 | SuperCallType::TwoArg { .. } => { |
| 8471 | self.emit_load_super_method(idx); |
| 8472 | } |
| 8473 | SuperCallType::ZeroArg => { |
| 8474 | self.emit_load_zero_super_method(idx); |
| 8475 | } |
| 8476 | } |
| 8477 | // NOP for line tracking at .method( line |
| 8478 | self.set_source_range(attr.range()); |
| 8479 | emit!(self, Instruction::Nop); |
| 8480 | // CALL at .method( line (not the full expression line) |
| 8481 | self.codegen_call_helper(0, args, attr.range())?; |
| 8482 | } else { |
| 8483 | self.compile_expression(value)?; |
| 8484 | let idx = self.name(attr.as_str()); |
| 8485 | // Imported names and CALL_FUNCTION_EX-style calls use plain |
| 8486 | // LOAD_ATTR + PUSH_NULL; other names use method-call mode. |
| 8487 | // Check current scope and enclosing scopes for IMPORTED flag. |
| 8488 | let is_import = matches!(value.as_ref(), ast::Expr::Name(ast::ExprName { id, .. }) |
| 8489 | if self.is_name_imported(id.as_str())); |
| 8490 | if is_import || uses_ex_call { |
| 8491 | self.emit_load_attr(idx); |
| 8492 | emit!(self, Instruction::PushNull); |
| 8493 | } else { |
| 8494 | self.emit_load_attr_method(idx); |
| 8495 | } |
| 8496 | self.codegen_call_helper(0, args, call_range)?; |
| 8497 | } |
| 8498 | } else if let Some(kind) = (!uses_ex_call) |
| 8499 | .then(|| self.detect_builtin_generator_call(func, args)) |
| 8500 | .flatten() |
| 8501 | { |
| 8502 | // Optimized builtin(genexpr) path: compile the genexpr only once |
| 8503 | // so its code object appears exactly once in co_consts. |
| 8504 | let end = self.new_block(); |
| 8505 | self.compile_expression(func)?; |
no test coverage detected