Skip to content
Merged
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
7 changes: 6 additions & 1 deletion src/dataflow/eval/resolve/alias-tracking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { DfEdge, EdgeType } from '../../graph/edge';
import type { DataflowGraph } from '../../graph/graph';
import { onReplacementOperator, type ReplacementOperatorHandlerArgs } from '../../graph/unknown-replacement';
import { onUnknownSideEffect } from '../../graph/unknown-side-effect';
import { VertexType } from '../../graph/vertex';
import { isValueVertex, VertexType } from '../../graph/vertex';
import { valueFromRNodeConstant, valueFromTsValue } from '../values/general';
import { Bottom, isTop, type Lift, Top, type Value, type ValueSet } from '../values/r-value';
import { setFrom } from '../values/sets/set-constants';
Expand Down Expand Up @@ -380,6 +380,11 @@ export function trackAliasesInGraph(id: NodeId, graph: DataflowGraph, ctx: ReadO

const values: Set<Value> = new Set<Value>();
for(const id of resultIds) {
const vertex = graph.getVertex(id);
if(isValueVertex(vertex) && vertex.value !== undefined) {
values.add(vertex.value);
continue;
}
const node = idMap.get(id);
if(node !== undefined) {
if(node.info.role === RoleInParent.ParameterDefaultValue || RNode.iterateParents(node, idMap).some(p => p.info.role === RoleInParent.ParameterDefaultValue)) {
Expand Down
18 changes: 7 additions & 11 deletions src/dataflow/graph/vertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { REnvironmentInformation } from '../environments/environment';
import type { ControlDependency, ExitPoint } from '../info';
import type { Identifier } from '../environments/identifier';
import type { BuiltInProcName } from '../environments/built-in-proc-name';
import type { Value } from '../eval/values/r-value';


export enum VertexType {
Expand Down Expand Up @@ -56,27 +57,22 @@ export interface DataflowGraphVertexAstLink {

/**
* Marker vertex for a value in the dataflow of the program.
* This does not contain the _value_ of the referenced constant
* as this is available with the {@link DataflowGraphVertexBase#id|id} in the {@link NormalizedAst|normalized AST}
* (or more specifically the {@link AstIdMap}).
*
* If you have a {@link DataflowGraph|dataflow graph} named `graph`
* with an {@link AstIdMap} and a value vertex object with name `value` the following Code should work:
* For user-code constants (numbers, strings, logicals) the value is recovered by looking up the
* {@link DataflowGraphVertexBase#id|id} in the {@link NormalizedAst|normalized AST}:
* @example
* ```ts
* const node = graph.idMap.get(value.id)
* ```
*
* This then returns the corresponding node in the {@link NormalizedAst|normalized AST}, for example,
* an {@link RNumber} or {@link RString}.
*
* This works similarly for {@link IdentifierReference|identifier references}
* for which you can use the {@link IdentifierReference#nodeId|`nodeId`}.
* For built-in constants whose id is not in the {@link AstIdMap} (e.g. `T` resolving to `built-in:T`),
* the abstract {@link Value} is stored directly in the {@link DataflowGraphVertexValue#value|value} field.
* @see {@link isValueVertex} - to check if a vertex is a value vertex
*/
export interface DataflowGraphVertexValue extends DataflowGraphVertexBase {
readonly tag: VertexType.Value
readonly environment?: undefined
/** Pre-computed abstract value; set for built-in constants (e.g. `T`, `F`) whose id is not in the AST id map */
readonly value?: Value
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { expensiveTrace } from '../../../../../../util/log';
import type { Writable } from 'ts-essentials';
import { makeAllMaybe } from '../../../../../environments/reference-to-maybe';
import { BuiltInProcName } from '../../../../../environments/built-in-proc-name';
import type { BuiltInIdentifierConstant } from '../../../../../environments/built-in';
import { valueFromTsValue } from '../../../../../eval/values/general';



Expand Down Expand Up @@ -60,6 +62,14 @@ function linkReadNameToWriteIfPossible(read: IdentifierReference, environments:
} else {
nextGraph.addEdge(rid, tid, EdgeType.Reads);
}
if(target.type === ReferenceType.BuiltInConstant) {
nextGraph.addVertex({
tag: VertexType.Value,
id: tid,
cds: undefined,
value: valueFromTsValue((target as BuiltInIdentifierConstant).value)
}, environments, false);
}
}
}

Expand Down
37 changes: 28 additions & 9 deletions test/functionality/dataflow/environments/resolve.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,21 @@ import { resolveIdToValue, resolveToConstants } from '../../../../src/dataflow/e
import { contextFromInput } from '../../../../src/project/context/flowr-analyzer-context';
import { FlowrConfig } from '../../../../src/config';

/** Controls which extra results are accepted in addition to an exact match */
enum Allow {
None = 0,
Top = 1,
Bottom = 2
/** Only the exact expected value is accepted */
ExactOnly = 0,
/** Also accept top (unknown); use when the analysis may give up */
Top = 1,
/** Also accept bottom (unreachable); use when the result may be unreachable */
Bottom = 2
}

/** Controls how {@link resolveIdToValue} is invoked during tests */
enum With {
Graph,
/** Pass only the dataflow graph, no environment is given to the resolver */
GraphOnly,
/** Pass the full program environment, the normal fully-informed path */
Environment
}

Expand All @@ -49,8 +56,8 @@ describe.sequential('Resolve', withShell(shell => {
}

function testWithGraphAndEnvironment(name: string, tests: (withWhat: With) => void) {
describe(`${name} (Graph)`, () => {
tests(With.Graph);
describe(`${name} (Graph only)`, () => {
tests(With.GraphOnly);
});

describe(`${name} (Environment)`, () => {
Expand All @@ -63,7 +70,7 @@ describe.sequential('Resolve', withShell(shell => {
identifier: SlicingCriterion,
code: string,
expectedValues: Value,
allow: Allow = Allow.None,
allow: Allow = Allow.ExactOnly,
withEnv: With = With.Environment
): void {
const effectiveName = decorateLabelContext(label(name), ['resolve']);
Expand All @@ -76,7 +83,7 @@ describe.sequential('Resolve', withShell(shell => {
}).allRemainingSteps();

const resolved = resolveIdToValue(SlicingCriterion.parse(identifier, dataflow.normalize.idMap), {
environment: withEnv === With.Environment ? dataflow.dataflow.environment : undefined,
environment: withEnv === With.GraphOnly ? undefined : dataflow.dataflow.environment,
graph: dataflow.dataflow.graph,
idMap: dataflow.normalize.idMap,
full: true,
Expand All @@ -96,7 +103,7 @@ describe.sequential('Resolve', withShell(shell => {
});
}

function testMutate(name: string, line: number, identifier: string, code: string, expected: Value, allow: Allow = Allow.None) {
function testMutate(name: string, line: number, identifier: string, code: string, expected: Value, allow: Allow = Allow.ExactOnly) {
const distractors: string[] = [
`while(FALSE) { ${identifier} <- 0 }`,
`if(FALSE) { ${identifier} <- 0 }`,
Expand Down Expand Up @@ -210,6 +217,18 @@ describe.sequential('Resolve', withShell(shell => {
});
});
});
describe.each<[string, With]>([
['Graph only', With.GraphOnly ],
['Environment', With.Environment],
])('Resolve built-in constants (%s)', (_name, resolveWith) => {
testResolve('T resolves to true', '1@T', 'T', set([true]), Allow.ExactOnly, resolveWith);
testResolve('F resolves to false', '1@F', 'F', set([false]), Allow.ExactOnly, resolveWith);
testResolve('TRUE resolves to true', '1@TRUE', 'TRUE', set([true]), Allow.ExactOnly, resolveWith);
testResolve('T multiple, resolve second', '2@T', 'T\nT\nT', set([true]), Allow.ExactOnly, resolveWith);
testResolve('T shadow with logical', '2@T', 'T <- FALSE\nT', set([false]), Allow.ExactOnly, resolveWith);
testResolve('T shadow with number', '2@T', 'T <- 42\nT', set([42]), Allow.ExactOnly, resolveWith);
});

describe('Builtin Constants', () => {
// Always Resolve
test.each([
Expand Down
15 changes: 15 additions & 0 deletions test/functionality/dataflow/main/atomic/dataflow-atomic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ describe.sequential('Atomic (dataflow information)', withShell(shell => {
}
});

describe('Built-in constant aliases', () => {
for(const [input, capability] of [
['T', 'logical'],
['F', 'logical'],
] as [string, SupportedFlowrCapabilityId][]) {
assertDataflow(label(input, [capability]), shell, input,
emptyGraph()
.use(0, input)
.constant(NodeId.toBuiltIn(input), {}, false)
.reads(0, NodeId.toBuiltIn(input)),
{ expectIsSubgraph: true }
);
}
});

assertDataflow(label('simple variable', ['name-normal']), shell,
'xylophone', emptyGraph().use(0, 'xylophone')
);
Expand Down
Loading