Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Traits overlapping #1601

Open
wants to merge 79 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
3a33d6e
Impl parsing
gzanitti Jun 28, 2024
3ede469
Format error
gzanitti Jun 28, 2024
693c011
Conflicts solved
gzanitti Jul 9, 2024
afb17ad
Missed conflict
gzanitti Jul 9, 2024
6e05d81
Missed conflict
gzanitti Jul 9, 2024
b7dfeb2
Match fixed
gzanitti Jul 9, 2024
2fe457b
Test
gzanitti Jul 9, 2024
88fab39
Grammar improvements
gzanitti Jul 10, 2024
7817c69
Merge remote-tracking branch 'upstream/main' into impl_parsing
gzanitti Jul 10, 2024
c41a89c
Types in grammar based on PR #1545
gzanitti Jul 10, 2024
049226b
Update ast/src/parsed/mod.rs
gzanitti Jul 15, 2024
1a91bdb
Minor fixes
gzanitti Jul 15, 2024
e3df4ab
Merge remote-tracking branch 'upstream/main' into impl_parsing
gzanitti Jul 19, 2024
ec70fe8
Basic overlap
gzanitti Jul 19, 2024
58b5a2f
Trait overlapping and named types
gzanitti Jul 24, 2024
545aef6
Conflict solved
gzanitti Jul 24, 2024
b4949a9
Error msg
gzanitti Jul 24, 2024
ccd8ae6
Error msg
gzanitti Jul 24, 2024
287f9b1
More checks & better error reporting
gzanitti Jul 24, 2024
955f1d1
Minor fix
gzanitti Jul 24, 2024
fcce19a
Merge remote-tracking branch 'upstream/main' into check_traits_overla…
gzanitti Jul 24, 2024
5e10258
WIP
gzanitti Jul 25, 2024
f1cb39f
clippy and minor fix
gzanitti Jul 25, 2024
e497f2f
Useless test removed
gzanitti Jul 25, 2024
48fa76a
More tests and minor fix (clippy)
gzanitti Jul 25, 2024
b9ff12d
More tests before impl unification
gzanitti Jul 25, 2024
5fe17da
One more test
gzanitti Jul 25, 2024
02df478
Impls handling
gzanitti Jul 25, 2024
9c027c3
Merge remote-tracking branch 'upstream/main' into check_traits_overla…
gzanitti Jul 25, 2024
dedd1dd
WIP
gzanitti Jul 25, 2024
ba32d7c
Statements & conflicts
gzanitti Jul 26, 2024
fcab5b2
Extra dot removed
gzanitti Jul 26, 2024
37a01c0
Wrong test removed
gzanitti Jul 27, 2024
2a8dd2c
Merge remote-tracking branch 'upstream/main' into check_traits_overla…
gzanitti Jul 29, 2024
22cba1f
PR updated & improved
gzanitti Jul 30, 2024
cceeee9
conflict and tests
gzanitti Jul 30, 2024
1344477
removed duplicated rule
gzanitti Jul 30, 2024
940a34a
Better error
gzanitti Jul 30, 2024
943d7eb
Cleaner code
gzanitti Jul 30, 2024
a793542
Remove comments, clean code, etc
gzanitti Jul 30, 2024
dfbe0e4
Improved code organization. Documentation
gzanitti Jul 30, 2024
3a26f73
docs
gzanitti Jul 30, 2024
05bd031
Merge remote-tracking branch 'upstream/main' into check_traits_overla…
gzanitti Jul 30, 2024
dc86999
Fix grammar
gzanitti Jul 30, 2024
b2c78ca
Fix grammar
gzanitti Jul 30, 2024
a5461e1
space remove
gzanitti Jul 30, 2024
19e5df4
moved out of PILAnalyzer
gzanitti Jul 30, 2024
3a5891d
improved test
gzanitti Jul 30, 2024
c6c8854
new test: trait_with_user_defined_enum
gzanitti Jul 30, 2024
82a4bb2
new test: trait_with_user_defined_enum2
gzanitti Jul 30, 2024
088f631
Improvements and documentation
gzanitti Jul 31, 2024
b8e97ee
TypeChecker replaced by Unifier
gzanitti Jul 31, 2024
1b8d558
Minor code change
gzanitti Jul 31, 2024
2c0be40
rollback change
gzanitti Jul 31, 2024
b175ff3
type var check & test
gzanitti Jul 31, 2024
a526a2f
vars inside format
gzanitti Jul 31, 2024
1603cf6
Code improvements
gzanitti Aug 5, 2024
ee5f204
Tests removed & minor changes
gzanitti Aug 5, 2024
4140e18
unused_type_var_error fixed
gzanitti Aug 5, 2024
cc99969
Tests with TypeVarManager (temporal)
gzanitti Aug 5, 2024
bf042b1
Overlapping of TypeVars fixed. Need to fix TypeVarManager counter/con…
gzanitti Aug 5, 2024
c2f9fd0
TypeVarManager
gzanitti Aug 5, 2024
2bba728
TypeVarManager with Unifier
gzanitti Aug 5, 2024
be35795
TypeVarManager functionality moved to Unifier
gzanitti Aug 6, 2024
ebb3139
Main merged
gzanitti Aug 6, 2024
3a5532e
Minor changes
gzanitti Aug 7, 2024
9cb49db
fn -> pub fn
gzanitti Aug 7, 2024
7d181a6
unifier simplification
gzanitti Aug 7, 2024
2961171
Update pil-analyzer/src/pil_analyzer.rs
gzanitti Aug 8, 2024
58b4c0b
Update pil-analyzer/src/pil_analyzer.rs
gzanitti Aug 8, 2024
afb6c1a
Cleaner version
gzanitti Aug 8, 2024
064d340
General fixes
gzanitti Aug 8, 2024
35e1fad
Clippy
gzanitti Aug 8, 2024
6ef5fe5
Temporal error fix
gzanitti Aug 8, 2024
2b8d470
Main merged
gzanitti Aug 26, 2024
e692364
Improved code
gzanitti Aug 26, 2024
62923b0
Test fixed
gzanitti Aug 26, 2024
1b5beee
Minor fixes
gzanitti Aug 28, 2024
df6e98b
conflict
gzanitti Sep 9, 2024
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
1 change: 1 addition & 0 deletions ast/src/parsed/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ impl<R> Children<Expression<R>> for EnumVariant<Expression<R>> {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize, JsonSchema)]
pub struct TraitImplementation<Expr> {
pub name: SymbolPath,
pub source_ref: SourceRef,
pub type_scheme: TypeScheme,
pub functions: Vec<NamedExpression<Expr>>,
}
Expand Down
6 changes: 3 additions & 3 deletions parser/src/powdr.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ TraitFunction: TraitFunction<Expression> = {
}

TraitImplementation: TraitImplementation<Expression> = {
"impl" <type_scheme: GenericTraitName> "{" <functions:NamedExpressions> "}" => TraitImplementation { name: type_scheme.0, type_scheme: type_scheme.1, functions }
<start:@L> "impl" <type_scheme: GenericTraitName> "{" <functions:NamedExpressions> "}" <end:@L> => TraitImplementation { name: type_scheme.0, source_ref: ctx.source_ref(start, end), type_scheme: type_scheme.1, functions }
}

NamedExpressions: Vec<NamedExpression<Expression>> = {
Expand All @@ -760,8 +760,8 @@ NamedExpression: NamedExpression<Expression> = {
}

GenericTraitName: (SymbolPath, TypeScheme) = {
<vars:("<" <TypeVarBounds> ">")> <name:SymbolPath> <items:("<" <TypeTermList<ArrayLengthNumber>> ">")> =>
(name, TypeScheme{ vars, ty: Type::Tuple(TupleType{items}) })
<vars:("<" <TypeVarBounds> ">")?> <name:SymbolPath> <items:("<" <TypeTermList<ArrayLengthNumber>> ">")> =>
(name, TypeScheme{ vars: vars.unwrap_or_default(), ty: Type::Tuple(TupleType{items}) })
}


Expand Down
1 change: 1 addition & 0 deletions pil-analyzer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod expression_processor;
mod pil_analyzer;
mod side_effect_checker;
mod statement_processor;
mod traits_resolver;
mod type_builtins;
mod type_inference;
mod type_processor;
Expand Down
15 changes: 14 additions & 1 deletion pil-analyzer/src/pil_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use powdr_ast::parsed::types::{ArrayType, Type};
use powdr_ast::parsed::visitor::Children;
use powdr_ast::parsed::{
self, FunctionKind, LambdaExpression, PILFile, PilStatement, SelectedExpressions,
SymbolCategory,
SymbolCategory, TraitImplementation,
};
use powdr_number::{FieldElement, GoldilocksField};

Expand All @@ -23,6 +23,7 @@ use powdr_ast::analyzed::{
};
use powdr_parser::{parse, parse_module, parse_type};

use crate::traits_resolver::traits_unification;
use crate::type_builtins::constr_function_statement_type;
use crate::type_inference::infer_types;
use crate::{side_effect_checker, AnalysisDriver};
Expand Down Expand Up @@ -52,6 +53,7 @@ fn analyze<T: FieldElement>(files: Vec<PILFile>) -> Analyzed<T> {
let mut analyzer = PILAnalyzer::new();
analyzer.process(files);
analyzer.side_effect_check();
analyzer.check_traits_overlap();
analyzer.type_check();
analyzer.condense()
}
Expand All @@ -72,6 +74,8 @@ struct PILAnalyzer {
symbol_counters: Option<Counters>,
/// Symbols from the core that were added automatically but will not be printed.
auto_added_symbols: HashSet<String>,
/// Trait implementations found, organized according to their associated trait name.
implementations: HashMap<String, Vec<TraitImplementation<Expression>>>,
}

/// Reads and parses the given path and all its imports.
Expand Down Expand Up @@ -227,6 +231,10 @@ impl PILAnalyzer {
}
}

pub fn check_traits_overlap(&mut self) {
traits_unification(&self.definitions, &mut self.implementations);
}

pub fn type_check(&mut self) {
let query_type: Type = parse_type("int -> std::prelude::Query").unwrap().into();
let mut expressions = vec![];
Expand Down Expand Up @@ -409,6 +417,11 @@ impl PILAnalyzer {
self.source_order.push(StatementIdentifier::Identity(index));
self.identities.push(identity)
}
PILItem::TraitImplementation(trait_impl) => self
.implementations
.entry(trait_impl.name.to_string())
.or_default()
.push(trait_impl),
}
}
}
Expand Down
67 changes: 66 additions & 1 deletion pil-analyzer/src/statement_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ use std::sync::Arc;
use itertools::Itertools;

use powdr_ast::analyzed::{DegreeRange, TypedExpression};
use powdr_ast::parsed::asm::{Part, SymbolPath};
use powdr_ast::parsed::{
self,
types::{ArrayType, Type, TypeScheme},
types::{ArrayType, TupleType, Type, TypeScheme},
ArrayLiteral, EnumDeclaration, EnumVariant, FunctionDefinition, FunctionKind, LambdaExpression,
PilStatement, PolynomialName, SelectedExpressions, TraitDeclaration, TraitFunction,
};
use powdr_ast::parsed::{NamedExpression, SymbolCategory, TraitImplementation};

use powdr_parser_util::SourceRef;

Expand All @@ -28,6 +30,7 @@ pub enum PILItem {
Definition(Symbol, Option<FunctionValueDefinition>),
PublicDeclaration(PublicDeclaration),
Identity(Identity<SelectedExpressions<Expression>>),
TraitImplementation(TraitImplementation<Expression>),
}

pub struct Counters {
Expand Down Expand Up @@ -201,6 +204,10 @@ where
None,
Some(FunctionDefinition::TraitDeclaration(trait_decl.clone())),
),
PilStatement::TraitImplementation(_, trait_impl) => {
let trait_impl = self.process_trait_implementation(trait_impl);
vec![PILItem::TraitImplementation(trait_impl)]
}
_ => self.handle_identity_statement(statement),
}
}
Expand Down Expand Up @@ -643,4 +650,62 @@ where
functions,
}
}

fn process_trait_implementation(
&self,
trait_impl: parsed::TraitImplementation<parsed::Expression>,
) -> TraitImplementation<Expression> {
let type_vars = trait_impl.type_scheme.vars.vars().collect();
let functions = trait_impl
.functions
.into_iter()
.map(|named| NamedExpression {
name: named.name,
body: Box::new(
self.expression_processor(&type_vars)
.process_expression(named.body.as_ref().clone()),
),
})
.collect();

let Type::Tuple(TupleType { items }) = trait_impl.type_scheme.ty.clone() else {
panic!("Type from trait scheme is not a tuple.")
};

let mapped_types: Vec<_> = items
.into_iter()
.map(|mut ty| {
ty.map_to_type_vars(&type_vars);
ty
})
.collect();

let resolved_name = self
.driver
.resolve_ref(&trait_impl.name, SymbolCategory::TraitDeclaration);

TraitImplementation {
name: SymbolPath::from_parts(
// TODO GZ: Should be a better way to do this.
resolved_name
.split('.')
.map(|p| {
if p == "super" {
Part::Super
} else {
Part::Named(p.to_string())
}
})
.collect::<Vec<_>>(),
),
source_ref: trait_impl.source_ref,
type_scheme: TypeScheme {
vars: trait_impl.type_scheme.vars,
ty: Type::Tuple(TupleType {
items: mapped_types,
}),
},
functions,
}
}
}
153 changes: 153 additions & 0 deletions pil-analyzer/src/traits_resolver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use std::collections::{HashMap, HashSet};

use powdr_ast::{
analyzed::{Expression, FunctionValueDefinition, Symbol},
parsed::{
types::{TupleType, Type, TypeScheme},
TraitDeclaration, TraitImplementation,
},
};

use crate::type_unifier::Unifier;

pub struct TraitsResolver<'a> {
definitions: &'a HashMap<String, (Symbol, Option<FunctionValueDefinition>)>,
}

impl<'a> TraitsResolver<'a> {
pub fn new(
definitions: &'a HashMap<String, (Symbol, Option<FunctionValueDefinition>)>,
) -> Self {
Self { definitions }
}

/// Checks for overlapping trait implementations.
///
/// This method iterates through all the trait implementations.
/// For each implementation, it checks that there are no traits with the same name and overlapping type variables.
/// Overlapping implementations can lead to ambiguity in trait function calls, even when all types
/// are fully concrete. This check helps prevent such ambiguities and ensures clear resolution
/// of trait function calls.
///
/// It also checks that the number of type variables in the implementation matches
/// the number of type variables in the corresponding trait declaration.
pub fn validate_trait_implementations(
&self,
implementations: &mut HashMap<String, Vec<TraitImplementation<Expression>>>,
) {
for (trait_name, trait_impls) in implementations.iter_mut() {
let trait_decl = self
.definitions
.get(&trait_name.clone())
.unwrap_or_else(|| panic!("Trait {trait_name} not found"))
.1
.as_ref()
.unwrap_or_else(|| panic!("Trait definition for {trait_name} not found"));

let FunctionValueDefinition::TraitDeclaration(trait_decl) = trait_decl else {
panic!("Invalid trait declaration");
};

self.validate_impl_definitions(trait_impls, trait_decl);
self.ensure_unique_impls(trait_impls);
}
}

/// Validates the trait implementation definitions in the given `implementations` map against the trait
/// declarations in the `definitions` map.
fn validate_impl_definitions(
&self,
implementations: &[TraitImplementation<Expression>],
trait_decl: &TraitDeclaration,
) {
for trait_impl in implementations {
let Type::Tuple(TupleType { items: mut types }) = trait_impl.type_scheme.ty.clone()
else {
panic!("Type from trait scheme is not a tuple.")
};
let trait_name = trait_impl.name.clone();

if types.len() != trait_decl.type_vars.len() {
panic!(
"{}",
trait_impl.source_ref.with_error(format!(
"Trait {} has {} type parameters, but implementation has {}",
trait_name,
trait_decl.type_vars.len(),
types.len(),
))
);
}

let type_vars_in_tuple: Vec<_> = types
.iter_mut()
.flat_map(|t| t.contained_type_vars())
.collect();

let type_vars_in_scheme: Vec<_> = trait_impl.type_scheme.vars.vars().collect();

for var in type_vars_in_scheme {
if !type_vars_in_tuple.contains(&var) {
panic!(
"{}",
trait_impl.source_ref.with_error(format!(
"Impl {trait_name} introduces a type variable {var} that is not used",
))
);
}
}
}
}

/// Ensures that there are no overlapping trait implementations in the given `implementations` map.
///
/// This function iterates through all the trait implementations comparing them with each other and ensuring that
/// there are no traits with overlapping type variables.
fn ensure_unique_impls(&self, implementations: &mut [TraitImplementation<Expression>]) {
for i in 0..implementations.len() {
let type_vars: HashSet<_> = implementations[i].type_scheme.vars.vars().collect();
implementations[i]
.type_scheme
.ty
.map_to_type_vars(&type_vars);

for j in (i + 1)..implementations.len() {
let type_vars: HashSet<_> = implementations[j].type_scheme.vars.vars().collect();
implementations[j]
.type_scheme
.ty
.map_to_type_vars(&type_vars);

self.unify_traits_types(
&implementations[i].type_scheme,
&implementations[j].type_scheme,
)
.map_err(|err| {
implementations[i]
.source_ref
.with_error(format!("Impls for {}: {err}", implementations[i].name))
})
.unwrap()
}
}
}

fn unify_traits_types(&self, ty1: &TypeScheme, ty2: &TypeScheme) -> Result<(), String> {
let mut unifier = Unifier::new();
let instantiated_ty1 = unifier.instantiate_scheme(ty1.clone());
let instantiated_ty2 = unifier.instantiate_scheme(ty2.clone());

match unifier.unify_types(instantiated_ty1.0.clone(), instantiated_ty2.0.clone()) {
Ok(_) => Err(format!("Types {} and {} overlap", ty1.ty, ty2.ty)),
Err(_) => Ok(()),
}
}
}

pub fn traits_unification(
definitions: &HashMap<String, (Symbol, Option<FunctionValueDefinition>)>,
//identities: &Vec<Identity<SelectedExpressions<Expression>>>, // TODO: To be added
trait_impls: &mut HashMap<String, Vec<TraitImplementation<Expression>>>,
) {
TraitsResolver::new(definitions).validate_trait_implementations(trait_impls);
}
8 changes: 8 additions & 0 deletions pil-analyzer/src/type_unifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ pub struct Unifier {
}

impl Unifier {
pub fn new() -> Self {
Self {
type_var_bounds: HashMap::new(),
substitutions: HashMap::new(),
last_type_var: 0,
}
}

pub fn type_var_bounds(&self, type_var: &String) -> HashSet<String> {
self.type_var_bounds
.get(type_var)
Expand Down
Loading