Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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);
Comment thread
EagleoutIce marked this conversation as resolved.
Outdated
}
}
}

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