libadalang fails to resolve quoted predefined "xor" for modular types (infix works)
Nobody has claimed this yet.
- Dominant language
- Ada
- Stars
- 171
- Forks
- 48
- PR merge metrics
- No merged PRs in 30d
Description
Issue
For the script below, libadalang resolves infix modular xor (<modular> xor <modular>) but fails to resolve the quoted call "xor"(<modular>, <modular>) unless an explicit renaming is provided (e.g., function "xor" (Left, Right : Bitty) return Bitty is (Left xor Right);).
For Boolean operands, libadalang resolves the quoted "xor" without any renaming.
GNAT compiles both quoted and infix forms for modular types without an explicit renaming.
Is this a libadalang limitation (predefined modular operators not considered in quoted-call resolution), or am I misusing the API?
Environment: libadalang-25.0.0-9.50.x86_64 libadalang 26.0 on openSUSE.
Reproducer
#!/usr/bin/env python3
"""
Minimal LAL repro:
- Boolean: quoted "xor" resolves (Standard."xor" is visible).
- Modular: infix xor resolves, quoted "xor" does not (implicit predefined op).
- Modular with explicit renaming: quoted "xor" resolves.
Ada sources (written to a temp dir):
-- test_types.ads
-- package Test_Types is
-- type Bitty is mod 256;
-- Left_Val : constant Bitty := 45;
-- Right_Val : constant Bitty := 38;
-- Bool_L : constant Boolean := True;
-- Bool_R : constant Boolean := False;
--
-- -- Uncommented in the "with_renaming" variant below:
-- -- function "xor" (Left, Right : Bitty) return Bitty is (Left xor Right);
-- end Test_Types;
--
-- test_ops.adb
-- with Test_Types; use Test_Types;
-- procedure Test_Ops is
-- Infix_Result : Bitty;
-- Quoted_Result : Bitty;
-- Bool_Infix : Boolean;
-- Bool_Quoted : Boolean;
-- begin
-- Infix_Result := Left_Val xor Right_Val;
-- Quoted_Result := "xor" (Left_Val, Right_Val);
-- Bool_Infix := Bool_L xor Bool_R;
-- Bool_Quoted := "xor" (Bool_L, Bool_R);
-- end Test_Ops;
--
-- Expected behavior (GNAT): both compile.
-- Libadalang: infix resolves; quoted "xor" fails unless explicit renaming.
-- p_nameres_diagnostics shows LAL only trying Standard."xor" overloads.
--
-- Requires: pip install libadalang, GNAT in PATH for gnatls.
"""
import subprocess
import tempfile
from collections.abc import Sequence
from pathlib import Path
import libadalang as lal
def get_gnatls_search_paths() -> list[str]:
"""Return Ada include search paths from gnatls -v."""
result = subprocess.run(
["gnatls", "-v"], capture_output=True, text=True, check=True, timeout=10
)
search_paths: list[str] = []
for line in result.stdout.split("\n") + result.stderr.split("\n"):
if "adainclude" in line.lower():
p = Path(line.strip())
if p.is_absolute() and p.exists():
search_paths.append(str(p))
return search_paths
def get_stdlib_files(search_paths: list[str]) -> list[str]:
"""Collect Ada stdlib .ads files from the given search paths."""
stdlib_files: list[str] = []
for sp in search_paths:
for ext in ["*.ads", "*.ADS", "*.Ads"]:
stdlib_files.extend(str(f) for f in Path(sp).glob(ext))
return stdlib_files
def analyze_variant(label: str, with_renaming: bool) -> None:
"""Analyze a single variant (with or without renaming) and print results."""
ada_types = """\
package Test_Types is
type Bitty is mod 256;
Left_Val : constant Bitty := 45;
Right_Val : constant Bitty := 38;
Bool_L : constant Boolean := True;
Bool_R : constant Boolean := False;
"""
if with_renaming:
ada_types += """\
function "xor" (Left, Right : Bitty) return Bitty is (Left xor Right);
"""
ada_types += """\
end Test_Types;
"""
ada_body = """\
with Test_Types; use Test_Types;
procedure Test_Ops is
Infix_Result : Bitty;
Quoted_Result : Bitty;
Bool_Infix : Boolean;
Bool_Quoted : Boolean;
begin
Infix_Result := Left_Val xor Right_Val;
Quoted_Result := "xor" (Left_Val, Right_Val);
Bool_Infix := Bool_L xor Bool_R;
Bool_Quoted := "xor" (Bool_L, Bool_R);
end Test_Ops;
"""
with tempfile.TemporaryDirectory() as tmpdir:
tmp = Path(tmpdir)
_ = (tmp / "test_types.ads").write_text(ada_types)
_ = (tmp / "test_ops.adb").write_text(ada_body)
search_paths = get_gnatls_search_paths()
stdlib_files = get_stdlib_files(search_paths)
project_files = [str(tmp / "test_types.ads"), str(tmp / "test_ops.adb")]
ctx = lal.AnalysisContext(
unit_provider=lal.UnitProvider.auto(
input_files=project_files + stdlib_files
)
)
unit = ctx.get_from_file(str(tmp / "test_ops.adb"))
print(f"=== {label} ===")
if unit.diagnostics:
print("Parse diagnostics:", unit.diagnostics)
infix_nodes: list[lal.BinOp] = []
quoted_nodes: list[lal.CallExpr] = []
for node in unit.root.findall(lambda _: True):
if isinstance(node, lal.BinOp) and "xor" in node.text:
infix_nodes.append(node)
if isinstance(node, lal.CallExpr) and '"xor"' in node.text:
quoted_nodes.append(node)
def print_diagnostics(diags: Sequence[object]) -> None:
"""Print diagnostics as a list, always including [] when empty."""
if diags:
for d in diags:
print(" ", d)
else:
print(" []")
print("Infix xor:")
if not infix_nodes:
print(" Not found")
else:
for binop in infix_nodes:
print(" text:", binop.text)
print(" expr_type:", binop.p_expression_type)
print(" nameres_diagnostics:")
print_diagnostics(binop.p_nameres_diagnostics)
print('Quoted "xor":')
if not quoted_nodes:
print(" Not found")
else:
for call in quoted_nodes:
print(" text:", call.text)
print(" expr_type:", call.p_expression_type)
print(" nameres_diagnostics:")
print_diagnostics(call.p_nameres_diagnostics)
print()
def main() -> None:
"""Run all variants and print results."""
analyze_variant(
"no renaming (Boolean resolves, modular quoted fails)", with_renaming=False
)
analyze_variant(
"with explicit renaming (modular quoted resolves)", with_renaming=True
)
if __name__ == "__main__":
main()
Output
=== no renaming (Boolean resolves, modular quoted fails) ===
Infix xor:
text: Left_Val xor Right_Val
expr_type: <ConcreteTypeDecl ["Bitty"] test_types.ads:2:4-2:26>
nameres_diagnostics:
[]
text: Bool_L xor Bool_R
expr_type: <ConcreteTypeDecl ["Boolean"] __standard:3:3-3:33>
nameres_diagnostics:
[]
Quoted "xor":
text: "xor" (Left_Val, Right_Val)
expr_type: None
nameres_diagnostics:
<SolverDiagnostic message_template=expected {}, got {} args=[<ConcreteTypeDecl ["Wide_Wide_String"] __standard:109:3-110:44>, <ConcreteTypeDecl ["Bitty"] test_types.ads:2:4-2:26>] location=<Id "Left_Val" test_ops.adb:10:28-10:36> contexts=[<LogicContext ref_node=<Str ""xor"" test_ops.adb:10:21-10:26> decl_node=<SyntheticSubpDecl [""xor""] __standard:109:28-110:43>>] round=1>
<SolverDiagnostic message_template=expected {}, got {} args=[<ConcreteTypeDecl ["Wide_Wide_String"] __standard:109:3-110:44>, <ConcreteTypeDecl ["Bitty"] test_types.ads:2:4-2:26>] location=<Id "Right_Val" test_ops.adb:10:38-10:47> contexts=[<LogicContext ref_node=<Str ""xor"" test_ops.adb:10:21-10:26> decl_node=<SyntheticSubpDecl [""xor""] __standard:109:28-110:43>>] round=1>
<SolverDiagnostic message_template=expected {}, got {} args=[<ConcreteTypeDecl ["Wide_String"] __standard:107:3-108:39>, <ConcreteTypeDecl ["Bitty"] test_types.ads:2:4-2:26>] location=<Id "Left_Val" test_ops.adb:10:28-10:36> contexts=[<LogicContext ref_node=<Str ""xor"" test_ops.adb:10:21-10:26> decl_node=<SyntheticSubpDecl [""xor""] __standard:107:23-108:38>>] round=2>
<SolverDiagnostic message_template=expected {}, got {} args=[<ConcreteTypeDecl ["Wide_String"] __standard:107:3-108:39>, <ConcreteTypeDecl ["Bitty"] test_types.ads:2:4-2:26>] location=<Id "Right_Val" test_ops.adb:10:38-10:47> contexts=[<LogicContext ref_node=<Str ""xor"" test_ops.adb:10:21-10:26> decl_node=<SyntheticSubpDecl [""xor""] __standard:107:23-108:38>>] round=2>
<SolverDiagnostic message_template=expected {}, got {} args=[<ConcreteTypeDecl ["String"] __standard:105:3-105:57>, <ConcreteTypeDecl ["Bitty"] test_types.ads:2:4-2:26>] location=<Id "Left_Val" test_ops.adb:10:28-10:36> contexts=[<LogicContext ref_node=<Str ""xor"" test_ops.adb:10:21-10:26> decl_node=<SyntheticSubpDecl [""xor""] __standard:105:18-105:56>>] round=3>
<SolverDiagnostic message_template=expected {}, got {} args=[<ConcreteTypeDecl ["String"] __standard:105:3-105:57>, <ConcreteTypeDecl ["Bitty"] test_types.ads:2:4-2:26>] location=<Id "Right_Val" test_ops.adb:10:38-10:47> contexts=[<LogicContext ref_node=<Str ""xor"" test_ops.adb:10:21-10:26> decl_node=<SyntheticSubpDecl [""xor""] __standard:105:18-105:56>>] round=3>
<SolverDiagnostic message_template=expected {}, got {} args=[<ConcreteTypeDecl ["Boolean"] __standard:3:3-3:33>, <ConcreteTypeDecl ["Bitty"] test_types.ads:2:4-2:26>] location=<Id "Left_Val" test_ops.adb:10:28-10:36> contexts=[<LogicContext ref_node=<Str ""xor"" test_ops.adb:10:21-10:26> decl_node=<SyntheticSubpDecl [""xor""] __standard:3:19-3:32>>] round=4>
<SolverDiagnostic message_template=expected {}, got {} args=[<ConcreteTypeDecl ["Boolean"] __standard:3:3-3:33>, <ConcreteTypeDecl ["Bitty"] test_types.ads:2:4-2:26>] location=<Id "Right_Val" test_ops.adb:10:38-10:47> contexts=[<LogicContext ref_node=<Str ""xor"" test_ops.adb:10:21-10:26> decl_node=<SyntheticSubpDecl [""xor""] __standard:3:19-3:32>>] round=4>
text: "xor" (Bool_L, Bool_R)
expr_type: <ConcreteTypeDecl ["Boolean"] __standard:3:3-3:33>
nameres_diagnostics:
[]
=== with explicit renaming (modular quoted resolves) ===
Infix xor:
text: Left_Val xor Right_Val
expr_type: <ConcreteTypeDecl ["Bitty"] test_types.ads:2:4-2:26>
nameres_diagnostics:
[]
text: Bool_L xor Bool_R
expr_type: <ConcreteTypeDecl ["Boolean"] __standard:3:3-3:33>
nameres_diagnostics:
[]
Quoted "xor":
text: "xor" (Left_Val, Right_Val)
expr_type: <ConcreteTypeDecl ["Bitty"] test_types.ads:2:4-2:26>
nameres_diagnostics:
[]
text: "xor" (Bool_L, Bool_R)
expr_type: <ConcreteTypeDecl ["Boolean"] __standard:3:3-3:33>
nameres_diagnostics:
[]
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the Python reproducer in the issue, especially the quoted modular "xor" call and its p_nameres_diagnostics output, then inspect the quoted-call name-resolution path. Done means the modular quoted call resolves without an explicit renaming, matching the infix form and the GNAT behavior shown.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100