Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 169 additions & 82 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,10 @@ rustpython-sre_engine = { path = "vm/sre_engine", version = "0.4.0" }
rustpython-wtf8 = { path = "wtf8", version = "0.4.0" }
rustpython-doc = { git = "https://github.com/RustPython/__doc__", tag = "0.3.0", version = "0.3.0" }

ruff_python_parser = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
ruff_python_ast = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
ruff_text_size = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
ruff_source_file = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
ruff_python_parser = { git = "https://github.com/astral-sh/ruff.git", tag = "0.13.1" }
ruff_python_ast = { git = "https://github.com/astral-sh/ruff.git", tag = "0.13.1" }
ruff_text_size = { git = "https://github.com/astral-sh/ruff.git", tag = "0.13.1" }
ruff_source_file = { git = "https://github.com/astral-sh/ruff.git", tag = "0.13.1" }

ahash = "0.8.12"
ascii = "1.1"
Expand Down
179 changes: 109 additions & 70 deletions compiler/codegen/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ use ruff_python_ast::{
Alias, Arguments, BoolOp, CmpOp, Comprehension, ConversionFlag, DebugText, Decorator, DictItem,
ExceptHandler, ExceptHandlerExceptHandler, Expr, ExprAttribute, ExprBoolOp, ExprContext,
ExprFString, ExprList, ExprName, ExprSlice, ExprStarred, ExprSubscript, ExprTuple, ExprUnaryOp,
FString, FStringElement, FStringElements, FStringFlags, FStringPart, Identifier, Int, Keyword,
MatchCase, ModExpression, ModModule, Operator, Parameters, Pattern, PatternMatchAs,
PatternMatchClass, PatternMatchMapping, PatternMatchOr, PatternMatchSequence,
PatternMatchSingleton, PatternMatchStar, PatternMatchValue, Singleton, Stmt, StmtExpr,
TypeParam, TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple, TypeParams, UnaryOp,
WithItem,
FString, FStringFlags, FStringPart, Identifier, Int, InterpolatedElement,
InterpolatedStringElement, InterpolatedStringElements, Keyword, MatchCase, ModExpression,
ModModule, Operator, Parameters, Pattern, PatternMatchAs, PatternMatchClass,
PatternMatchMapping, PatternMatchOr, PatternMatchSequence, PatternMatchSingleton,
PatternMatchStar, PatternMatchValue, Singleton, Stmt, StmtExpr, TypeParam, TypeParamParamSpec,
TypeParamTypeVar, TypeParamTypeVarTuple, TypeParams, UnaryOp, WithItem,
};
use ruff_text_size::{Ranged, TextRange};
use rustpython_compiler_core::{
Mode, OneIndexed, SourceFile, SourceLocation,
Mode, OneIndexed, PositionEncoding, SourceFile, SourceLocation,
bytecode::{
self, Arg as OpArgMarker, BinaryOperator, CodeObject, ComparisonOperator, ConstantData,
Instruction, OpArg, OpArgType, UnpackExArgs,
Expand Down Expand Up @@ -240,18 +240,18 @@ fn eprint_location(zelf: &Compiler) {
let start = zelf
.source_file
.to_source_code()
.source_location(zelf.current_source_range.start());
.source_location(zelf.current_source_range.start(), PositionEncoding::Utf8);
let end = zelf
.source_file
.to_source_code()
.source_location(zelf.current_source_range.end());
.source_location(zelf.current_source_range.end(), PositionEncoding::Utf8);
eprintln!(
"LOCATION: {} from {}:{} to {}:{}",
zelf.source_file.name(),
start.row,
start.column,
end.row,
end.column
start.line,
start.character_offset,
end.line,
end.character_offset
);
}

Expand Down Expand Up @@ -531,7 +531,7 @@ impl Compiler {
let location = self
.source_file
.to_source_code()
.source_location(range.start());
.source_location(range.start(), PositionEncoding::Utf8);
CodegenError {
error,
location: Some(location),
Expand Down Expand Up @@ -631,8 +631,8 @@ impl Compiler {
) -> CompileResult<()> {
// Create location
let location = SourceLocation {
row: OneIndexed::new(lineno as usize).unwrap_or(OneIndexed::MIN),
column: OneIndexed::new(1).unwrap(),
line: OneIndexed::new(lineno as usize).unwrap_or(OneIndexed::MIN),
character_offset: OneIndexed::MIN,
};

// Allocate a new compiler unit
Expand Down Expand Up @@ -769,8 +769,8 @@ impl Compiler {
let _resume_loc = if scope_type == CompilerScope::Module {
// Module scope starts with lineno 0
SourceLocation {
row: OneIndexed::MIN,
column: OneIndexed::MIN,
line: OneIndexed::MIN,
character_offset: OneIndexed::MIN,
}
} else {
location
Expand Down Expand Up @@ -4872,6 +4872,7 @@ impl Compiler {
Expr::Named(ExprNamed {
target,
value,
node_index: _,
range: _,
}) => {
self.compile_expression(value)?;
Expand All @@ -4881,6 +4882,9 @@ impl Compiler {
Expr::FString(fstring) => {
self.compile_expr_fstring(fstring)?;
}
Expr::TString(_) => {
return Err(self.error(CodegenErrorType::NotImplementedYet));
}
Comment on lines +4885 to +4887
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

TString not compiled (returns NotImplementedYet)

The parser can produce Expr::TString; this path will error during compilation. Either implement TString codegen (ideally reusing compile_fstring_elements with the new Interpolated* types) or gate it behind a feature/parse guard until supported.

Run to see where TString appears:


🏁 Script executed:

#!/bin/bash
rg -nP -C2 '\bTString\b' --compiler rust

Length of output: 435


🏁 Script executed:

#!/bin/bash
# Locate all occurrences of `TString` in the codebase
rg -nP '\bTString\b' -C2

Length of output: 2596


Add codegen or gate parsing for Expr::TString
Expr::TString(_) in compiler/codegen/src/compile.rs (lines 4885–4887) still returns NotImplementedYet. Support for t-strings is unimplemented across codegen, symbol table, and stdlib. Either implement codegen by reusing compile_expr_fstring on tstring.value.elements(), or gate its parsing behind a feature flag.

🤖 Prompt for AI Agents
In compiler/codegen/src/compile.rs around lines 4885–4887, Expr::TString(_)
currently returns NotImplementedYet; implement codegen for t-strings by
extracting the tstring value elements and calling the existing
compile_expr_fstring logic (e.g., pass tstring.value.elements() into
compile_expr_fstring and wire the returned IR/value through the same path as
f-strings), and ensure corresponding symbol-table entries and stdlib support are
present or updated; alternatively, if you don't want runtime support yet, gate
the parsing by adding a feature flag so TString nodes are not produced unless
the feature is enabled (update parser/AST creation and any callers accordingly)
and keep the NotImplemented error for the disabled case.

Expr::StringLiteral(string) => {
let value = string.value.to_str();
if value.contains(char::REPLACEMENT_CHARACTER) {
Expand Down Expand Up @@ -5334,7 +5338,7 @@ impl Compiler {
let location = self
.source_file
.to_source_code()
.source_location(range.start());
.source_location(range.start(), PositionEncoding::Utf8);
// TODO: insert source filename
self.current_block().instructions.push(ir::InstructionInfo {
instr,
Expand Down Expand Up @@ -5524,27 +5528,14 @@ impl Compiler {
Expr::Named(ExprNamed {
target,
value,
node_index: _,
range: _,
}) => Self::contains_await(target) || Self::contains_await(value),
Expr::FString(ExprFString { value, range: _ }) => {
fn expr_element_contains_await<F: Copy + Fn(&Expr) -> bool>(
expr_element: &FStringExpressionElement,
contains_await: F,
) -> bool {
contains_await(&expr_element.expression)
|| expr_element
.format_spec
.iter()
.flat_map(|spec| spec.elements.expressions())
.any(|element| expr_element_contains_await(element, contains_await))
}

value.elements().any(|element| match element {
FStringElement::Expression(expr_element) => {
expr_element_contains_await(expr_element, Self::contains_await)
}
FStringElement::Literal(_) => false,
})
Expr::FString(fstring) => {
Self::interpolated_string_contains_await(fstring.value.elements())
}
Expr::TString(tstring) => {
Self::interpolated_string_contains_await(tstring.value.elements())
}
Expr::StringLiteral(_)
| Expr::BytesLiteral(_)
Expand All @@ -5556,6 +5547,29 @@ impl Compiler {
}
}

fn interpolated_string_contains_await<'a>(
mut elements: impl Iterator<Item = &'a InterpolatedStringElement>,
) -> bool {
fn interpolated_element_contains_await<F: Copy + Fn(&Expr) -> bool>(
expr_element: &InterpolatedElement,
contains_await: F,
) -> bool {
contains_await(&expr_element.expression)
|| expr_element
.format_spec
.iter()
.flat_map(|spec| spec.elements.interpolations())
.any(|element| interpolated_element_contains_await(element, contains_await))
}

elements.any(|element| match element {
InterpolatedStringElement::Interpolation(expr_element) => {
interpolated_element_contains_await(expr_element, Self::contains_await)
}
InterpolatedStringElement::Literal(_) => false,
})
}

fn compile_expr_fstring(&mut self, fstring: &ExprFString) -> CompileResult<()> {
let fstring = &fstring.value;
for part in fstring {
Expand Down Expand Up @@ -5602,13 +5616,13 @@ impl Compiler {
fn compile_fstring_elements(
&mut self,
flags: FStringFlags,
fstring_elements: &FStringElements,
fstring_elements: &InterpolatedStringElements,
) -> CompileResult<()> {
let mut element_count = 0;
for element in fstring_elements {
element_count += 1;
match element {
FStringElement::Literal(string) => {
InterpolatedStringElement::Literal(string) => {
if string.value.contains(char::REPLACEMENT_CHARACTER) {
// might have a surrogate literal; should reparse to be sure
let source = self.source_file.slice(string.range);
Expand All @@ -5625,7 +5639,7 @@ impl Compiler {
});
}
}
FStringElement::Expression(fstring_expr) => {
InterpolatedStringElement::Interpolation(fstring_expr) => {
let mut conversion = fstring_expr.conversion;

if let Some(DebugText { leading, trailing }) = &fstring_expr.debug_text {
Expand Down Expand Up @@ -5858,21 +5872,27 @@ mod ruff_tests {

// f'{x}'
let expr_x = Expr::Name(ExprName {
node_index: AtomicNodeIndex::NONE,
range,
id: Name::new("x"),
ctx: ExprContext::Load,
});
let not_present = &Expr::FString(ExprFString {
node_index: AtomicNodeIndex::NONE,
range,
value: FStringValue::single(FString {
node_index: AtomicNodeIndex::NONE,
range,
elements: vec![FStringElement::Expression(FStringExpressionElement {
range,
expression: Box::new(expr_x),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: None,
})]
elements: vec![InterpolatedStringElement::Interpolation(
InterpolatedElement {
node_index: AtomicNodeIndex::NONE,
range,
expression: Box::new(expr_x),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: None,
},
)]
.into(),
flags,
}),
Expand All @@ -5881,24 +5901,31 @@ mod ruff_tests {

// f'{await x}'
let expr_await_x = Expr::Await(ExprAwait {
node_index: AtomicNodeIndex::NONE,
range,
value: Box::new(Expr::Name(ExprName {
node_index: AtomicNodeIndex::NONE,
range,
id: Name::new("x"),
ctx: ExprContext::Load,
})),
});
let present = &Expr::FString(ExprFString {
node_index: AtomicNodeIndex::NONE,
range,
value: FStringValue::single(FString {
node_index: AtomicNodeIndex::NONE,
range,
elements: vec![FStringElement::Expression(FStringExpressionElement {
range,
expression: Box::new(expr_await_x),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: None,
})]
elements: vec![InterpolatedStringElement::Interpolation(
InterpolatedElement {
node_index: AtomicNodeIndex::NONE,
range,
expression: Box::new(expr_await_x),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: None,
},
)]
.into(),
flags,
}),
Expand All @@ -5907,39 +5934,51 @@ mod ruff_tests {

// f'{x:{await y}}'
let expr_x = Expr::Name(ExprName {
node_index: AtomicNodeIndex::NONE,
range,
id: Name::new("x"),
ctx: ExprContext::Load,
});
let expr_await_y = Expr::Await(ExprAwait {
node_index: AtomicNodeIndex::NONE,
range,
value: Box::new(Expr::Name(ExprName {
node_index: AtomicNodeIndex::NONE,
range,
id: Name::new("y"),
ctx: ExprContext::Load,
})),
});
let present = &Expr::FString(ExprFString {
node_index: AtomicNodeIndex::NONE,
range,
value: FStringValue::single(FString {
node_index: AtomicNodeIndex::NONE,
range,
elements: vec![FStringElement::Expression(FStringExpressionElement {
range,
expression: Box::new(expr_x),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: Some(Box::new(FStringFormatSpec {
elements: vec![InterpolatedStringElement::Interpolation(
InterpolatedElement {
node_index: AtomicNodeIndex::NONE,
range,
elements: vec![FStringElement::Expression(FStringExpressionElement {
expression: Box::new(expr_x),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: Some(Box::new(InterpolatedStringFormatSpec {
node_index: AtomicNodeIndex::NONE,
range,
expression: Box::new(expr_await_y),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: None,
})]
.into(),
})),
})]
elements: vec![InterpolatedStringElement::Interpolation(
InterpolatedElement {
node_index: AtomicNodeIndex::NONE,
range,
expression: Box::new(expr_await_y),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: None,
},
)]
.into(),
})),
},
)]
.into(),
flags,
}),
Expand Down
6 changes: 3 additions & 3 deletions compiler/codegen/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl CodeInfo {
*arg = new_arg;
}
let (extras, lo_arg) = arg.split();
locations.extend(std::iter::repeat_n(info.location.clone(), arg.instr_size()));
locations.extend(std::iter::repeat_n(info.location, arg.instr_size()));
instructions.extend(
extras
.map(|byte| CodeUnit::new(Instruction::ExtendedArg, byte))
Expand Down Expand Up @@ -422,8 +422,8 @@ fn generate_linetable(locations: &[SourceLocation], first_line: i32) -> Box<[u8]

// Get line and column information
// SourceLocation always has row and column (both are OneIndexed)
let line = loc.row.get() as i32;
let col = (loc.column.get() as i32) - 1; // Convert 1-based to 0-based
let line = loc.line.get() as i32;
let col = loc.character_offset.to_zero_indexed() as i32;

let line_delta = line - prev_line;

Expand Down
1 change: 1 addition & 0 deletions compiler/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ impl ToPythonName for Expr {
Self::Starred { .. } => "starred",
Self::Slice { .. } => "slice",
Self::FString { .. } => "f-string expression",
Self::TString { .. } => "t-string expression",
Self::Name { .. } => "name",
Self::Lambda { .. } => "lambda",
Self::If { .. } => "conditional expression",
Expand Down
Loading
Loading