Skip to content

Commit

Permalink
Move static truthiness to new module
Browse files Browse the repository at this point in the history
  • Loading branch information
sharkdp committed Dec 11, 2024
1 parent 3869741 commit 4ac3737
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 79 deletions.
3 changes: 2 additions & 1 deletion crates/red_knot_python_semantic/src/semantic_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ pub mod symbol;
mod use_def;

pub(crate) use self::use_def::{
BindingWithConstraints, BindingWithConstraintsIterator, DeclarationsIterator,
BindingWithConstraints, BindingWithConstraintsIterator, BranchingConditionsIterator,
DeclarationsIterator,
};

type SymbolMap = hashbrown::HashMap<ScopedSymbolId, (), FxBuildHasher>;
Expand Down
81 changes: 5 additions & 76 deletions crates/red_knot_python_semantic/src/semantic_index/use_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,17 +225,15 @@ use self::symbol_state::{
BindingIdWithConstraintsIterator, ConstraintIdIterator, DeclarationIdIterator,
ScopedConstraintId, ScopedDefinitionId, SymbolBindings, SymbolDeclarations, SymbolState,
};
use crate::semantic_index::ast_ids::{HasScopedExpressionId, ScopedUseId};
use crate::semantic_index::ast_ids::ScopedUseId;
use crate::semantic_index::branching_condition::BranchingCondition;
use crate::semantic_index::constraint::ConstraintNode;
use crate::semantic_index::definition::Definition;
use crate::semantic_index::symbol::ScopedSymbolId;
use crate::semantic_index::use_def::symbol_state::{
BranchingConditionIdIterator, BranchingConditions, ScopedBranchingConditionId,
};
use crate::symbol::Boundness;
use crate::types::{infer_expression_types, Truthiness};
use crate::Db;
use crate::types::StaticTruthiness;
use ruff_index::IndexVec;
use rustc_hash::FxHashMap;

Expand All @@ -246,7 +244,7 @@ mod symbol_state;

fn compute_boundness<'db, 'map, C>(
db: &dyn crate::db::Db,
conditions: C,
conditions_per_binding: C,
may_be_undefined: bool,
) -> Option<Boundness>
where
Expand All @@ -255,8 +253,8 @@ where
{
let mut definitely_bound = false;
let mut definitely_unbound = true;
for condition in conditions {
let result = condition.truthiness(db);
for conditions in conditions_per_binding {
let result = StaticTruthiness::analyze(db, conditions);

if !result.any_always_false {
definitely_unbound = false;
Expand Down Expand Up @@ -483,75 +481,6 @@ impl<'db> Iterator for BranchingConditionsIterator<'_, 'db> {
}
}

pub(crate) struct BranchConditionTruthiness {
pub any_always_false: bool,
pub all_always_true: bool,
pub at_least_one_condition: bool,
}

impl<'db> BranchingConditionsIterator<'_, 'db> {
pub(crate) fn truthiness(self, db: &'db dyn Db) -> BranchConditionTruthiness {
let mut result = BranchConditionTruthiness {
any_always_false: false,
all_always_true: true,
at_least_one_condition: false,
};

for condition in self {
let truthiness = match condition {
BranchingCondition::ConditionalOn(Constraint {
node: ConstraintNode::Expression(test_expr),
is_positive,
}) => {
let inference = infer_expression_types(db, test_expr);
let scope = test_expr.scope(db);
let ty = inference
.expression_ty(test_expr.node_ref(db).scoped_expression_id(db, scope));

ty.bool(db).negate_if(!is_positive)
}
BranchingCondition::ConditionalOn(Constraint {
node: ConstraintNode::Pattern(inner),
..
}) => match inner.kind(db) {
super::constraint::PatternConstraintKind::Value(value) => {
let subject_expression = inner.subject(db);
let inference = infer_expression_types(db, *subject_expression);
let scope = subject_expression.scope(db);
let subject_ty = inference.expression_ty(
subject_expression
.node_ref(db)
.scoped_expression_id(db, scope),
);

let inference = infer_expression_types(db, *value);
let scope = value.scope(db);
let value_ty = inference
.expression_ty(value.node_ref(db).scoped_expression_id(db, scope));

if subject_ty.is_single_valued(db) {
Truthiness::from_bool(subject_ty.is_equivalent_to(db, value_ty))
} else {
Truthiness::Ambiguous
}
}
super::constraint::PatternConstraintKind::Singleton(_)
| super::constraint::PatternConstraintKind::Unsupported => {
Truthiness::Ambiguous
}
},
BranchingCondition::Ambiguous => Truthiness::Ambiguous,
};

result.any_always_false |= truthiness.is_always_false();
result.all_always_true &= truthiness.is_always_true();
result.at_least_one_condition = true;
}

result
}
}

impl std::iter::FusedIterator for BranchingConditionsIterator<'_, '_> {}

#[derive(Clone)]
Expand Down
6 changes: 4 additions & 2 deletions crates/red_knot_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::types::diagnostic::TypeCheckDiagnosticsBuilder;
use crate::types::mro::{ClassBase, Mro, MroError, MroIterator};
use crate::types::narrow::narrowing_constraint;
use crate::{Db, FxOrderSet, Module, Program, PythonVersion};
pub(crate) use static_truthiness::StaticTruthiness;

mod builder;
mod call;
Expand All @@ -39,6 +40,7 @@ mod infer;
mod mro;
mod narrow;
mod signatures;
mod static_truthiness;
mod string_annotation;
mod unpacker;

Expand Down Expand Up @@ -253,7 +255,7 @@ fn bindings_ty<'db>(
constraints,
branching_conditions,
}| {
let result = branching_conditions.truthiness(db);
let result = StaticTruthiness::analyze(db, branching_conditions);

if result.any_always_false {
(None, UnconditionallyVisible::No)
Expand Down Expand Up @@ -333,7 +335,7 @@ fn declarations_ty<'db>(
) -> DeclaredTypeResult<'db> {
let decl_types = declarations
.map(|(declaration, branching_conditions)| {
let result = branching_conditions.truthiness(db);
let result = StaticTruthiness::analyze(db, branching_conditions);

if result.any_always_false {
(Type::Never, UnconditionallyVisible::No)
Expand Down
103 changes: 103 additions & 0 deletions crates/red_knot_python_semantic/src/types/static_truthiness.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use crate::semantic_index::{
ast_ids::HasScopedExpressionId,
branching_condition::BranchingCondition,
constraint::{Constraint, ConstraintNode, PatternConstraintKind},
BranchingConditionsIterator,
};
use crate::types::{infer_expression_types, Truthiness};
use crate::Db;

/// The result of a static-truthiness analysis.
///
/// Consider the following example:
/// ```py
/// a = 1
/// if True:
/// b = 1
/// if <bool>:
/// c = 1
/// if False:
/// d = 1
/// ```
///
/// Given an iterator over the branching conditions for each of these bindings, we would get:
/// ```
/// - a: {any_always_false: false, all_always_true: true, at_least_one_condition: false}
/// - b: {any_always_false: false, all_always_true: true, at_least_one_condition: true}
/// - c: {any_always_false: false, all_always_true: false, at_least_one_condition: true}
/// - d: {any_always_false: true, all_always_true: false, at_least_one_condition: true}
/// ```
pub(crate) struct StaticTruthiness {
/// Is any of the branching conditions always false? (false if there are no conditions)
pub(crate) any_always_false: bool,
/// Are all of the branching conditions always true? (true if there are no conditions)
pub(crate) all_always_true: bool,
/// Is there at least one branching condition?
pub(crate) at_least_one_condition: bool,
}

impl StaticTruthiness {
/// Analyze the (statically known) truthiness for a list of branching conditions.
pub(crate) fn analyze<'db>(
db: &'db dyn Db,
branching_conditions: BranchingConditionsIterator<'_, 'db>,
) -> Self {
let mut result = Self {
any_always_false: false,
all_always_true: true,
at_least_one_condition: false,
};

for condition in branching_conditions {
let truthiness = match condition {
BranchingCondition::ConditionalOn(Constraint {
node: ConstraintNode::Expression(test_expr),
is_positive,
}) => {
let inference = infer_expression_types(db, test_expr);
let scope = test_expr.scope(db);
let ty = inference
.expression_ty(test_expr.node_ref(db).scoped_expression_id(db, scope));

ty.bool(db).negate_if(!is_positive)
}
BranchingCondition::ConditionalOn(Constraint {
node: ConstraintNode::Pattern(inner),
..
}) => match inner.kind(db) {
PatternConstraintKind::Value(value) => {
let subject_expression = inner.subject(db);
let inference = infer_expression_types(db, *subject_expression);
let scope = subject_expression.scope(db);
let subject_ty = inference.expression_ty(
subject_expression
.node_ref(db)
.scoped_expression_id(db, scope),
);

let inference = infer_expression_types(db, *value);
let scope = value.scope(db);
let value_ty = inference
.expression_ty(value.node_ref(db).scoped_expression_id(db, scope));

if subject_ty.is_single_valued(db) {
Truthiness::from_bool(subject_ty.is_equivalent_to(db, value_ty))
} else {
Truthiness::Ambiguous
}
}
PatternConstraintKind::Singleton(_) | PatternConstraintKind::Unsupported => {
Truthiness::Ambiguous
}
},
BranchingCondition::Ambiguous => Truthiness::Ambiguous,
};

result.any_always_false |= truthiness.is_always_false();
result.all_always_true &= truthiness.is_always_true();
result.at_least_one_condition = true;
}

result
}
}

0 comments on commit 4ac3737

Please sign in to comment.