Skip to content

Commit f24ac60

Browse files
authored
Merge pull request #928 from nasa/issue-927-implied-string-deps
Implied uses for strings
2 parents d836771 + 56380bf commit f24ac60

File tree

196 files changed

+6936
-5530
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

196 files changed

+6936
-5530
lines changed

compiler/lib/src/main/scala/analysis/Analyzers/BasicUseAnalyzer.scala

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ trait BasicUseAnalyzer extends TypeExpressionAnalyzer {
1111

1212
/** A use of a component definition */
1313
def componentUse(a: Analysis, node: AstNode[Ast.QualIdent], use: Name.Qualified): Result = default(a)
14-
14+
1515
/** A use of a component instance definition */
1616
def componentInstanceUse(a: Analysis, node: AstNode[Ast.QualIdent], use: Name.Qualified): Result = default(a)
1717

@@ -33,6 +33,37 @@ trait BasicUseAnalyzer extends TypeExpressionAnalyzer {
3333
/** A use of a state machine definition*/
3434
def stateMachineUse(a: Analysis, node: AstNode[Ast.QualIdent], use: Name.Qualified): Result = default(a)
3535

36+
/** An implied constant use */
37+
def impliedConstantUse(a: Analysis, iu: ImpliedUse) = {
38+
val result = for {
39+
a <- exprNode(a, iu.asExprNode)
40+
a <- impliedUse(a, iu, ImpliedUse.Kind.Constant)
41+
} yield a
42+
iu.annotateResult(result)
43+
}
44+
45+
/** An implied port use */
46+
def impliedPortUse(a: Analysis, iu: ImpliedUse) = {
47+
val result = for {
48+
a <- portUse(a, iu.asQualIdentNode, iu.name)
49+
a <- impliedUse(a, iu, ImpliedUse.Kind.Port)
50+
} yield a
51+
iu.annotateResult(result)
52+
}
53+
54+
/** An implied type use */
55+
def impliedTypeUse(a: Analysis, iu: ImpliedUse) = {
56+
val result = for {
57+
a <- typeUse(a, iu.asTypeNameNode, iu.name)
58+
a <- impliedUse(a, iu, ImpliedUse.Kind.Type)
59+
} yield a
60+
iu.annotateResult(result)
61+
}
62+
63+
/** An implied use */
64+
def impliedUse(a: Analysis, iu: ImpliedUse, kind: ImpliedUse.Kind): Result =
65+
Right(a)
66+
3667
override def defComponentInstanceAnnotatedNode(a: Analysis, node: Ast.Annotated[AstNode[Ast.DefComponentInstance]]) = {
3768
val (_, node1, _) = node
3869
val data = node1.data
@@ -165,11 +196,23 @@ trait BasicUseAnalyzer extends TypeExpressionAnalyzer {
165196
typeUse(a, node, use)
166197
}
167198

199+
override def typeNameStringNode(
200+
a: Analysis,
201+
node: AstNode[Ast.TypeName],
202+
tn: Ast.TypeNameString
203+
) = {
204+
val id = node.id
205+
for {
206+
a <- visitImpliedUses(a, id)
207+
a <- super.typeNameStringNode(a, node, tn)
208+
} yield a
209+
}
210+
168211
private def portInstanceIdentifierNode(a: Analysis, node: AstNode[Ast.PortInstanceIdentifier]): Result =
169212
qualIdentNode (componentInstanceUse) (a, node.data.componentInstance)
170213

171214
private def qualIdentNode
172-
(f: (Analysis, AstNode[Ast.QualIdent], Name.Qualified) => Result)
215+
(f: (Analysis, AstNode[Ast.QualIdent], Name.Qualified) => Result)
173216
(a: Analysis, qualIdent: AstNode[Ast.QualIdent]): Result = {
174217
val use = Name.Qualified.fromQualIdent(qualIdent.data)
175218
f(a, qualIdent, use)
@@ -190,20 +233,17 @@ trait BasicUseAnalyzer extends TypeExpressionAnalyzer {
190233

191234
private def visitImpliedConstantUses(a: Analysis, id: AstNode.Id) = {
192235
val uses = a.getImpliedUses(ImpliedUse.Kind.Constant, id).toList
193-
def visit(a: Analysis, iu: ImpliedUse) = exprNode(a, iu.asExprNode)
194-
visitList(a, uses, visit)
236+
visitList(a, uses, impliedConstantUse)
195237
}
196238

197239
private def visitImpliedPortUses(a: Analysis, id: AstNode.Id) = {
198240
val uses = a.getImpliedUses(ImpliedUse.Kind.Port, id).toList
199-
def visit(a: Analysis, iu: ImpliedUse) = portUse(a, iu.asQualIdentNode, iu.name)
200-
visitList(a, uses, visit)
241+
visitList(a, uses, impliedPortUse)
201242
}
202243

203244
private def visitImpliedTypeUses(a: Analysis, id: AstNode.Id) = {
204245
val uses = a.getImpliedUses(ImpliedUse.Kind.Type, id).toList
205-
def visit(a: Analysis, iu: ImpliedUse) = typeUse(a, iu.asTypeNameNode, iu.name)
206-
visitList(a, uses, visit)
246+
visitList(a, uses, impliedTypeUse)
207247
}
208248

209249
private def visitImpliedUses(a: Analysis, id: AstNode.Id) = {

compiler/lib/src/main/scala/analysis/CheckSemantics/CheckUseDefCycles.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ object CheckUseDefCycles extends UseAnalyzer {
100100
val symbol = a.useDefMap(node.id)
101101
val m = UseDefMatching(node.id, use, symbol)
102102
val a1 = a.copy(useDefMatchingList = m :: a.useDefMatchingList)
103-
visitDefPre(a1, symbol)
103+
for (_ <- visitDefPre(a1, symbol)) yield a
104104
}
105105

106106
}

compiler/lib/src/main/scala/analysis/CheckSemantics/CheckUses.scala

Lines changed: 27 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,33 @@ object CheckUses extends BasicUseAnalyzer {
8181
visitExprNode(a, node)
8282
}
8383

84+
// Check that an implied use (a) is not a member
85+
// of a def and (b) does not shadow the required def
86+
override def impliedUse(a: Analysis, iu: ImpliedUse, kind: ImpliedUse.Kind) = {
87+
val sym = a.useDefMap(iu.id)
88+
val symQualifiedName = a.getQualifiedName(sym).toString
89+
val iuName = iu.name.toString
90+
// Check that the name of the def matches the name of the use
91+
if symQualifiedName == iuName
92+
// OK, they match
93+
then Right(a)
94+
else {
95+
val msg = if symQualifiedName.length < iuName.length
96+
// Definition has a shorter name: the use is a member of the definition
97+
then s"it has $iuName as a member"
98+
// Definition has a longer name: it shadows the required definition
99+
else s"it shadows $iuName here"
100+
Left(
101+
SemanticError.InvalidSymbol(
102+
symQualifiedName,
103+
Locations.get(iu.id),
104+
msg,
105+
sym.getLoc
106+
)
107+
)
108+
}
109+
}
110+
84111
override def defComponentAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefComponent]]) = {
85112
val (_, node, _) = aNode
86113
val data = node.data
@@ -146,30 +173,6 @@ object CheckUses extends BasicUseAnalyzer {
146173
} yield a.copy(nestedScope = a.nestedScope.pop)
147174
}
148175

149-
override def defTopologyAnnotatedNode(a: Analysis, node: Ast.Annotated[AstNode[Ast.DefTopology]]) = {
150-
val impliedTypeUses = a.getImpliedUses(ImpliedUse.Kind.Type, node._2.id).toList
151-
val impliedConstantUses = a.getImpliedUses(ImpliedUse.Kind.Constant, node._2.id).toList
152-
for {
153-
a <- Result.foldLeft (impliedConstantUses) (a) ((a, iu) => {
154-
val exprNode = iu.asExprNode
155-
for {
156-
a <- Result.annotateResult(
157-
constantUse(a, exprNode, iu.name),
158-
s"when constructing a dictionary, the constant ${iu.name} must be defined"
159-
)
160-
_ <- checkImpliedUseIsConstantDef(a, iu, exprNode)
161-
} yield a
162-
})
163-
a <- Result.foldLeft (impliedTypeUses) (a) ((a, iu) => {
164-
Result.annotateResult(
165-
typeUse(a, iu.asTypeNameNode, iu.name),
166-
s"when constructing a dictionary, the type ${iu.name} must be defined"
167-
)
168-
})
169-
a <- TopologyAnalyzer.visit(this, a, node)
170-
} yield a
171-
}
172-
173176
override def portUse(a: Analysis, node: AstNode[Ast.QualIdent], use: Name.Qualified) =
174177
helpers.visitQualIdentNode (NameGroup.Port) (a, node)
175178

@@ -192,31 +195,4 @@ object CheckUses extends BasicUseAnalyzer {
192195
}
193196
}
194197

195-
// Check that an implied use is a constant def and not a member
196-
// of a constant def
197-
private def checkImpliedUseIsConstantDef(a: Analysis, iu: ImpliedUse, exprNode: AstNode[Ast.Expr]) = {
198-
val sym = a.useDefMap(exprNode.id)
199-
// Check that the name of the def matches the name of the use
200-
if a.getQualifiedName(sym) == iu.name
201-
// OK, they match
202-
then Right(a)
203-
// They don't match: the definition does not provide the required constant
204-
else
205-
val iuName = iu.name
206-
val symName = sym.getUnqualifiedName
207-
val error = Left(
208-
SemanticError.InvalidSymbol(
209-
symName,
210-
Locations.get(exprNode.id),
211-
s"it has $iuName as a member",
212-
sym.getLoc
213-
)
214-
)
215-
val notes = List(
216-
s"$iuName is an F Prime framework constant",
217-
"it must be a constant definition"
218-
)
219-
Result.annotateResult(error, notes)
220-
}
221-
222198
}

compiler/lib/src/main/scala/analysis/CheckSemantics/ConstructImpliedUseMap.scala

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,17 @@ import fpp.compiler.ast._
44
import fpp.compiler.util._
55

66
/** Construct the implied use map */
7-
object ConstructImpliedUseMap
8-
extends Analyzer
9-
with ComponentAnalyzer
10-
with InterfaceAnalyzer
11-
with ModuleAnalyzer
12-
{
7+
object ConstructImpliedUseMap extends TypeExpressionAnalyzer {
138

14-
override def specPortInstanceAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.SpecPortInstance]]) = {
9+
override def specPortInstanceAnnotatedNode(
10+
a: Analysis,
11+
aNode: Ast.Annotated[AstNode[Ast.SpecPortInstance]]
12+
) = {
1513
val (_, node, _) = aNode
1614
val data = node.data
1715
val id = node.id
18-
data match {
19-
case _ : Ast.SpecPortInstance.General => Right(a)
16+
val a1 = data match {
17+
case _ : Ast.SpecPortInstance.General => a
2018
case special : Ast.SpecPortInstance.Special =>
2119
// Construct the port use implied by the special port instance
2220
val name = special.kind match {
@@ -37,8 +35,9 @@ object ConstructImpliedUseMap
3735
val identList = List("Fw", name)
3836
val impliedUse = ImpliedUse.fromIdentListAndId(identList, node.id)
3937
val map = Map(ImpliedUse.Kind.Port -> Set(impliedUse))
40-
Right(a.copy(impliedUseMap = a.impliedUseMap + (id -> map)))
38+
a.copy(impliedUseMap = a.impliedUseMap + (id -> map))
4139
}
40+
super.specPortInstanceAnnotatedNode(a1, aNode)
4241
}
4342

4443
override def defTopologyAnnotatedNode(
@@ -48,21 +47,53 @@ object ConstructImpliedUseMap
4847
val id = aNode._2.id
4948
val typeNames = ImpliedUse.getTopologyTypes(a)
5049
val empty: ImpliedUse.Uses = Map()
50+
val annotations = List("this implied use occurs when constructing a dictionary")
5151
val typeMap = typeNames.foldLeft (empty) ((m, tn) => {
5252
val id1 = ImpliedUse.replicateId(id)
53-
val impliedUse = ImpliedUse.fromIdentListAndId(tn, id1)
53+
val impliedUse = ImpliedUse.fromIdentListAndId(tn, id1, annotations)
5454
val set = m.get(ImpliedUse.Kind.Type).getOrElse(Set())
5555
m + (ImpliedUse.Kind.Type -> (set + impliedUse))
5656
})
57-
5857
val constants = ImpliedUse.getTopologyConstants(a)
5958
val map = constants.foldLeft (typeMap) ((m, c) => {
6059
val id1 = ImpliedUse.replicateId(id)
61-
val impliedUse = ImpliedUse.fromIdentListAndId(c, id1)
60+
val impliedUse = ImpliedUse.fromIdentListAndId(c, id1, annotations)
6261
val set = m.get(ImpliedUse.Kind.Constant).getOrElse(Set())
6362
m + (ImpliedUse.Kind.Constant -> (set + impliedUse))
6463
})
65-
Right(a.copy(impliedUseMap = a.impliedUseMap + (id -> map)))
64+
val a1 = a.copy(impliedUseMap = a.impliedUseMap + (id -> map))
65+
super.defTopologyAnnotatedNode(a1, aNode)
66+
}
67+
68+
override def typeNameStringNode(
69+
a: Analysis,
70+
node: AstNode[Ast.TypeName],
71+
tn: Ast.TypeNameString
72+
) = {
73+
val id = node.id
74+
def getImpliedUses(name: String, annotation: String) = {
75+
val il = List(name)
76+
val id1 = ImpliedUse.replicateId(id)
77+
Set(ImpliedUse.fromIdentListAndId(il, id1, List(annotation)))
78+
}
79+
val map = {
80+
val uses = getImpliedUses(
81+
"FwSizeStoreType",
82+
"use of a string type requires this definition"
83+
)
84+
Map(ImpliedUse.Kind.Type -> uses)
85+
}
86+
val map1 = if tn.size.isDefined
87+
then map
88+
else {
89+
val uses = getImpliedUses(
90+
"FW_FIXED_LENGTH_STRING_SIZE",
91+
"use of a string type with default size requires this definition"
92+
)
93+
map + (ImpliedUse.Kind.Constant -> uses)
94+
}
95+
val a1 = a.copy(impliedUseMap = a.impliedUseMap + (id -> map1))
96+
super.typeNameStringNode(a1, node, tn)
6697
}
6798

6899
}

compiler/lib/src/main/scala/analysis/Semantics/ImpliedUse.scala

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ case class ImpliedUse(
88
/** The fully-qualified name of the implied use */
99
name: Name.Qualified,
1010
/** The AST node id associated with the implied use */
11-
id: AstNode.Id
11+
id: AstNode.Id,
12+
/** Optional annotations for error reporting */
13+
annotations: List[String] = Nil
1214
) {
1315

1416
def asExprNode: AstNode[Ast.Expr] = {
@@ -20,18 +22,6 @@ case class ImpliedUse(
2022
AstNode.create(expr, id)
2123
}
2224

23-
def asUniqueExprNode: AstNode[Ast.Expr] = {
24-
val Name.Qualified(qualifier, base) = name
25-
val head :: tail = name.toIdentList
26-
val expr = tail.foldLeft (Ast.ExprIdent(head): Ast.Expr) ((e1, s) =>
27-
Ast.ExprDot(
28-
AstNode.create(e1, ImpliedUse.replicateId(id)),
29-
AstNode.create(s, ImpliedUse.replicateId(id))
30-
)
31-
)
32-
AstNode.create(expr, ImpliedUse.replicateId(id))
33-
}
34-
3525
def asQualIdentNode: AstNode[Ast.QualIdent] = {
3626
val nodeList = name.toIdentList.map(AstNode.create(_, id))
3727
val qualIdent = Ast.QualIdent.fromNodeList(nodeList)
@@ -43,6 +33,11 @@ case class ImpliedUse(
4333
AstNode.create(typeName, id)
4434
}
4535

36+
def annotateResult[T](r: Result.Result[T]) = {
37+
val as = s"the symbol $name has an implied use at the point of the error" :: annotations
38+
Result.annotateResult(r, as)
39+
}
40+
4641
}
4742

4843
object ImpliedUse {
@@ -82,14 +77,18 @@ object ImpliedUse {
8277
)
8378
else Nil
8479

80+
/** Create a new ID at the same location as id */
8581
def replicateId(id: AstNode.Id) = {
8682
val loc = Locations.get(id)
8783
val id1 = AstNode.getId
8884
Locations.put(id1, loc)
8985
id1
9086
}
9187

92-
def fromIdentListAndId(identList: List[Name.Unqualified], id: AstNode.Id) =
93-
ImpliedUse(Name.Qualified.fromIdentList(identList), id)
88+
def fromIdentListAndId(
89+
identList: List[Name.Unqualified],
90+
id: AstNode.Id,
91+
annotations: List[String] = Nil
92+
) = ImpliedUse(Name.Qualified.fromIdentList(identList), id, annotations)
9493

9594
}

compiler/lib/src/main/scala/util/Error.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ sealed trait Error {
238238
case SemanticError.InvalidTypeForMemberSelection(memberName, loc, typeName) =>
239239
Error.print (Some(loc)) (s"no member $memberName in value of type $typeName")
240240
case SemanticError.InvalidSymbol(name, loc, msg, defLoc) =>
241-
Error.print (Some(loc)) (s"invalid symbol $name: $msg")
241+
Error.print (Some(loc)) (s"invalid use of symbol $name: $msg")
242242
System.err.println("symbol is defined here:")
243243
System.err.println(defLoc)
244244
case SemanticError.InvalidTlmChannelName(loc, channelName, componentName) =>

compiler/lib/src/main/scala/util/Result.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ object Result {
99

1010
/** Wraps error as an AnnotatedError with notes */
1111
def annotateResult[A](r: Result[A], notes: List[String]): Result[A] =
12-
r match {
13-
case Right(v) => Right(v)
14-
case Left(e: Error) => Left(AnnotatedError(e, notes))
12+
(r, notes) match {
13+
case (Right(_), _) => r
14+
case (_, Nil) => r
15+
case (Left(e: Error), _) => Left(AnnotatedError(e, notes))
1516
}
1617

1718
/** Wraps error as an AnnotatedError with a single note */
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
type FwSizeStoreType = U16
2+
constant FW_FIXED_LENGTH_STRING_SIZE = 256
3+
14
type T = string
25
array A = [3] T format "{.3f}"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
fpp-check
2-
[ local path prefix ]/compiler/tools/fpp-check/test/array/format_alias_not_numeric.fpp:2.24
2+
[ local path prefix ]/compiler/tools/fpp-check/test/array/format_alias_not_numeric.fpp:5.24
33
array A = [3] T format "{.3f}"
44
^
55
error: invalid format string: T is not a floating-point type

0 commit comments

Comments
 (0)