private static State FindStartState(Machine machine, ITranslationErrorHandler handler) { var foundStartState = false; foreach (State state in machine.AllStates()) { if (state == machine.StartState || state.IsStart) { if (!foundStartState) { foundStartState = true; } else { throw handler.InternalError(state.SourceLocation, $"Two start states {state.Name} occurs twice in all states list"); } } } if (foundStartState && machine.StartState == null) { throw handler.InternalError(machine.SourceLocation, "machine has unregistered start state"); } if (!foundStartState || machine.StartState == null) { // Allow machines with no start state so long as there are no other states. throw handler.MissingStartState(machine); } return(machine.StartState); }
public override PLanguageType VisitNamedType(PParser.NamedTypeContext context) { string typeName = context.name.GetText(); if (scope.Lookup(typeName, out PEnum pEnum)) { return(new EnumType(pEnum)); } if (scope.Lookup(typeName, out TypeDef typeDef)) { if (visitedTypeDefs.Contains(typeDef)) { throw handler.CircularTypeDef(context.name, typeDef); } if (typeDef.Type == null) { visitedTypeDefs.Push(typeDef); switch (typeDef.SourceLocation) { case PParser.ForeignTypeDefContext foreignType: typeDef.Type = new ForeignType(foreignType.name.GetText()); break; case PParser.PTypeDefContext typedefDecl: typeDef.Type = Visit(typedefDecl.type()); break; case InterpreterRuleContext _: case RuleContextWithAltNum _: default: throw handler.InternalError(typeDef.SourceLocation, new ArgumentOutOfRangeException(nameof(context))); } visitedTypeDefs.Pop(); } return(new TypeDefType(typeDef)); } if (scope.Lookup(typeName, out NamedEventSet eventSet)) { return(new PermissionType(eventSet)); } if (scope.Lookup(typeName, out Interface pInterface)) { return(new PermissionType(pInterface)); } if (scope.Lookup(typeName, out Machine machine)) { return(new PermissionType(machine)); } throw handler.MissingDeclaration(context.name, "enum, typedef, event set, machine, or interface", typeName); }
public override IPExpr VisitKeywordExpr(PParser.KeywordExprContext context) { switch (context.fun.Text) { case "keys": { IPExpr expr = Visit(context.expr()); if (!(expr.Type.Canonicalize() is MapType mapType)) { throw handler.TypeMismatch(expr, TypeKind.Map); } return(new KeysExpr(context, expr, new SequenceType(mapType.KeyType))); } case "values": { IPExpr expr = Visit(context.expr()); if (!(expr.Type.Canonicalize() is MapType mapType)) { throw handler.TypeMismatch(expr, TypeKind.Map); } return(new ValuesExpr(context, expr, new SequenceType(mapType.ValueType))); } case "sizeof": { var expr = Visit(context.expr()); if (!(expr.Type.Canonicalize() is SequenceType) && !(expr.Type.Canonicalize() is MapType) && !(expr.Type.Canonicalize() is SetType)) { throw handler.TypeMismatch(expr, TypeKind.Map, TypeKind.Sequence, TypeKind.Set); } return(new SizeofExpr(context, expr)); } case "default": { PLanguageType type = TypeResolver.ResolveType(context.type(), table, handler); return(new DefaultExpr(context, type.Canonicalize())); } default: { throw handler.InternalError(context, new ArgumentException($"Unknown keyword expression {context.fun.Text}", nameof(context))); } } }
public void CheckWellFormedness(IPModuleExpr moduleExpr) { switch (moduleExpr) { case AssertModuleExpr assertExpr: CheckWellFormedness(assertExpr); break; case BindModuleExpr bindExpr: CheckWellFormedness(bindExpr); break; case RenameModuleExpr renameExpr: CheckWellFormedness(renameExpr); break; case UnionOrComposeModuleExpr uOrCExpr: CheckWellFormedness(uOrCExpr); break; case HideEventModuleExpr hideEExpr: CheckWellFormedness(hideEExpr); break; case HideInterfaceModuleExpr hideIExpr: CheckWellFormedness(hideIExpr); break; default: throw handler.InternalError(moduleExpr.SourceLocation, new ArgumentOutOfRangeException(nameof(moduleExpr))); } }
public override PLanguageType VisitNamedType(PParser.NamedTypeContext context) { string typeName = context.name.GetText(); if (scope.Lookup(typeName, out PEnum pEnum)) { return(new EnumType(pEnum)); } if (scope.Lookup(typeName, out TypeDef typeDef)) { if (visitedTypeDefs.Contains(typeDef)) { throw handler.CircularTypeDef(context.name, typeDef); } if (typeDef.Type == null) { visitedTypeDefs.Add(typeDef); switch (typeDef.SourceLocation) { case PParser.ForeignTypeDefContext foreignType: typeDef.Type = new ForeignType(foreignType.name.GetText()); break; case PParser.PTypeDefContext typedefDecl: typeDef.Type = Visit(typedefDecl.type()); break; default: throw handler.InternalError(typeDef.SourceLocation, $"Grammar changed without updating {nameof(TypeVisitor)}"); } } return(new TypeDefType(typeDef)); } if (scope.Lookup(typeName, out NamedEventSet eventSet)) { return(new PermissionType(eventSet)); } if (scope.Lookup(typeName, out Interface pInterface)) { return(new PermissionType(pInterface)); } if (scope.Lookup(typeName, out Machine machine)) { return(new PermissionType(machine)); } throw handler.MissingDeclaration(context.name, "enum, typedef, event set, machine, or interface", typeName); }
public static void ValidateNoStaticHandlers(ITranslationErrorHandler handler, Machine machine) { foreach (State state in machine.AllStates()) { bool illegalUsage = state.Entry != null && IsStaticOrForeign(state.Entry); if (illegalUsage) { throw handler.StaticFunctionNotAllowedAsHandler(state.SourceLocation, state.Entry.Name); } illegalUsage = state.Exit != null && IsStaticOrForeign(state.Exit); if (illegalUsage) { throw handler.StaticFunctionNotAllowedAsHandler(state.SourceLocation, state.Exit.Name); } foreach (KeyValuePair <PEvent, AST.IStateAction> pair in state.AllEventHandlers) { switch (pair.Value) { case EventDoAction eventDoAction: if (eventDoAction.Target != null && IsStaticOrForeign(eventDoAction.Target)) { throw handler.StaticFunctionNotAllowedAsHandler(eventDoAction.SourceLocation, eventDoAction.Target.Name); } break; case EventGotoState eventGotoState: if (eventGotoState.TransitionFunction != null && IsStaticOrForeign(eventGotoState.TransitionFunction)) { throw handler.StaticFunctionNotAllowedAsHandler(eventGotoState.SourceLocation, eventGotoState.TransitionFunction.Name); } break; case EventDefer _: case EventIgnore _: case EventPushState _: break; default: throw handler.InternalError(pair.Value.SourceLocation, new System.Exception("Unknown transition type parsed, report to the P team")); } } } }
private static PLanguageType GetStatePayload(State startState, ITranslationErrorHandler handler) { if (!(startState.Entry?.Signature.Parameters.Count > 0)) { return(PrimitiveType.Null); } if (startState.Entry.Signature.Parameters.Count != 1) { throw handler.InternalError(startState.OwningMachine.SourceLocation, "Allowed start state entry with multiple parameters"); } return(startState.Entry.Signature.Parameters[0].Type); }
public override IPStmt VisitAssignStmt(PParser.AssignStmtContext context) { IPExpr variable = exprVisitor.Visit(context.lvalue()); IPExpr value = exprVisitor.Visit(context.rvalue()); // If we're doing a move/swap assignment if (value is ILinearRef linearRef) { Variable refVariable = linearRef.Variable; switch (linearRef.LinearType) { case LinearType.Move: // Moved values must be subtypes of their destinations if (!variable.Type.IsAssignableFrom(refVariable.Type)) { throw handler.TypeMismatch(context.rvalue(), refVariable.Type, variable.Type); } return(new MoveAssignStmt(context, variable, refVariable)); case LinearType.Swap: // Within a function, swaps must only be subtyped in either direction // the actual types are checked at runtime. This is to allow swapping // with the `any` type. if (!variable.Type.IsAssignableFrom(refVariable.Type) && !refVariable.Type.IsAssignableFrom(variable.Type)) { throw handler.TypeMismatch(context.rvalue(), refVariable.Type, variable.Type); } return(new SwapAssignStmt(context, variable, refVariable)); default: throw handler.InternalError(linearRef.SourceLocation, new ArgumentOutOfRangeException(nameof(context))); } } // If this is a value assignment, we just need subtyping if (!variable.Type.IsAssignableFrom(value.Type)) { throw handler.TypeMismatch(context.rvalue(), value.Type, variable.Type); } return(new AssignStmt(context, variable, value)); }
private static void ValidateEventPayloadToTransitionTarget(ITranslationErrorHandler handler, ParserRuleContext sourceLocation, PLanguageType eventPayloadType, Function targetFunction) { IReadOnlyList <PLanguageType> entrySignature = targetFunction.Signature.ParameterTypes.ToList(); if (entrySignature.Count == 0) { return; } if (entrySignature.Count > 1) { throw handler.InternalError(sourceLocation, new System.Exception("Target function cannot have multiple parameters (report this to the P developers)")); } if (entrySignature.Count == 1 && entrySignature[0].IsAssignableFrom(eventPayloadType)) { return; } if (entrySignature.Count == 1 && eventPayloadType.Canonicalize() is TupleType tuple && tuple.Types.Count == 1 && entrySignature[0].IsAssignableFrom(tuple.Types[0])) { return; } if (entrySignature.Count == 1) { throw handler.TypeMismatch(sourceLocation, eventPayloadType, entrySignature[0]); } PLanguageType entrySignatureType = new TupleType(entrySignature.ToArray()); if (!entrySignatureType.IsAssignableFrom(eventPayloadType)) { throw handler.TypeMismatch(sourceLocation, eventPayloadType, entrySignatureType); } }
public static void ValidateMachine(ITranslationErrorHandler handler, Machine machine) { State startState = FindStartState(machine, handler); PLanguageType startStatePayloadType = GetStatePayload(startState, handler); if (!startStatePayloadType.IsSameTypeAs(machine.PayloadType)) { throw handler.InternalError(machine.SourceLocation, "machine payload type is not the same as start state's entry payload type"); } foreach (Interface machineInterface in machine.Interfaces) { if (!machine.PayloadType.IsAssignableFrom(machineInterface.PayloadType)) { // TODO: add special "invalid machine interface" error throw handler.TypeMismatch(machine.StartState.Entry?.SourceLocation ?? machine.SourceLocation, machine.PayloadType, machineInterface.PayloadType); } } }
private ISet <Variable> ProcessStatement(ISet <Variable> unavailable, IPStmt statement) { Contract.Requires(statement != null); switch (statement) { case CompoundStmt compoundStmt: unavailable = compoundStmt.Statements.Aggregate(unavailable, ProcessStatement); break; case AssertStmt assertStmt: unavailable = ProcessExpr(unavailable, assertStmt.Assertion); break; case PrintStmt printStmt: unavailable = ProcessArgList(printStmt.Args, unavailable, ArgOptions.SwapNotAllowed); break; case ReturnStmt returnStmt: if (returnStmt.ReturnValue != null) { unavailable = ProcessExpr(unavailable, returnStmt.ReturnValue); } break; case AssignStmt assignStmt: unavailable = ProcessExpr(unavailable, assignStmt.Value); if (assignStmt.Location is VariableAccessExpr assignAccess) { unavailable.Remove(assignAccess.Variable); } else { unavailable = ProcessExpr(unavailable, assignStmt.Location); } break; case MoveAssignStmt moveAssignStmt: if (moveAssignStmt.FromVariable.Role.Equals(VariableRole.Field)) { throw handler.MovedField(moveAssignStmt); } unavailable.Add(moveAssignStmt.FromVariable); if (moveAssignStmt.ToLocation is VariableAccessExpr moveAssignAccess) { unavailable.Remove(moveAssignAccess.Variable); } else { unavailable = ProcessExpr(unavailable, moveAssignStmt.ToLocation); } break; case SwapAssignStmt swapAssignStmt: if (swapAssignStmt.NewLocation is VariableAccessExpr swapAssignAccess) { if (unavailable.Contains(swapAssignAccess.Variable)) { throw handler.SwapAssignUnavailable(swapAssignStmt, swapAssignAccess.Variable); } } else { unavailable = ProcessExpr(unavailable, swapAssignStmt.NewLocation); } if (unavailable.Contains(swapAssignStmt.OldLocation)) { throw handler.SwapAssignUnavailable(swapAssignStmt, swapAssignStmt.OldLocation); } break; case InsertStmt insertStmt: unavailable = ProcessExpr(unavailable, insertStmt.Variable); unavailable = ProcessExpr(unavailable, insertStmt.Index); unavailable = ProcessExpr(unavailable, insertStmt.Value); break; case RemoveStmt removeStmt: unavailable = ProcessExpr(unavailable, removeStmt.Variable); unavailable = ProcessExpr(unavailable, removeStmt.Value); break; case WhileStmt whileStmt: unavailable = ProcessExpr(unavailable, whileStmt.Condition); // process running the body twice. on the first go, the loop can potentially // relinquish additional variables on the second go, either the body will use // one of these variables and throw or reach a fixed point since all paths are // considered simultaneously. Then, we continue our overapproximation by taking // the union of no runs and one or more runs. var bodyUnavailable = ProcessStatement(new HashSet <Variable>(unavailable), whileStmt.Body); bodyUnavailable = ProcessExpr(bodyUnavailable, whileStmt.Condition); // TODO: more efficient way of doing this? bodyUnavailable = ProcessStatement(bodyUnavailable, whileStmt.Body); bodyUnavailable = ProcessExpr(bodyUnavailable, whileStmt.Condition); unavailable.UnionWith(bodyUnavailable); break; case IfStmt ifStmt: unavailable = ProcessExpr(unavailable, ifStmt.Condition); var thenUnavailable = ProcessStatement(new HashSet <Variable>(unavailable), ifStmt.ThenBranch); var elseUnavailable = ProcessStatement(new HashSet <Variable>(unavailable), ifStmt.ElseBranch); thenUnavailable.UnionWith(elseUnavailable); unavailable = thenUnavailable; break; case CtorStmt ctorStmt: unavailable = ProcessArgList(ctorStmt.Arguments, unavailable, ArgOptions.SwapNotAllowed); break; case FunCallStmt funCallStmt: unavailable = ProcessArgList(funCallStmt.ArgsList, unavailable); funCallStmts.Add(funCallStmt); break; case RaiseStmt raiseStmt: unavailable = ProcessExpr(unavailable, raiseStmt.PEvent); unavailable = ProcessArgList(raiseStmt.Payload, unavailable, ArgOptions.SwapNotAllowed); break; case SendStmt sendStmt: unavailable = ProcessExpr(unavailable, sendStmt.MachineExpr); unavailable = ProcessExpr(unavailable, sendStmt.Evt); unavailable = ProcessArgList(sendStmt.Arguments, unavailable, ArgOptions.SwapNotAllowed); break; case AnnounceStmt announceStmt: unavailable = ProcessExpr(unavailable, announceStmt.PEvent); if (announceStmt.Payload != null) { unavailable = ProcessExpr(unavailable, announceStmt.Payload); } break; case GotoStmt gotoStmt: if (gotoStmt.Payload != null) { unavailable = ProcessExpr(unavailable, gotoStmt.Payload); } break; case ReceiveStmt receiveStmt: var postUnavailable = new HashSet <Variable>(); var caseVariables = new HashSet <Variable>(); foreach (var recvCase in receiveStmt.Cases) { var caseUnavailable = ProcessStatement(new HashSet <Variable>(unavailable), recvCase.Value.Body); postUnavailable.UnionWith(caseUnavailable); caseVariables.UnionWith(recvCase.Value.Signature.Parameters); } unavailable = postUnavailable; unavailable.ExceptWith(caseVariables); break; case PopStmt _: case NoStmt _: // nothing to check break; default: throw handler.InternalError(statement.SourceLocation, new ArgumentOutOfRangeException(nameof(statement))); } return(unavailable); }
private static IEnumerable <Interface> InferCreates(IPAST tree, ITranslationErrorHandler handler) { switch (tree) { case Function function: if (function.IsForeign) { return(function.CreatesInterfaces); } return(InferCreates(function.Body, handler)); case AddStmt addStmt: return(InferCreatesForExpr(addStmt.Variable, handler) .Union(InferCreatesForExpr(addStmt.Value, handler))); case AnnounceStmt announce: return(InferCreatesForExpr(announce.PEvent, handler) .Union(InferCreatesForExpr(announce.Payload, handler))); case AssertStmt assertStmt: return(InferCreatesForExpr(assertStmt.Assertion, handler)); case AssignStmt assignStmt: return(InferCreatesForExpr(assignStmt.Location, handler) .Union(InferCreatesForExpr(assignStmt.Value, handler))); case CompoundStmt compoundStmt: return(compoundStmt.Statements.SelectMany(tree1 => InferCreates(tree1, handler))); case CtorStmt ctorStmt: Interface[] res = new[] { ctorStmt.Interface }; return(res.Union(ctorStmt.Arguments.SelectMany(expr => InferCreatesForExpr(expr, handler)))); case FunCallStmt funCallStmt: return(InferCreates(funCallStmt.Function, handler) .Union(funCallStmt.ArgsList.SelectMany(expr => InferCreatesForExpr(expr, handler)))); case GotoStmt gotoStmt: return(InferCreatesForExpr(gotoStmt.Payload, handler)); case IfStmt ifStmt: return(InferCreatesForExpr(ifStmt.Condition, handler) .Union(InferCreates(ifStmt.ThenBranch, handler)) .Union(InferCreates(ifStmt.ElseBranch, handler))); case InsertStmt insertStmt: return(InferCreatesForExpr(insertStmt.Variable, handler) .Union(InferCreatesForExpr(insertStmt.Index, handler)) .Union(InferCreatesForExpr(insertStmt.Value, handler))); case MoveAssignStmt moveAssignStmt: return(InferCreatesForExpr(moveAssignStmt.ToLocation, handler)); case NoStmt _: return(Enumerable.Empty <Interface>()); case PopStmt _: return(Enumerable.Empty <Interface>()); case PrintStmt printStmt: return(printStmt.Args.SelectMany(expr => InferCreatesForExpr(expr, handler))); case RaiseStmt raiseStmt: return(InferCreatesForExpr(raiseStmt.PEvent, handler) .Union(raiseStmt.Payload.SelectMany(expr => InferCreatesForExpr(expr, handler)))); case ReceiveStmt receiveStmt: return(receiveStmt.Cases.SelectMany(x => InferCreates(x.Value, handler))); case RemoveStmt removeStmt: return(InferCreatesForExpr(removeStmt.Variable, handler) .Union(InferCreatesForExpr(removeStmt.Value, handler))); case ReturnStmt returnStmt: return(InferCreatesForExpr(returnStmt.ReturnValue, handler)); case StringAssignStmt stringAssignStmt: return(InferCreatesForExpr(stringAssignStmt.Location, handler) .Union(stringAssignStmt.Args.SelectMany(expr => InferCreatesForExpr(expr, handler)))); case BreakStmt breakStmt: return(Enumerable.Empty <Interface>()); case ContinueStmt continueStmt: return(Enumerable.Empty <Interface>()); case SendStmt sendStmt: return(InferCreatesForExpr(sendStmt.MachineExpr, handler) .Union(InferCreatesForExpr(sendStmt.Evt, handler)) .Union(sendStmt.Arguments.SelectMany(expr => InferCreatesForExpr(expr, handler)))); case SwapAssignStmt swapAssignStmt: return(InferCreatesForExpr(swapAssignStmt.NewLocation, handler)); case WhileStmt whileStmt: return(InferCreatesForExpr(whileStmt.Condition, handler) .Union(InferCreates(whileStmt.Body, handler))); default: throw handler.InternalError(tree.SourceLocation, new ArgumentOutOfRangeException(nameof(tree))); } }