Skip to content
Open
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
12 changes: 7 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
- Made GetKnownContexts custom request not wait for analysis results by default, reducing the chances of overfilling workpool capacity while analysises are running.
- Fix issue where the server would internally format URIs incorrectly in some cases
- The language server logs will now be in local time of whatever machine they are running on, rather than UTC.
- The DLS will now properly not report conflicts between statements in a `#if` and its corresponding `#else` branch
- The DLS will now consider all `#if` branches on conditions directly based on `dml_1_2` and `dml_1_4` dead or alive appropriately
Comment thread
JonatanWaern marked this conversation as resolved.

## 0.9.19
- Added configuration option to control the max cache size while resolving references in semantic analysis, defaulting to 500MB
Expand All @@ -17,7 +19,7 @@
- Fixed a bug that caused intermittent failures around instantiated templates
- Added the ability to goto-definition on imports to find the file that was imported
- Allowed empty `-> ()` type specifier for method returns
- Replace provisional `explicit\_object\_decls` with `explicit\_object\_extensions`
- Replace provisional `explicit_object_decls` with `explicit_object_extensions`

## 0.9.18
- Fixed a rare case where the DLS would crash when reporting device contexts
Expand All @@ -28,12 +30,12 @@
- The DLS will now report when an ambiguous default call is made
- Improvements and clarifications to connections between symbols and references,
for details, see [USAGE.md](USAGE.md).
-- Method declared in unrelated templates in an allowed way will now have their
- Method declared in unrelated templates in an allowed way will now have their
references correctly resolved.
-- Goto-reference on default calls will now go to the methods that may be called.
-- Goto-implementations on templates will now go to all places where they are
- Goto-reference on default calls will now go to the methods that may be called.
- Goto-implementations on templates will now go to all places where they are
instantiated.
-- Goto-implementations on objects will now go to all the 'in each' declarations
- Goto-implementations on objects will now go to all the 'in each' declarations
which apply to that object.
- Added parser support for provisional 'explicit\_method\_decls', note that it has no semantic effect.
- The DLS will now correctly report missing template names in 'in each' constructs
Expand Down
173 changes: 117 additions & 56 deletions src/analysis/structure/toplevel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
// SPDX-License-Identifier: Apache-2.0 and MIT
use std::fmt::{Display, Formatter, self as fmt};
use std::path::PathBuf;
use std::sync::Arc;

use crate::analysis::templating::evaluation::{Evaluatable, EvaluationContext};
use crate::logging::trace;

use crate::analysis::structure::objects::{Bitorder, CBlock, CompObjectKind, CompositeObject, Constant, DMLObject, DMLStatement, Device, Error, Export, Hook, Import, InEach, Instantiation, Loggroup, Method, MethodModifier, Parameter, Statements, Template, ToStructure, Typedef, Variable, Version, make_statements};
use crate::analysis::structure::expressions::{Expression};
use crate::analysis::FileSpec;
use crate::analysis::{DMLError, FileSpec};
use crate::analysis::parsing::tree::{ZeroRange, ZeroSpan, TreeElement};
use crate::analysis::parsing::structure;
use crate::analysis::{DeclarationSpan, LocationSpan, Named,
Expand All @@ -30,7 +32,51 @@ pub enum ExistCondition {
// Note: Right now we are implicitly assuming this has some ordering, such
// that two objectdecls within the same hashif block will have the same
// existcondition
Conditional(Vec<(bool, Expression)>),
// These conditions are _hugely_ duplicated between objects, and are stored
// in an Arc to save memory. TODO: We could optimize this even further by
// storing references into a slice of conditional expression from outer
// to inner contexts
Conditional(Arc<Vec<(bool, Expression)>>),
}

impl ExistCondition {
pub fn exists(&self, context: &EvaluationContext, report: &mut Vec<DMLError>) -> bool {
match self {
ExistCondition::Always => true,
ExistCondition::Conditional(conds) => {
for (tbranch, cond) in conds.as_ref() {
let evaluated = cond.evaluate(context, report);
// TODO: check type and value validness
// NOTE: this concludes that non-evaluatable conditions are treated
// as always existing
if evaluated.as_bool()
.is_some_and(|b|b != *tbranch) {
return false;
}
}
true
}
}
}
pub fn guaranteed_exists(&self, context: &EvaluationContext, report: &mut Vec<DMLError>) -> bool {
match self {
ExistCondition::Always => true,
ExistCondition::Conditional(conds) => {
for (tbranch, cond) in conds.as_ref() {
let evaluated = cond.evaluate(context, report);
// TODO: check type and value validness
if let Some(value) = evaluated.as_bool() {
if value != *tbranch {
return false;
}
} else {
return false;
}
}
true
}
}
}
}

impl ExistCondition {
Expand All @@ -39,13 +85,11 @@ impl ExistCondition {
(ExistCondition::Always, ExistCondition::Always) => true,
(ExistCondition::Conditional(selfvec),
ExistCondition::Conditional(othervec)) => {
// This becomes equivalent to checking if they are in the
// same nested hashifs
// As it turns out, collision is guaranted regardless of
// which branch they are in
for ((_, cond1),
(_, cond2)) in selfvec.iter().zip(othervec.iter()) {
if cond1 != cond2 {
// TODO/NOTE: Currently we cannt check if a condition is equivalent with another,
// so we will only check if they are literally the same condition expression
for ((invert1, cond1),
(invert2, cond2)) in selfvec.iter().zip(othervec.iter()) {
if cond1 != cond2 || invert1 != invert2 {
Comment on lines +88 to +92
return false;
}
}
Expand All @@ -54,6 +98,27 @@ impl ExistCondition {
_ => false,
}
}

pub fn guaranteed_excluded_from(&self, other: &ExistCondition) -> bool {
match (self, other) {
(ExistCondition::Conditional(selfvec),
ExistCondition::Conditional(othervec)) => {
// Currently we cannt check if a condition is equivalent with another,
// so we will only check if they are literally the same condition expression
Comment on lines +106 to +107
for ((inverted1, cond1),
(inverted2, cond2)) in selfvec.iter().zip(othervec.iter()) {
if cond1 != cond2 {
return false;
}
if inverted1 != inverted2 {
return true;
}
}
false
},
_ => false,
}
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -154,15 +219,13 @@ where T: DeclarationSpan {

impl <T: Clone> ObjectDecl<T>
where T: DeclarationSpan {
pub fn depending_on_context(obj: &T,
context: StatementContext,
conds: &[(bool, Expression)])
pub fn with_conds(obj: &T,
conds: &Arc<Vec<(bool, Expression)>>)
-> ObjectDecl<T> {
match context {
StatementContext::HashIfTrue |
StatementContext::HashIfElse =>
ObjectDecl::conditional(obj, conds),
_ => ObjectDecl::always(obj),
if conds.is_empty() {
ObjectDecl::always(obj)
} else {
ObjectDecl::conditional(obj, conds)
}
}

Expand All @@ -174,10 +237,10 @@ where T: DeclarationSpan {
}
}
pub fn conditional(obj: &T,
conds: &[(bool, Expression)])
conds: &Arc<Vec<(bool, Expression)>>)
-> ObjectDecl<T> {
ObjectDecl {
cond: ExistCondition::Conditional(conds.to_vec()),
cond: ExistCondition::Conditional(Arc::clone(conds)),
obj: obj.clone(),
spec: StatementSpec::empty(),
}
Expand Down Expand Up @@ -330,7 +393,7 @@ impl StatementSpec {
// Flattens hashifs
fn flatten_hashif_branch(context: StatementContext,
stmnts: &Statements,
conds: Vec<(bool, Expression)>,
conds: Arc<Vec<(bool, Expression)>>,
report: &mut Vec<LocalDMLError>)
-> StatementSpec {
let mut objects = vec![];
Expand All @@ -347,24 +410,23 @@ fn flatten_hashif_branch(context: StatementContext,
let mut errors = vec![];
let mut templates = vec![];
for inst in &stmnts.instantiations {
instantiations.push(ObjectDecl::depending_on_context(
inst, context, &conds));
instantiations.push(ObjectDecl::with_conds(
inst, &conds));
}
for err in &stmnts.errors {
errors.push(ObjectDecl::depending_on_context(
err, context, &conds));
errors.push(ObjectDecl::with_conds(
err, &conds));
}
for ineach in stmnts.ineachs.iter() {
let spec = flatten_hashif_branch(StatementContext::InEach,
&ineach.statements,
vec![], report);
Arc::default(), report);
ineachs.push(
ObjectDecl {
cond: match context {
StatementContext::HashIfTrue |
StatementContext::HashIfElse =>
ExistCondition::Conditional(
conds.clone()),
ExistCondition::Conditional(Arc::clone(&conds)),
_ => ExistCondition::Always,
},
obj: ineach.clone(),
Expand Down Expand Up @@ -412,7 +474,7 @@ fn flatten_hashif_branch(context: StatementContext,
let subspec = flatten_hashif_branch(
StatementContext::Template,
&tmpl.statements,
vec![],
Arc::default(),
report);
templates.push(
ObjectDecl {
Expand Down Expand Up @@ -444,8 +506,8 @@ fn flatten_hashif_branch(context: StatementContext,
// TODO: Verify that conditions are constants
// or builtin parameters
DMLObject::Import(import) =>
imports.push(ObjectDecl::depending_on_context(
import, context, &conds)),
imports.push(ObjectDecl::with_conds(
import, &conds)),
DMLObject::Loggroup(loggroup) =>
report.push(LocalDMLError {
range: loggroup.span.range,
Expand All @@ -464,26 +526,25 @@ fn flatten_hashif_branch(context: StatementContext,
let subspec = flatten_hashif_branch(
StatementContext::Object,
&compobj.statements,
vec![], report);
Arc::clone(&conds), report);
objects.push(
ObjectDecl {
cond: match context {
StatementContext::HashIfTrue |
StatementContext::HashIfElse =>
ExistCondition::Conditional(
conds.clone()),
ExistCondition::Conditional(Arc::clone(&conds)),
_ => ExistCondition::Always,
},
obj: compobj.clone(),
spec: subspec,
});
},
DMLObject::Export(exp) =>
exports.push(ObjectDecl::depending_on_context(
exp, context, &conds)),
exports.push(ObjectDecl::with_conds(
exp, &conds)),
DMLObject::Hook(hook)=>
hooks.push(ObjectDecl::depending_on_context(
hook, context, &conds)),
hooks.push(ObjectDecl::with_conds(
hook, &conds)),
DMLObject::Method(meth) =>
// This error has already been reported, but
// we also need to clear our and pretend
Expand All @@ -496,40 +557,40 @@ fn flatten_hashif_branch(context: StatementContext,
modified_method.modifier
= MethodModifier::None;
methods.push(
ObjectDecl::depending_on_context(
&modified_method, context, &conds))
ObjectDecl::with_conds(
&modified_method, &conds))
} else {
methods.push(
ObjectDecl::depending_on_context(
meth, context, &conds))
ObjectDecl::with_conds(
meth, &conds))
},
DMLObject::Saved(saved) =>
saveds.push(ObjectDecl::depending_on_context(
saved, context, &conds)),
saveds.push(ObjectDecl::with_conds(
saved, &conds)),
DMLObject::Session(sess) =>
sessions.push(ObjectDecl::depending_on_context(
sess, context, &conds)),
sessions.push(ObjectDecl::with_conds(
sess, &conds)),
DMLObject::Parameter(param) =>
params.push(ObjectDecl::depending_on_context(
param, context, &conds)),
params.push(ObjectDecl::with_conds(
param, &conds)),
}
},
DMLStatement::HashIf(hi) => {
let mut true_conds = conds.clone();
let mut true_conds = (*conds).clone();
true_conds.push(
(true, hi.condition.clone()));
let mut false_conds = conds.clone();
let mut false_conds = (*conds).clone();
false_conds.push(
(false, hi.condition.clone()));
let truebranchspec = flatten_hashif_branch(
StatementContext::HashIfTrue,
&hi.truebranch,
true_conds,
Arc::new(true_conds),
report);
let falsebranchspec = flatten_hashif_branch(
StatementContext::HashIfElse,
&hi.falsebranch,
false_conds,
Arc::new(false_conds),
report);
truebranchspec.consume(&mut objects,
&mut sessions,
Expand Down Expand Up @@ -695,7 +756,7 @@ impl TopLevel {
for ineach in &statements.ineachs {
let spec = flatten_hashif_branch(StatementContext::Object,
&ineach.statements,
vec![], report);
Arc::default(), report);
ineachs.push(
ObjectDecl {
cond: ExistCondition::Always,
Expand Down Expand Up @@ -748,7 +809,7 @@ impl TopLevel {
let subspec = flatten_hashif_branch(
StatementContext::Template,
&tmpl.statements,
vec![],
Arc::default(),
report);
templates.push(
ObjectDecl {
Expand All @@ -768,7 +829,7 @@ impl TopLevel {
let subspec = flatten_hashif_branch(
StatementContext::Object,
&compobj.statements,
vec![], report);
Arc::default(), report);
objects.push(
ObjectDecl {
cond: ExistCondition::Always,
Expand Down Expand Up @@ -803,8 +864,8 @@ impl TopLevel {
DMLStatement::HashIf(hi) => {
// First element of the cond tuple is whether the
// condition is NOT in an elsebranch
let true_conds = vec![(true, hi.condition.clone())];
let false_conds = vec![(false, hi.condition.clone())];
let true_conds = Arc::new(vec![(true, hi.condition.clone())]);
let false_conds = Arc::new(vec![(false, hi.condition.clone())]);
let truebranchspec = flatten_hashif_branch(
StatementContext::HashIfTrue,
&hi.truebranch,
Expand Down
Loading
Loading