From: Jani Simomaa Date: Sat, 16 Mar 2019 14:48:34 +0000 (+0000) Subject: Merge "Fetch all audit logging events" X-Git-Tag: v1.43.0~136^2~177 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=a270cece5fc71b6e3ca7f58ed21b297b017029fc;hp=91efbfa3d0a6449e8bf183af733bce5aeb098821;p=simantics%2Fplatform.git Merge "Fetch all audit logging events" --- diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/Files.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/Files.java index cc41e8e9b..35c8822d5 100644 --- a/bundles/org.simantics.databoard/src/org/simantics/databoard/Files.java +++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/Files.java @@ -188,8 +188,7 @@ public class Files { public static Datatype readFileType(File file) throws IOException { BinaryFile rf = new BinaryFile( file, "r" ); try { - Binding datatype_binding = Bindings.getBindingUnchecked( Datatype.class ); - return (Datatype) Bindings.getSerializerUnchecked( datatype_binding ).deserialize( rf ); + return (Datatype) Bindings.getSerializerUnchecked( Bindings.DATATYPE ).deserialize( rf ); } finally { rf.close(); } @@ -208,8 +207,7 @@ public class Files { public static Object readFile(File file, Binding binding) throws IOException { BinaryFile rf = new BinaryFile( file, "r" ); try { - Binding datatype_binding = Bindings.getBindingUnchecked( Datatype.class ); - Datatype type = (Datatype) Bindings.getSerializerUnchecked( datatype_binding ).deserialize( rf ); + Datatype type = (Datatype) Bindings.getSerializerUnchecked( Bindings.DATATYPE ).deserialize( rf ); if (type.equals(binding.type())) { return Bindings.getSerializerUnchecked( binding ).deserialize(rf); @@ -229,6 +227,30 @@ public class Files { rf.close(); } } + + public static Object readFileTypeAdapting(File file, Binding binding) throws IOException { + BinaryFile rf = new BinaryFile( file, "r" ); + try { + Datatype type = (Datatype) Bindings.getSerializerUnchecked( Bindings.DATATYPE ).deserialize( rf ); + + if (type.equals(binding.type())) { + return Bindings.getSerializerUnchecked( binding ).deserialize(rf); + } else { + try { + Binding fileContentBinding = Bindings.getMutableBinding(type); + Adapter adapter = Bindings.getTypeAdapter(fileContentBinding, binding); + Object value = Bindings.getSerializerUnchecked( fileContentBinding ).deserialize(rf); + return adapter.adapt( value ); + } catch (AdapterConstructionException e) { + throw new IOException(e); + } catch (AdaptException e) { + throw new IOException(e); + } + } + } finally { + rf.close(); + } + } /** * Read a file to an object. @@ -241,8 +263,7 @@ public class Files { public static void readFile(File file, RecordBinding binding, Object dst) throws IOException { BinaryFile rf = new BinaryFile( file, "r" ); try { - Binding datatype_binding = Bindings.getBindingUnchecked( Datatype.class ); - Datatype type = (Datatype) Bindings.getSerializerUnchecked( datatype_binding ).deserialize( rf ); + Datatype type = (Datatype) Bindings.getSerializerUnchecked( Bindings.DATATYPE ).deserialize( rf ); if (type.equals(binding.type())) { Serializer s = Bindings.getSerializerUnchecked( binding ); @@ -276,8 +297,7 @@ public class Files { */ public static Object readFile(InputStream is, Binding binding) throws IOException { BinaryReadable readable = InputStreamReadable.readFully( is ); - Binding datatype_binding = Bindings.getBindingUnchecked( Datatype.class ); - Datatype type = (Datatype) Bindings.getSerializerUnchecked( datatype_binding ).deserialize( readable ); + Datatype type = (Datatype) Bindings.getSerializerUnchecked( Bindings.DATATYPE ).deserialize( readable ); if (!type.equals(binding.type())) { try { @@ -308,8 +328,7 @@ public class Files { */ public static Object readFile(InputStream is, long streamLength, Binding binding) throws IOException { BinaryReadable readable = new InputStreamReadable( is, streamLength ); - Binding datatype_binding = Bindings.getBindingUnchecked( Datatype.class ); - Datatype type = (Datatype) Bindings.getSerializerUnchecked( datatype_binding ).deserialize( readable ); + Datatype type = (Datatype) Bindings.getSerializerUnchecked( Bindings.DATATYPE ).deserialize( readable ); if (!type.equals(binding.type())) { try { diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/request/ChildrenByIdentifier.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/request/ChildrenByIdentifier.java new file mode 100644 index 000000000..8b3c0768b --- /dev/null +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/request/ChildrenByIdentifier.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.db.layer0.request; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.simantics.datatypes.literal.GUID; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.request.ResourceRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.layer0.Layer0; + +/** + * Get a map from GUIDs to the children of the given resource. + */ +public final class ChildrenByIdentifier extends ResourceRead> { + public ChildrenByIdentifier(Resource resource) { + super(resource); + } + + @Override + public Map perform(ReadGraph graph) throws DatabaseException { + Map result = new HashMap<>(); + Layer0 L0 = Layer0.getInstance(graph); + Collection children = graph.getObjects(resource, L0.ConsistsOf); + for (Resource child : children) { + GUID guid = graph.getPossibleRelatedValue(child, L0.identifier, GUID.BINDING); + if (guid != null) + result.put(guid, child); + } + + return result; + } +} diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/DiagramContentRequest.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/DiagramContentRequest.java index 9be72d29f..8f5afab1a 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/DiagramContentRequest.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/DiagramContentRequest.java @@ -31,6 +31,8 @@ import org.simantics.diagram.content.RouteGraphConnectionPartRequest; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.diagram.synchronization.ErrorHandler; import org.simantics.g2d.canvas.ICanvasContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.hash.THashMap; @@ -42,6 +44,8 @@ import gnu.trove.set.hash.THashSet; */ public class DiagramContentRequest extends BaseRequest { + private static final Logger LOGGER = LoggerFactory.getLogger(DiagramContentRequest.class); + int previousElementCount = 32; ErrorHandler errorHandler; @@ -58,7 +62,11 @@ public class DiagramContentRequest extends BaseRequest components = OrderedSetUtils.toList(g, data); DiagramContents res = g.syncRequest((AsyncRead)(graph, procedure) -> { diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/TypeGroup.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/TypeGroup.java index b7b4ba2ff..7e4cb0e37 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/TypeGroup.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/TypeGroup.java @@ -29,12 +29,16 @@ import org.simantics.db.procedure.SetListener; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.layer0.Layer0; import org.simantics.scenegraph.profile.Group; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Tuukka Lehtonen */ public class TypeGroup implements Group { + private static final Logger LOGGER = LoggerFactory.getLogger(TypeGroup.class); + private final Collection types; private final String name; @@ -103,14 +107,17 @@ public class TypeGroup implements Group { // for (Resource t : types) // System.out.println("\t" + NameUtils.getSafeName(graph, t, true)); - Collection elements = graph.syncRequest(new OrderedSet(realDiagram)); - for (Resource element : elements) { -// System.out.println("checking element " + NameUtils.getSafeName(graph, element, true)); - Collection elementTypes = graph.getTypes(element); - if (!Collections.disjoint(types, elementTypes)) - result.add(element); + if (graph.hasStatement(realDiagram)) { + Collection elements = graph.syncRequest(new OrderedSet(realDiagram)); + for (Resource element : elements) { + // System.out.println("checking element " + NameUtils.getSafeName(graph, element, true)); + Collection elementTypes = graph.getTypes(element); + if (!Collections.disjoint(types, elementTypes)) + result.add(element); + } + } else { + LOGGER.warn("Most likely after deleting a diagram or something therefore no ordered set can be found for {}", realDiagram); } - return result; } diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/componentTypeEditor/DerivedPropertiesSection.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/componentTypeEditor/DerivedPropertiesSection.java index 0f7bff396..5f96c2ba9 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/componentTypeEditor/DerivedPropertiesSection.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/componentTypeEditor/DerivedPropertiesSection.java @@ -330,12 +330,7 @@ public class DerivedPropertiesSection implements ComponentTypeViewerSection { SCLContext sclContext = SCLContext.getCurrent(); Object oldGraph = sclContext.get("graph"); try { - CompileSCLMonitorRequest compileSCLMonitorRequest = new CompileSCLMonitorRequest(graph, context) { - @Override - protected String getExpressionText(ReadGraph graph) throws DatabaseException { - return expression; - } - }; + CompileSCLMonitorRequest compileSCLMonitorRequest = new ValidationCompilationRequest(graph, context, expression); Function1 exp = graph.syncRequest(compileSCLMonitorRequest); sclContext.put("graph", graph); //return exp.apply(context.getParent(graph)); @@ -422,4 +417,29 @@ public class DerivedPropertiesSection implements ComponentTypeViewerSection { return 100.0; } + private static final class ValidationCompilationRequest extends CompileSCLMonitorRequest { + private final String expression; + + private ValidationCompilationRequest(ReadGraph graph, Variable context, String expression) + throws DatabaseException { + super(graph, context); + this.expression = expression; + } + + @Override + protected String getExpressionText(ReadGraph graph) throws DatabaseException { + return expression; + } + + @Override + public int hashCode() { + return super.hashCode() + 37 * expression.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj) && ((ValidationCompilationRequest)obj).expression.equals(expression); + } + } + } \ No newline at end of file diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalInstanceWithTemplate.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalInstanceWithTemplate.java index 9e5862480..df95cb90d 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalInstanceWithTemplate.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalInstanceWithTemplate.java @@ -44,8 +44,12 @@ public class SyncCurrentTypicalInstanceWithTemplate { return false; if (DatabaseJob.inProgress()) return false; - IResourceEditorInput input = (IResourceEditorInput) activeEditor.getEditorInput(); - return TypicalPropertyTester.isTypicalInstanceEditor(Simantics.getSession(), input.getResource()); + if (activeEditor.getEditorInput() instanceof IResourceEditorInput) { + IResourceEditorInput input = (IResourceEditorInput) activeEditor.getEditorInput(); + return TypicalPropertyTester.isTypicalInstanceEditor(Simantics.getSession(), input.getResource()); + } else { + return false; + } } @Execute diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalTemplateToInstances.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalTemplateToInstances.java index 3558aaa62..9936b603b 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalTemplateToInstances.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/handlers/e4/SyncCurrentTypicalTemplateToInstances.java @@ -43,8 +43,12 @@ public class SyncCurrentTypicalTemplateToInstances { return false; if (DatabaseJob.inProgress()) return false; - IResourceEditorInput input = (IResourceEditorInput) activeEditor.getEditorInput(); - return TypicalPropertyTester.isTypicalMasterEditor(Simantics.getSession(), input.getResource()); + if (activeEditor.getEditorInput() instanceof IResourceEditorInput) { + IResourceEditorInput input = (IResourceEditorInput) activeEditor.getEditorInput(); + return TypicalPropertyTester.isTypicalMasterEditor(Simantics.getSession(), input.getResource()); + } else { + return false; + } } @Execute diff --git a/bundles/org.simantics.modeling/scl/Simantics/Subscription.scl b/bundles/org.simantics.modeling/scl/Simantics/Subscription.scl index 01bc3f058..2672067c4 100644 --- a/bundles/org.simantics.modeling/scl/Simantics/Subscription.scl +++ b/bundles/org.simantics.modeling/scl/Simantics/Subscription.scl @@ -163,7 +163,7 @@ Example: create a subscription of module PO01 attribute PO11_PRESSURE to the def #430121 """ -addSubscription :: Variable -> Subscription +addSubscription :: Variable -> Subscription addSubscription variable = do model = modelOfVariable variable default = defaultSubscriptionFolder model diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/MigrateModel.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/MigrateModel.java index eb0b80709..4e472ded4 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/MigrateModel.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/MigrateModel.java @@ -11,24 +11,26 @@ *******************************************************************************/ package org.simantics.modeling; -import gnu.trove.set.hash.THashSet; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import org.simantics.databoard.Bindings; import org.simantics.databoard.util.URIStringUtils; +import org.simantics.datatypes.literal.GUID; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Statement; import org.simantics.db.WriteGraph; import org.simantics.db.common.NamedResource; +import org.simantics.db.common.procedure.adapter.TransientCacheListener; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.adapter.Instances; +import org.simantics.db.layer0.request.ChildrenByIdentifier; import org.simantics.db.layer0.util.Layer0Utils; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.layer0.Layer0; @@ -36,12 +38,18 @@ import org.simantics.structural.stubs.StructuralResource2; import org.simantics.structural2.modelingRules.AllowedConnectionTypes; import org.simantics.utils.ObjectUtils; import org.simantics.utils.datastructures.Triple; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import gnu.trove.set.hash.THashSet; /** * @author Antti Villberg */ public class MigrateModel { + public static final Logger LOGGER = LoggerFactory.getLogger(MigrateModel.class); + public static class MigrationOperation { public NamedResource instanceToMigrate; @@ -74,6 +82,15 @@ public class MigrateModel { private Resource getPossibleReplacement(ReadGraph graph, Resource type, Resource predicate) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); + + // Try to find a relation with the same GUID + GUID guid = graph.getPossibleRelatedValue(predicate, L0.identifier, GUID.BINDING); + Map children = graph.syncRequest(new ChildrenByIdentifier(type), TransientCacheListener.instance()); + Resource replacement = children.get(guid); + if (replacement != null) + return replacement; + + // Fall back to using relation name String name = graph.getPossibleRelatedValue(predicate, L0.HasName, Bindings.STRING); if(name == null) return null; Resource child = Layer0Utils.getPossibleChild(graph, type, name); @@ -110,6 +127,7 @@ public class MigrateModel { problems.append(" " + name + " was a property in the source type\n"); continue; } + String sourceValueType = graph.getPossibleRelatedValue(predicate, L0.RequiresValueType, Bindings.STRING); String replacementValueType = graph.getPossibleRelatedValue(replacement, L0.RequiresValueType, Bindings.STRING); if(!ObjectUtils.objectEquals(sourceValueType, replacementValueType)) { @@ -167,7 +185,29 @@ public class MigrateModel { Resource replacement = getPossibleReplacement(graph, type, predicate); graph.deny(stm); if(replacement == null) continue; - graph.claim(stm.getSubject(), replacement, stm.getObject()); + + // Filter out incompatible literal types. + // TODO: Show warning in UI + Resource object = stm.getObject(); + Resource replacementRange = graph.getPossibleObject(replacement, L0.HasRange); + if (replacementRange != null && !graph.isInstanceOf(object, replacementRange)) { + String instanceName = graph.getPossibleRelatedValue(instanceToMigrate, L0.HasName); + if (instanceName == null) instanceName = ""; + + String rangeName = graph.getPossibleRelatedValue(replacementRange, L0.HasName); + if (rangeName == null) rangeName = ""; + + Resource literalType= graph.getPossibleType(object, L0.Value); + String literalTypeName = literalType != null ? graph.getPossibleRelatedValue(literalType, L0.HasName) : null; + if (literalTypeName == null) literalTypeName = ""; + + String replacementName = graph.getRelatedValue(replacement, L0.HasName); + LOGGER.warn("{}: Ignored incompatible value of type {} for predicate {} with range {}", instanceName, literalTypeName, replacementName, rangeName); + + continue; + } + + graph.claim(stm.getSubject(), replacement, object); } } diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/mapping/DiagramToCompositeMapping3.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/mapping/DiagramToCompositeMapping3.java index 783084565..f9feea9e2 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/mapping/DiagramToCompositeMapping3.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/mapping/DiagramToCompositeMapping3.java @@ -195,14 +195,20 @@ public class DiagramToCompositeMapping3 extends MappingBase { destructiveConnectionRule() ), // If component does not have a corresponding element in the diagram, remove it - and(deny(b(new Tag(MOD.ComponentToElement), Component)), deny(exists(Component))) + and( + deny(b(new Tag(MOD.ComponentToElement), Component)), + deny(b(new Tag(STR.IsConnectedTo), Component)), + deny(exists(Component))) ) ) ), if_(b(mappedFromConnector, Component), // handle only mapped components query( unless(bf(MOD.ComponentToConnector, Component, Connector), - and(deny(b(new Tag(MOD.ComponentToElement), Component)), deny(exists(Component))) + and( + deny(b(new Tag(MOD.ComponentToElement), Component)), + deny(b(new Tag(STR.IsConnectedTo), Component)), + deny(exists(Component))) ) ) ) diff --git a/bundles/org.simantics.scl.compiler/scl/SCL/CommandSession.scl b/bundles/org.simantics.scl.compiler/scl/SCL/CommandSession.scl new file mode 100644 index 000000000..33b4dd7fc --- /dev/null +++ b/bundles/org.simantics.scl.compiler/scl/SCL/CommandSession.scl @@ -0,0 +1,19 @@ +// This module is meant to be imported with namespace +import "SCL/ModuleRepository" +import "SafeDynamic" + +importJava "org.simantics.scl.compiler.commands.CommandSession" where + data CommandSession + + @JavaName "" + create :: ModuleRepository -> CommandSession + + execute :: CommandSession -> String -> () + + @JavaName getVariableValueAndType + get :: CommandSession -> String -> Maybe SafeDynamic + @JavaName setVariable + set :: CommandSession -> String -> SafeDynamic -> () + + @JavaName removeVariable + remove :: CommandSession -> String -> () \ No newline at end of file diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/commands/CommandSession.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/commands/CommandSession.java index 46cd0cb45..00a7714a6 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/commands/CommandSession.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/commands/CommandSession.java @@ -17,6 +17,7 @@ import java.util.Set; import org.simantics.scl.compiler.common.names.Names; import org.simantics.scl.compiler.constants.StringConstant; +import org.simantics.scl.compiler.dynamic.SafeDynamic; import org.simantics.scl.compiler.elaboration.expressions.EApply; import org.simantics.scl.compiler.elaboration.expressions.EBlock; import org.simantics.scl.compiler.elaboration.expressions.EConstant; @@ -32,6 +33,7 @@ import org.simantics.scl.compiler.environment.Environment; import org.simantics.scl.compiler.environment.LocalEnvironment; import org.simantics.scl.compiler.environment.specification.EnvironmentSpecification; import org.simantics.scl.compiler.errors.CompilationError; +import org.simantics.scl.compiler.errors.ErrorSeverity; import org.simantics.scl.compiler.errors.Locations; import org.simantics.scl.compiler.internal.codegen.utils.NameMangling; import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException; @@ -83,6 +85,10 @@ public class CommandSession { */ private boolean validateOnly; + public CommandSession(ModuleRepository moduleRepository) { + this(moduleRepository, SCLReporting.getCurrentReportingHandler()); + } + public CommandSession(ModuleRepository moduleRepository, SCLReportingHandler handler) { this.moduleRepository = moduleRepository; this.defaultHandler = new PrintDecorator( @@ -123,7 +129,8 @@ public class CommandSession { defaultHandler.printError(failure.toString()); if(failure.reason instanceof CompilationError[]) for(CompilationError error : (CompilationError[])failure.reason) { - defaultHandler.printError(" " + error.description); + if(error.severity != ErrorSeverity.WARNING) + defaultHandler.printError(" " + error.description); } } for(CommandSessionImportEntry entry : importEntries) @@ -613,6 +620,11 @@ public class CommandSession { variableTypes.put(name, type); } + public void setVariable(String name, SafeDynamic typeAndValue) { + variableValues.put(name, typeAndValue.value); + variableTypes.put(name, typeAndValue.type_); + } + public Object getVariableValue(String name) { return variableValues.get(name); } @@ -621,6 +633,14 @@ public class CommandSession { return variableTypes.get(name); } + public SafeDynamic getVariableValueAndType(String name) { + Type type = variableTypes.get(name); + if(type == null) + return null; + Object value = variableValues.get(name); + return new SafeDynamic(type, value); + } + public void removeVariable(String name) { variableValues.remove(name); variableTypes.remove(name); @@ -657,7 +677,7 @@ public class CommandSession { formatException(handler, e); } } - + public static CompilationError[] validate(ModuleRepository moduleRepository,StringReader commandReader) { CommandSession session = new CommandSession(moduleRepository, null); return session.validate(commandReader); diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/JavaStaticMethod.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/JavaStaticMethod.java index 9e071e9cb..a72b844ba 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/JavaStaticMethod.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/JavaStaticMethod.java @@ -56,6 +56,8 @@ public class JavaStaticMethod extends FunctionValue { if(td.equals(TypeDesc.VOID)) throw new InternalCompilerError(); } + if( (returnTypeDesc == null) != (parameterTypeDescs == null) ) + throw new IllegalArgumentException("Either specify both returnTypeDesc and parameterTypeDescs or neither"); ClassBuilder.checkClassName(className); this.className = className; this.methodName = methodName; @@ -73,8 +75,11 @@ public class JavaStaticMethod extends FunctionValue { } @Override - public Type applyExact(MethodBuilder mb, Val[] parameters) { - if(returnTypeDesc == null) { + public Type applyExact(MethodBuilder mb, Val[] parameters) { + if(returnTypeDesc == null || parameterTypeDescs == null) { + // This method may be called from multiple threads at the same time when returnTypeDesc + // and parameterTypeDescs are uninitialized. Double initialization is OK in this case, + // but because there are two fields, we have to check that both are initialized. JavaTypeTranslator tt = mb.getJavaTypeTranslator(); returnTypeDesc = tt.toTypeDesc(returnType); parameterTypeDescs = JavaTypeTranslator.filterVoid( diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRQuery.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRQuery.java index 7bee6eae0..6b01d9953 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRQuery.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRQuery.java @@ -10,8 +10,11 @@ import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext; import org.simantics.scl.compiler.elaboration.contexts.TranslationContext; import org.simantics.scl.compiler.elaboration.contexts.TypingContext; import org.simantics.scl.compiler.elaboration.expressions.Expression; +import org.simantics.scl.compiler.elaboration.expressions.ExpressionVisitor; import org.simantics.scl.compiler.elaboration.expressions.Variable; import org.simantics.scl.compiler.elaboration.expressions.printing.ExpressionToStringVisitor; +import org.simantics.scl.compiler.elaboration.expressions.visitors.ForVariablesUsesVisitor; +import org.simantics.scl.compiler.elaboration.expressions.visitors.StandardExpressionVisitor; import org.simantics.scl.compiler.errors.Locations; import org.simantics.scl.compiler.internal.parsing.Symbol; @@ -92,4 +95,16 @@ public class CHRQuery extends Symbol { newLiterals[i] = literals[i].replace(context); return new CHRQuery(location, newLiterals); } + + public void accept(ExpressionVisitor visitor) { + for(CHRLiteral literal : literals) { + if(literal == null || literal.parameters == null) + continue; // FIXME why this happens? + for(Expression parameter : literal.parameters) { + if(parameter == null) + continue; // FIXME why this happens? + parameter.accept(visitor); + } + } + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRRule.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRRule.java index 78224ebb0..e698d72c6 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRRule.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRRule.java @@ -1,6 +1,7 @@ package org.simantics.scl.compiler.elaboration.chr; import java.util.ArrayList; +import java.util.HashMap; import org.simantics.scl.compiler.compilation.CompilationContext; import org.simantics.scl.compiler.elaboration.chr.plan.CHRSearchPlan; @@ -9,9 +10,13 @@ import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint; import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext; import org.simantics.scl.compiler.elaboration.contexts.TranslationContext; import org.simantics.scl.compiler.elaboration.contexts.TypingContext; +import org.simantics.scl.compiler.elaboration.expressions.EAsPattern; import org.simantics.scl.compiler.elaboration.expressions.EVariable; +import org.simantics.scl.compiler.elaboration.expressions.Expression; +import org.simantics.scl.compiler.elaboration.expressions.ExpressionVisitor; import org.simantics.scl.compiler.elaboration.expressions.Variable; import org.simantics.scl.compiler.elaboration.expressions.printing.ExpressionToStringVisitor; +import org.simantics.scl.compiler.elaboration.expressions.visitors.StandardExpressionVisitor; import org.simantics.scl.compiler.errors.Locations; import org.simantics.scl.compiler.internal.parsing.Symbol; import org.simantics.scl.compiler.types.Types; @@ -54,6 +59,52 @@ public class CHRRule extends Symbol { context.disallowNewExistentials(); body.resolve(context); existentialVariables = context.popExistentialFrame(); + + warnForExistentialsUsedOnlyOnce(context); + } + + private static final Object NEVER_USED = new Object(); + + private void warnForExistentialsUsedOnlyOnce(TranslationContext context) { + // Initialize the hash map + HashMap usageCount = new HashMap<>(existentialVariables.length); + for(Variable var : existentialVariables) + if(!var.getName().equals("_")) + usageCount.put(var, NEVER_USED); + + // Collect variable uses + ExpressionVisitor visitor = new StandardExpressionVisitor() { + private void handle(Expression expression, Variable variable) { + Object object = usageCount.remove(variable); + if(object == NEVER_USED) + usageCount.put(variable, expression); + } + @Override + public void visit(EVariable expression) { + if(expression.variable != null) + handle(expression, expression.variable); + } + @Override + public void visit(EAsPattern expression) { + expression.pattern.accept(this); + handle(expression, expression.var); + } + }; + head.accept(visitor); + body.accept(visitor); + + // Report as warnings + usageCount.forEach((variable, expression_) -> { + if(!(expression_ instanceof Expression)) + return; // Should never happen + Expression expression = (Expression)expression_; + if(context.isExpandedFromWildcard(expression)) + return; + + context.getErrorLog().logWarning(expression.location, + "Existential variable " + variable.getName() + " is referred only once. Replace by _ if this is a wildcard."); + }); + } public void checkType(TypingContext context) { diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/contexts/TranslationContext.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/contexts/TranslationContext.java index d8346f41f..e0c256599 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/contexts/TranslationContext.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/contexts/TranslationContext.java @@ -76,6 +76,8 @@ public class TranslationContext extends TypeTranslationContext implements Enviro TIntArrayList chrConstraintFrames = new TIntArrayList(); ArrayList chrConstraintEntries = new ArrayList(); + private THashSet expandedFromWildcard; + public CHRRuleset currentRuleset; public ModuleDebugInfo moduleDebugInfo; @@ -141,7 +143,7 @@ public class TranslationContext extends TypeTranslationContext implements Enviro variable = new Variable(name); variables.put(name, variable); existentialFrame.variables.add(name); - return new EVariable(variable); + return new EVariable(location, variable); } case '_': { if(name.length()==1) { @@ -583,7 +585,29 @@ public class TranslationContext extends TypeTranslationContext implements Enviro return Environments.getRuleset(environment, name); } + /** + * Tells that new existential variables are no longer allowed in this context. + */ public void disallowNewExistentials() { getCurrentExistentialFrame().disallowNewExistentials = true; } + + /** + * Marks that the expression is a result of expanding .. wildcard pattern in records. + */ + public void addExpandedFromWildcard(Expression expression) { + if(expandedFromWildcard == null) + expandedFromWildcard = new THashSet<>(); + expandedFromWildcard.add(expression); + } + + /** + * Asks if the expression is a result of expanding .. wildcard pattern in records. + */ + public boolean isExpandedFromWildcard(Expression expression) { + if(expandedFromWildcard == null) + return false; + else + return expandedFromWildcard.contains(expression); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/ERecord.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/ERecord.java index 47ef205df..cb34d0fb3 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/ERecord.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/ERecord.java @@ -150,7 +150,9 @@ public class ERecord extends ASTExpression { String variableName = fieldNames[i]; if(chrLiteral) variableName = "?" + variableName; - parameters[i] = new EVar(wildcardField.location, variableName); + EVar expandedVar = new EVar(wildcardField.location, variableName); + parameters[i] = expandedVar; + context.addExpandedFromWildcard(expandedVar); } } if(!recordMap.isEmpty()) { diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/EVar.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/EVar.java index d78fa8565..2e4809a0c 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/EVar.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/EVar.java @@ -49,7 +49,10 @@ public class EVar extends ASTExpression { @Override public Expression resolve(TranslationContext context) { - return context.resolveVariable(location, name); + Expression resolved = context.resolveVariable(location, name); + if(context.isExpandedFromWildcard(this)) + context.addExpandedFromWildcard(resolved); + return resolved; } @Override @@ -59,7 +62,10 @@ public class EVar extends ASTExpression { @Override public Expression resolveAsPattern(TranslationContext context) { - return context.resolvePattern(this); + Expression resolved = context.resolvePattern(this); + if(context.isExpandedFromWildcard(this)) + context.addExpandedFromWildcard(resolved); + return resolved; } @Override diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/VariableProcedure.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/VariableProcedure.java index 44e9b6ae7..af50692fa 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/VariableProcedure.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/VariableProcedure.java @@ -1,5 +1,6 @@ package org.simantics.scl.compiler.elaboration.expressions; +@FunctionalInterface public interface VariableProcedure { void execute(long location, Variable variable); } diff --git a/bundles/org.simantics.scl.runtime/scl/Iterator.scl b/bundles/org.simantics.scl.runtime/scl/Iterator.scl index 19e51a0d5..b98e14f42 100644 --- a/bundles/org.simantics.scl.runtime/scl/Iterator.scl +++ b/bundles/org.simantics.scl.runtime/scl/Iterator.scl @@ -1,3 +1,5 @@ +import "JavaBuiltin" as Java + importJava "java.util.Iterator" where data T a @@ -16,6 +18,17 @@ iter f it = loop () loop () else () +@inline +iterI :: (Integer -> a -> b) -> T a -> () +iterI f it = loop 0 + where + loop i = + if hasNext it + then do + f i (next it) + loop (Java.iadd i 1) + else () + @inline iterB :: (a -> Boolean) -> T a -> Boolean iterB f it = loop () diff --git a/bundles/org.simantics.scl.runtime/scl/Prelude.scl b/bundles/org.simantics.scl.runtime/scl/Prelude.scl index 80b4d0ad7..e92603bec 100644 --- a/bundles/org.simantics.scl.runtime/scl/Prelude.scl +++ b/bundles/org.simantics.scl.runtime/scl/Prelude.scl @@ -963,6 +963,9 @@ A class of monads with zero element satisfying """ class (Monad m) => MonadZero m where mzero :: m a + mfilter :: (a -> Boolean) -> m a -> m a + + mfilter p m = m >>= (\x -> if p x then return x else mzero) "Injects a boolean test to a type beloning to `MonadZero`." guard :: MonadZero m => Boolean -> m () @@ -1105,7 +1108,21 @@ instance MonadE (Either a) where instance MonadE [] where bindE l f = concatMap f l + +/// MZeroE /// + +class (MonadE m, MonadZero m) => MonadZeroE m where + filter :: (a -> Boolean) -> m a -> m a + + filter p m = m `bindE` (\x -> if p x then return x else mzero) +instance MonadZeroE [] where + filter = filterList + +instance MonadZeroE Maybe where + filter p (Just x) | not (p x) = Nothing + filter _ m = m + /// Category /// "Identity function." @@ -1504,11 +1521,11 @@ snd :: (a,b) -> b snd (x,y) = y @inline -mapFst :: (a -> b) -> (a,c) -> (b,c) +mapFst :: (a -> b) -> (a,c) -> (b,c) mapFst f (x,y) = (f x, y) @inline -mapSnd :: (a -> b) -> (c,a) -> (c,b) +mapSnd :: (a -> b) -> (c,a) -> (c,b) mapSnd f (x,y) = (x, f y) instance (Ord a, Ord b) => Ord (a, b) where @@ -1841,10 +1858,10 @@ foldr1 f l = loop (l!(len-1)) (len-2) `filter pred lst` returns those elements of `lst` that the predicate `pred` accepts. For example filter (> 3) [1, 2, 3, 4, 5, 6] = [4, 5, 6] -""" +""" @inline -filter :: (a -> Boolean) -> [a] -> [a] -filter p l = build (\empty cons -> foldl (\cur x -> if p x then cons cur x else cur) empty l) +filterList :: (a -> Boolean) -> [a] -> [a] +filterList p l = build (\empty cons -> foldl (\cur x -> if p x then cons cur x else cur) empty l) """ Takes those elements of the input list that match `(Just x)` and adds the contents to the resulting list. For example, diff --git a/bundles/org.simantics.scl.runtime/scl/Set.scl b/bundles/org.simantics.scl.runtime/scl/Set.scl index 2bf2911c5..34033d91e 100644 --- a/bundles/org.simantics.scl.runtime/scl/Set.scl +++ b/bundles/org.simantics.scl.runtime/scl/Set.scl @@ -11,13 +11,31 @@ importJava "java.util.Set" where iterator :: T a -> Iterator.T a @inline -iter :: (a -> ()) -> T a -> () +iter :: (a -> b) -> T a -> () iter f s = runProc (Iterator.iter f (iterator s)) @inline iterB :: (a -> Boolean) -> T a -> Boolean iterB f s = runProc (Iterator.iterB f (iterator s)) +@inline +iterI :: (Integer -> a -> b) -> T a -> () +iterI f s = runProc (Iterator.iterI f (iterator s)) + @inline fold :: (a -> b -> a) -> a -> T b -> a fold f init s = runProc (Iterator.fold f init (iterator s)) + +importJava "java.util.Collections" where + singleton :: a -> T a + + @JavaName emptySet + empty :: T a + +importJava "gnu.trove.set.hash.THashSet" where + @JavaName "" + fromList :: [a] -> T a + +importJava "java.util.ArrayList" where + @JavaName "" + toList :: T a -> [a] diff --git a/bundles/org.simantics.scl.runtime/scl/SetClasses.scl b/bundles/org.simantics.scl.runtime/scl/SetClasses.scl new file mode 100644 index 000000000..8c3d1e6a8 --- /dev/null +++ b/bundles/org.simantics.scl.runtime/scl/SetClasses.scl @@ -0,0 +1,61 @@ +import "Prelude" +import "MSet" as MSet +import "Set" as Set + +instance Functor Set.T where + fmap = map + +instance FunctorE Set.T where + map f set = runProc do + result = MSet.create () + Set.iter (\x -> MSet.add result $ f x) set + MSet.freeze result + + iter = Set.iter + iterI = Set.iterI + +instance Monad Set.T where + return = Set.singleton + (>>=) = bindE + +@private +importJava "java.util.Set" where + @JavaName addAll + addAll' :: MSet.T a -> Set.T a -> Boolean + +instance MonadE Set.T where + set `bindE` f = runProc do + result = MSet.create () + Set.iter (\x -> addAll' result $ f x) set + MSet.freeze result + +instance MonadZero Set.T where + mzero = Set.empty + +instance MonadZeroE Set.T where + filter p set = runProc do + result = MSet.create () + Set.iter (\x -> + if p x + then ignore $ MSet.add result x + else () + ) set + MSet.freeze result + +instance (Show a) => Show (Set.T a) where + sb <+ set = do + sb << "{" + Set.iterI (\i x -> (if i > 0 then sb << ", " else sb) <+ x) set + sb << "}" + +instance Additive (Set.T a) where + zero = Set.empty + a + b = runProc do + result = MSet.create () + Set.iter (MSet.add result) a + Set.iter (MSet.add result) b + MSet.freeze result + sum sets = runProc do + result = MSet.create () + iter (Set.iter (MSet.add result)) sets + MSet.freeze result \ No newline at end of file diff --git a/bundles/org.simantics.scl.runtime/scl/SetUtils.scl b/bundles/org.simantics.scl.runtime/scl/SetUtils.scl deleted file mode 100644 index 00fa75215..000000000 --- a/bundles/org.simantics.scl.runtime/scl/SetUtils.scl +++ /dev/null @@ -1,13 +0,0 @@ -import "Prelude" -import "Set" as Set -import "MSet" as MSet -import "MList" as MList - -fromList :: [a] -> Set.T a -fromList l = runProc (MSet.freeze $ MSet.fromList l) - -toList :: Set.T a -> [a] -toList s = runProc do - result = MList.createC (Set.size s) - Set.iter (MList.add result) s - MList.freeze result \ No newline at end of file diff --git a/bundles/org.simantics.scl.runtime/scl/StandardLibrary.scl b/bundles/org.simantics.scl.runtime/scl/StandardLibrary.scl index 7234b70be..e774f9746 100644 --- a/bundles/org.simantics.scl.runtime/scl/StandardLibrary.scl +++ b/bundles/org.simantics.scl.runtime/scl/StandardLibrary.scl @@ -10,7 +10,7 @@ include "Lazy" as Lazy include "File" as File include "Serialization" as Serialization include "Set" as Set -include "SetUtils" as Set +include "SetClasses" //include "Map" as Map include "MMap" as MMap include "MSet" as MSet diff --git a/bundles/org.simantics.scl.runtime/src/org/simantics/scl/runtime/reporting/SCLReporting.java b/bundles/org.simantics.scl.runtime/src/org/simantics/scl/runtime/reporting/SCLReporting.java index 97ef75f8b..5266b13a2 100644 --- a/bundles/org.simantics.scl.runtime/src/org/simantics/scl/runtime/reporting/SCLReporting.java +++ b/bundles/org.simantics.scl.runtime/src/org/simantics/scl/runtime/reporting/SCLReporting.java @@ -18,6 +18,11 @@ public class SCLReporting { private static final Logger LOGGER = LoggerFactory.getLogger(SCLReporting.class); + public static SCLReportingHandler getCurrentReportingHandler() { + SCLReportingHandler handler = ((SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER)); + return handler == null ? SCLReportingHandler.DEFAULT : handler; + } + public static void print(String text) { SCLReportingHandler handler = ((SCLReportingHandler)SCLContext.getCurrent().get(SCLReportingHandler.REPORTING_HANDLER)); if(handler != null) diff --git a/bundles/org.simantics.ui/src/org/simantics/ui/selection/WorkbenchSelectionUtils.java b/bundles/org.simantics.ui/src/org/simantics/ui/selection/WorkbenchSelectionUtils.java index 111ac8a3a..e3e6eeafe 100644 --- a/bundles/org.simantics.ui/src/org/simantics/ui/selection/WorkbenchSelectionUtils.java +++ b/bundles/org.simantics.ui/src/org/simantics/ui/selection/WorkbenchSelectionUtils.java @@ -11,6 +11,7 @@ import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.handlers.HandlerUtil; import org.simantics.Simantics; +import org.simantics.databoard.type.Datatype; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; @@ -19,8 +20,8 @@ import org.simantics.db.common.primitiverequest.Supertypes; import org.simantics.db.common.primitiverequest.Types; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.PossibleGUID; -import org.simantics.db.layer0.request.PossibleVariableGUID; import org.simantics.db.layer0.request.PossibleURI; +import org.simantics.db.layer0.request.PossibleVariableGUID; import org.simantics.db.layer0.request.PossibleVariableRepresents; import org.simantics.db.layer0.request.VariableRead; import org.simantics.db.layer0.request.VariableURI; @@ -108,12 +109,30 @@ public class WorkbenchSelectionUtils { if(var != null) { String uri = processor.syncRequest(new VariableURI(var)); String guid = processor.syncRequest(new PossibleVariableGUID(var)); + + Set classifications = processor.syncRequest(new VariableRead>(var) { + @Override + public Set perform(ReadGraph graph) throws DatabaseException { + return var.getClassifications(graph); + } + }); + String classificationsStr = toJSONStringArray(new ArrayList<>(classifications)); + + Datatype datatype = processor.syncRequest(new VariableRead(var) { + @Override + public Datatype perform(ReadGraph graph) throws DatabaseException { + return var.getPossibleDatatype(graph); + } + }); + return toJSONObjectString( "type", "\"Variable\"", "uri", safeQuotedString(uri), "guid", safeQuotedString(guid), "resourceId", res == null ? "" : Long.toString(res.getResourceId()), - "typeResources", typesStr); + "typeResources", typesStr, + "classifications", classificationsStr, + "datatype", safeQuotedString(datatype != null ? datatype.getClass().getName() : null)); } if(res != null) { String uri = processor.syncRequest(new PossibleURI(res)); diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR4.scl b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR4.scl index dc0714973..90b82e79a 100644 --- a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR4.scl +++ b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR4.scl @@ -3,7 +3,9 @@ main = () when ?x <- ?y then True -- +3:10-3:12: Existential variable ?x is referred only once. Replace by _ if this is a wildcard. 3:10-3:18: Cannot solve the query. +3:16-3:18: Existential variable ?y is referred only once. Replace by _ if this is a wildcard. -- import "Prelude" diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR8.scl b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR8.scl index dd915ebb5..6929a0b92 100644 --- a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR8.scl +++ b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR8.scl @@ -5,4 +5,5 @@ main = () where X ?x => Y ?y -- +6:7-6:9: Existential variable ?x is referred only once. Replace by _ if this is a wildcard. 6:15-6:17: New existential variables can be defined only in queries. \ No newline at end of file