Skip to content

Commit da17446

Browse files
authored
feat: extensions of interfaces (#1425)
Interfaces can now be extended by other interfaces. This involves changes in the parsing, indexing, resolving and most importantly the validation stage. Extends the already existing validations for extended `FUNCTION_BLOCKS` to also take interfaces into account, wherever applicable.
1 parent ca9a577 commit da17446

21 files changed

+2292
-330
lines changed

compiler/plc_ast/src/ast.rs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,14 @@ pub struct Pou {
5050

5151
#[derive(Debug, PartialEq)]
5252
pub struct Interface {
53-
pub name: String,
53+
pub id: AstId,
54+
pub identifier: Identifier,
5455
pub methods: Vec<Pou>,
5556
pub location: SourceLocation,
56-
pub location_name: SourceLocation,
57+
pub extensions: Vec<Identifier>,
5758
}
5859

59-
#[derive(Debug, PartialEq, Clone)]
60+
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
6061
pub struct Identifier {
6162
pub name: String,
6263
pub location: SourceLocation,
@@ -310,6 +311,22 @@ pub enum AccessModifier {
310311
Internal,
311312
}
312313

314+
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
315+
pub enum DeclarationKind {
316+
Abstract,
317+
Concrete,
318+
}
319+
320+
impl DeclarationKind {
321+
pub fn is_abstract(&self) -> bool {
322+
matches!(self, DeclarationKind::Abstract)
323+
}
324+
325+
pub fn is_concrete(&self) -> bool {
326+
matches!(self, DeclarationKind::Concrete)
327+
}
328+
}
329+
313330
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
314331
pub enum PouType {
315332
Program,
@@ -323,6 +340,8 @@ pub enum PouType {
323340

324341
/// The fully qualified name of the property this GET or SET method represents
325342
property: Option<String>,
343+
344+
declaration_kind: DeclarationKind,
326345
},
327346
Init,
328347
ProjectInit,
@@ -1350,7 +1369,7 @@ impl Operator {
13501369

13511370
#[cfg(test)]
13521371
mod tests {
1353-
use crate::ast::{ArgumentProperty, PouType, VariableBlockType};
1372+
use crate::ast::{ArgumentProperty, DeclarationKind, PouType, VariableBlockType};
13541373

13551374
#[test]
13561375
fn display_pou() {
@@ -1359,7 +1378,15 @@ mod tests {
13591378
assert_eq!(PouType::FunctionBlock.to_string(), "FunctionBlock");
13601379
assert_eq!(PouType::Action.to_string(), "Action");
13611380
assert_eq!(PouType::Class.to_string(), "Class");
1362-
assert_eq!(PouType::Method { parent: String::new(), property: None }.to_string(), "Method");
1381+
assert_eq!(
1382+
PouType::Method {
1383+
parent: String::new(),
1384+
property: None,
1385+
declaration_kind: DeclarationKind::Concrete
1386+
}
1387+
.to_string(),
1388+
"Method"
1389+
);
13631390
}
13641391

13651392
#[test]

compiler/plc_lowering/src/inheritance.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,7 +1967,7 @@ mod units_tests {
19671967

19681968
let (_, project) = parse_and_annotate("test", vec![src]).unwrap();
19691969
let unit = &project.units[0].get_unit().units[3];
1970-
assert_debug_snapshot!(unit, @r###"
1970+
assert_debug_snapshot!(unit, @r#"
19711971
POU {
19721972
name: "child.foo",
19731973
variable_blocks: [
@@ -2014,11 +2014,12 @@ mod units_tests {
20142014
pou_type: Method {
20152015
parent: "child",
20162016
property: None,
2017+
declaration_kind: Concrete,
20172018
},
20182019
return_type: None,
20192020
interfaces: [],
20202021
}
2021-
"###);
2022+
"#);
20222023
}
20232024

20242025
#[test]
@@ -2040,14 +2041,15 @@ mod units_tests {
20402041

20412042
let (_, project) = parse_and_annotate("test", vec![src]).unwrap();
20422043
let unit = &project.units[0].get_unit().implementations[1];
2043-
assert_debug_snapshot!(unit, @r###"
2044+
assert_debug_snapshot!(unit, @r#"
20442045
Implementation {
20452046
name: "bar.set0",
20462047
type_name: "bar.set0",
20472048
linkage: Internal,
20482049
pou_type: Method {
20492050
parent: "bar",
20502051
property: None,
2052+
declaration_kind: Concrete,
20512053
},
20522054
statements: [
20532055
Assignment {
@@ -2111,7 +2113,7 @@ mod units_tests {
21112113
Protected,
21122114
),
21132115
}
2114-
"###);
2116+
"#);
21152117
}
21162118
}
21172119

src/index.rs

Lines changed: 104 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use itertools::Itertools;
66
use rustc_hash::{FxHashSet, FxHasher};
77

88
use plc_ast::ast::{
9-
AstId, AstNode, AstStatement, ConfigVariable, DirectAccessType, GenericBinding, HardwareAccessType,
10-
Identifier, Interface, LinkageType, PouType, TypeNature,
9+
AstId, AstNode, AstStatement, ConfigVariable, DeclarationKind, DirectAccessType, GenericBinding,
10+
HardwareAccessType, Identifier, Interface, LinkageType, PouType, TypeNature,
1111
};
1212
use plc_diagnostics::diagnostics::Diagnostic;
1313
use plc_source::source_location::SourceLocation;
@@ -491,48 +491,117 @@ impl ImplementationType {
491491
}
492492
}
493493

494-
#[derive(PartialEq, Eq)]
494+
#[derive(PartialEq, Eq, Hash)]
495495
pub struct InterfaceIndexEntry {
496-
/// The interface name
497-
pub name: String,
496+
/// The interface identifier, consisting of its name and name-location
497+
pub identifier: Identifier,
498498

499499
/// The location of the interface as a whole
500500
pub location: SourceLocation,
501501

502-
/// The location of the interface name
503-
pub location_name: SourceLocation,
504-
505502
/// A list of qualified names of the methods in this interface; the actual methods are located in
506503
/// [`Index::pous`]
507504
pub methods: Vec<String>,
505+
506+
/// A list of other interfaces this interface extends
507+
pub extensions: Vec<Identifier>,
508508
}
509509

510510
impl InterfaceIndexEntry {
511-
/// Returns a list of methods defined in this interface
512-
pub fn get_methods<'idx>(&self, index: &'idx Index) -> Vec<&'idx PouIndexEntry> {
511+
pub fn get_name(&self) -> &str {
512+
self.identifier.name.as_str()
513+
}
514+
515+
pub fn get_name_location(&self) -> &SourceLocation {
516+
&self.identifier.location
517+
}
518+
519+
pub fn get_location(&self) -> &SourceLocation {
520+
&self.location
521+
}
522+
523+
/// Returns a list of methods this interface declared
524+
pub fn get_declared_methods<'idx>(&self, index: &'idx Index) -> Vec<&'idx PouIndexEntry> {
513525
self.methods
514526
.iter()
515527
.map(|name| index.find_pou(name).expect("must exist because of present InterfaceIndexEntry"))
516528
.collect()
517529
}
530+
531+
/// Returns a list of methods this interface inherited
532+
pub fn get_derived_methods<'idx>(&'idx self, index: &'idx Index) -> Vec<&'idx PouIndexEntry> {
533+
self.get_derived_methods_recursive(index, &mut FxHashSet::default())
534+
}
535+
536+
/// Returns a list of methods defined in this interface, including inherited methods from derived interfaces
537+
pub fn get_methods<'idx>(&'idx self, index: &'idx Index) -> Vec<&'idx PouIndexEntry> {
538+
self.get_methods_recursive(index, &mut FxHashSet::default())
539+
}
540+
541+
/// Returns a list of interfaces this interface implements
542+
pub fn get_extensions(&self) -> Vec<&Identifier> {
543+
self.extensions.iter().collect()
544+
}
545+
546+
/// Returns a list of interfaces this interface inherited
547+
pub fn get_derived_interfaces<'idx>(
548+
&self,
549+
index: &'idx Index,
550+
) -> Vec<Result<&'idx InterfaceIndexEntry, Identifier>> {
551+
self.extensions
552+
.iter()
553+
.flat_map(|id| index.find_interface(&id.name).map(Result::Ok).or(Some(Err(id.to_owned()))))
554+
.collect()
555+
}
556+
557+
fn get_methods_recursive<'idx>(
558+
&'idx self,
559+
index: &'idx Index,
560+
seen: &mut FxHashSet<&'idx str>,
561+
) -> Vec<&'idx PouIndexEntry> {
562+
seen.insert(self.get_name());
563+
self.get_declared_methods(index)
564+
.into_iter()
565+
.chain(self.get_derived_methods_recursive(index, seen))
566+
.collect_vec()
567+
}
568+
569+
fn get_derived_methods_recursive<'idx>(
570+
&'idx self,
571+
index: &'idx Index,
572+
seen: &mut FxHashSet<&'idx str>,
573+
) -> Vec<&'idx PouIndexEntry> {
574+
self.get_derived_interfaces(index)
575+
.iter()
576+
.filter_map(|it| it.as_ref().ok())
577+
.flat_map(|it| {
578+
if !seen.contains(it.get_name()) {
579+
it.get_methods_recursive(index, seen)
580+
} else {
581+
vec![]
582+
}
583+
})
584+
.collect()
585+
}
518586
}
519587

520588
impl std::fmt::Debug for InterfaceIndexEntry {
521589
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
522590
f.debug_struct("InterfaceIndexEntry")
523-
.field("name", &self.name)
591+
.field("name", &self.get_name())
524592
.field("methods", &self.methods)
593+
.field("extensions", &self.extensions)
525594
.finish()
526595
}
527596
}
528597

529598
impl From<&Interface> for InterfaceIndexEntry {
530599
fn from(interface: &Interface) -> Self {
531600
InterfaceIndexEntry {
532-
name: interface.name.clone(),
601+
identifier: interface.identifier.clone(),
533602
location: interface.location.clone(),
534-
location_name: interface.location_name.clone(),
535603
methods: interface.methods.iter().map(|method| method.name.clone()).collect(),
604+
extensions: interface.extensions.clone(),
536605
}
537606
}
538607
}
@@ -574,15 +643,16 @@ pub enum PouIndexEntry {
574643
},
575644
Method {
576645
name: String,
577-
parent_pou_name: String,
646+
parent_name: String,
647+
declaration_kind: DeclarationKind,
578648
return_type: String,
579649
instance_struct_name: String,
580650
linkage: LinkageType,
581651
location: SourceLocation,
582652
},
583653
Action {
584654
name: String,
585-
parent_pou_name: String,
655+
parent_name: String,
586656
instance_struct_name: String,
587657
linkage: LinkageType,
588658
location: SourceLocation,
@@ -693,7 +763,7 @@ impl PouIndexEntry {
693763
) -> PouIndexEntry {
694764
PouIndexEntry::Action {
695765
name: qualified_name.into(),
696-
parent_pou_name: pou_name.into(),
766+
parent_name: pou_name.into(),
697767
instance_struct_name: pou_name.into(),
698768
linkage,
699769
location,
@@ -729,12 +799,14 @@ impl PouIndexEntry {
729799
name: &str,
730800
return_type: &str,
731801
owner_class: &str,
802+
declaration_kind: DeclarationKind,
732803
linkage: LinkageType,
733804
location: SourceLocation,
734805
) -> PouIndexEntry {
735806
PouIndexEntry::Method {
736807
name: name.into(),
737-
parent_pou_name: owner_class.into(),
808+
parent_name: owner_class.into(),
809+
declaration_kind,
738810
instance_struct_name: name.into(),
739811
return_type: return_type.into(),
740812
linkage,
@@ -775,14 +847,21 @@ impl PouIndexEntry {
775847

776848
pub fn get_parent_pou_name(&self) -> Option<&str> {
777849
match self {
778-
PouIndexEntry::Method { parent_pou_name, .. } | PouIndexEntry::Action { parent_pou_name, .. } => {
779-
Some(parent_pou_name.as_str())
850+
PouIndexEntry::Method { parent_name, .. } | PouIndexEntry::Action { parent_name, .. } => {
851+
Some(parent_name.as_str())
780852
}
781853

782854
_ => None,
783855
}
784856
}
785857

858+
pub fn get_declaration_kind(&self) -> Option<DeclarationKind> {
859+
match self {
860+
PouIndexEntry::Method { declaration_kind, .. } => Some(*declaration_kind),
861+
_ => None,
862+
}
863+
}
864+
786865
/// returns the name of the struct-type used to store the POUs state
787866
/// (interface-variables)
788867
pub fn get_instance_struct_type_name(&self) -> Option<&str> {
@@ -819,8 +898,8 @@ impl PouIndexEntry {
819898
| PouIndexEntry::FunctionBlock { .. }
820899
| PouIndexEntry::Class { .. }
821900
| PouIndexEntry::Function { .. } => self.get_name(),
822-
PouIndexEntry::Action { parent_pou_name, .. } | PouIndexEntry::Method { parent_pou_name, .. } => {
823-
parent_pou_name.as_str()
901+
PouIndexEntry::Action { parent_name, .. } | PouIndexEntry::Method { parent_name, .. } => {
902+
parent_name.as_str()
824903
}
825904
}
826905
}
@@ -1925,11 +2004,11 @@ impl Index {
19252004

19262005
/// Returns all methods declared on container, or its parents.
19272006
/// If a method is declared in the container the parent method is not included
1928-
pub fn find_methods(&self, container: &str) -> Vec<&PouIndexEntry> {
1929-
self.find_method_recursive(container, vec![], &mut FxHashSet::default())
2007+
pub fn get_methods(&self, container: &str) -> Vec<&PouIndexEntry> {
2008+
self.get_methods_recursive(container, vec![], &mut FxHashSet::default())
19302009
}
19312010

1932-
fn find_method_recursive<'b>(
2011+
fn get_methods_recursive<'b>(
19332012
&'b self,
19342013
container: &str,
19352014
current_methods: Vec<&'b PouIndexEntry>,
@@ -1952,7 +2031,7 @@ impl Index {
19522031
if !seen.insert(super_class) {
19532032
return res;
19542033
};
1955-
self.find_method_recursive(super_class, res, seen)
2034+
self.get_methods_recursive(super_class, res, seen)
19562035
} else {
19572036
res
19582037
}

src/index/indexer.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ impl AstVisitor for SymbolIndexer {
6767
self.visit_pou(method);
6868
}
6969

70-
self.index.interfaces.insert(interface.name.clone(), InterfaceIndexEntry::from(interface));
70+
self.index
71+
.interfaces
72+
.insert(interface.identifier.name.to_owned(), InterfaceIndexEntry::from(interface));
7173
}
7274
}

0 commit comments

Comments
 (0)