private void AddQueryOrProc(IRRule rule) { // Check if all parameters in the PROC/QRY declaration are typed. var procDefn = rule.Conditions[0]; if (procDefn is IRFuncCondition) { var def = procDefn as IRFuncCondition; FunctionType type; switch (rule.Type) { case RuleType.Proc: type = FunctionType.Proc; break; case RuleType.Query: type = FunctionType.UserQuery; break; default: throw new InvalidOperationException("Cannot register this type as a PROC or QUERY"); } if (!PropagateSignatureIfRequired(rule, def.Func.Name, type, def.Params, true)) { // TODO - possibly a warning? /*Context.Log.Error(procDefn.Location, DiagnosticCode.InvalidProcDefinition, "Signature must be completely typed in declaration of {0} {1}", rule.Type, def.Func.Name);*/ } } else { Context.Log.Error(procDefn.Location, DiagnosticCode.InvalidProcDefinition, "Declaration of a {0} must start with a {0} name and signature.", rule.Type); } }
private IRValue ASTValueToIR(IRRule rule, ASTRValue astValue) { if (astValue is ASTConstantValue) { return(ASTConstantToIR(astValue as ASTConstantValue)); } else if (astValue is ASTLocalVar) { var astVar = astValue as ASTLocalVar; // TODO - compiler error if type resolution fails var type = astVar.Type != null?Context.LookupType(astVar.Type) : null; var ruleVar = rule.FindOrAddVariable(astVar.Name, type); return(new IRVariable { Index = ruleVar.Index, Type = type, Location = astValue.Location }); } else { throw new InvalidOperationException("Cannot convert unknown AST value type to IR"); } }
private void PropagateRuleTypes(IRRule rule, IRBinaryCondition condition) { if (condition.LValue.Type == null && condition.LValue is IRVariable) { var lval = condition.LValue as IRVariable; var ruleVariable = rule.Variables[lval.Index]; if (ruleVariable.Type != null) { lval.Type = ruleVariable.Type; } } if (condition.RValue.Type == null && condition.RValue is IRVariable) { var rval = condition.RValue as IRVariable; var ruleVariable = rule.Variables[rval.Index]; if (ruleVariable.Type != null) { rval.Type = ruleVariable.Type; } } // TODO - handle implicit re-typing of rule variables? }
private IRRule ASTRuleToIR(IRGoal goal, ASTRule astRule) { var rule = new IRRule { Goal = goal, Type = astRule.Type, Conditions = new List <IRCondition>(astRule.Conditions.Count), Actions = new List <IRStatement>(astRule.Actions.Count), Variables = new List <IRRuleVariable>(), VariablesByName = new Dictionary <String, IRRuleVariable>(), Location = astRule.Location }; foreach (var condition in astRule.Conditions) { rule.Conditions.Add(ASTConditionToIR(rule, condition)); } foreach (var action in astRule.Actions) { rule.Actions.Add(ASTActionToIR(rule, action)); } return(rule); }
private ValueType DetermineSignature(IRRule rule, IRValue value) { if (value is IRConstant) { return DetermineSignature(value as IRConstant); } else if (value is IRVariable) { if (value.Type != null) { return value.Type; } var irVar = value as IRVariable; var ruleVar = rule.Variables[irVar.Index]; if (ruleVar.Type != null) { return ruleVar.Type; } else { return null; } } else { throw new ArgumentException("Invalid IR value type"); } }
private bool PropagateRuleTypes(IRRule rule, IRBinaryCondition condition) { bool updated = false; if (condition.LValue.Type == null && condition.LValue is IRVariable) { var lval = condition.LValue as IRVariable; var ruleVariable = rule.Variables[lval.Index]; if (ruleVariable.Type != null) { lval.Type = ruleVariable.Type; updated = true; } } if (condition.RValue.Type == null && condition.RValue is IRVariable) { var rval = condition.RValue as IRVariable; var ruleVariable = rule.Variables[rval.Index]; if (ruleVariable.Type != null) { rval.Type = ruleVariable.Type; updated = true; } } // TODO - handle implicit re-typing of rule variables? return(updated); }
private bool TryPropagateSignature(IRRule rule, FunctionNameAndArity name, FunctionType? type, List<IRValue> parameters, bool allowPartial) { // Build a signature with all parameters to make sure that all types can be resolved var sig = new List<ValueType>(parameters.Count); foreach (var param in parameters) { var paramSignature = DetermineSignature(rule, param); if (paramSignature != null) { sig.Add(paramSignature); } else { if (allowPartial) { sig.Add(null); } else { return false; } } } // Apply signature to symbol return ApplySignature(name, type, sig); }
private bool PropagateIRVariableType(IRRule rule, IRVariable variable, ValueType type) { bool updated = false; var ruleVar = rule.Variables[variable.Index]; if (ruleVar.Type == null) { ruleVar.Type = type; updated = true; } if (variable.Type == null) { // If a more specific type alias is available from the rule variable, apply the // rule type instead of the function argument type if (ruleVar.Type.IsAliasOf(type)) { variable.Type = ruleVar.Type; } else { variable.Type = type; } updated = true; } return(updated); }
private void VerifyIRValueCall(IRRule rule, IRValue value, FunctionSignature signature, Int32 parameterIndex, Int32 conditionIndex, bool not) { if (value is IRVariable) { VerifyIRVariableCall(rule, value as IRVariable, signature, parameterIndex, conditionIndex, not); } }
private void VerifyIRValue(IRRule rule, IRValue value) { if (value is IRConstant) { VerifyIRConstant(value as IRConstant); } else { VerifyIRVariable(rule, value as IRVariable); } }
private void VerifyIRValue(IRRule rule, IRValue value, FunctionSignature func) { if (value is IRConstant) { VerifyIRConstant(value as IRConstant); } else { VerifyIRVariable(rule, value as IRVariable, func); } }
private void VerifyIRRule(IRRule rule) { if (rule.Type == RuleType.Proc || rule.Type == RuleType.Query) { var initialName = (rule.Conditions[0] as IRFuncCondition).Func.Name; if (rule.Type == RuleType.Proc && initialName.Name.Substring(0, 4).ToUpper() != "PROC") { Context.Log.Warn(rule.Conditions[0].Location, DiagnosticCode.RuleNamingStyle, "Name of PROC \"{0}\" should start with the prefix \"PROC\"", initialName); } if (rule.Type == RuleType.Query && initialName.Name.Substring(0, 3).ToUpper() != "QRY") { Context.Log.Warn(rule.Conditions[0].Location, DiagnosticCode.RuleNamingStyle, "Name of Query \"{0}\" should start with the prefix \"QRY\"", initialName); } } for (var i = 0; i < rule.Conditions.Count; i++) { var condition = rule.Conditions[i]; if (condition is IRBinaryCondition) { VerifyIRBinaryCondition(rule, condition as IRBinaryCondition, i); } else { VerifyIRFuncCondition(rule, condition as IRFuncCondition, i); } } foreach (var action in rule.Actions) { VerifyIRStatement(rule, action); } foreach (var variable in rule.Variables) { if (variable.Type == null) { // TODO - return location of first variable reference instead of rule Context.Log.Error(rule.Location, DiagnosticCode.UnresolvedVariableType, "Variable \"{0}\" of rule could not be typed", variable.Name); } } }
private void VerifyIRVariable(IRRule rule, IRVariable variable, FunctionSignature func) { var ruleVar = rule.Variables[variable.Index]; if (variable.Type == null) { Context.Log.Error(variable.Location, DiagnosticCode.UnresolvedType, "Type of variable {0} could not be determined", ruleVar.Name); return; } if (ruleVar.Type == null) { Context.Log.Error(variable.Location, DiagnosticCode.UnresolvedType, "Type of rule variable {0} could not be determined", ruleVar.Name); return; } if ((func == null || TypeCoercionWhitelist == null || !TypeCoercionWhitelist.Contains(func.GetNameAndArity().ToString())) && !AllowTypeCoercion) { if (!AreIntrinsicTypesCompatible(ruleVar.Type.IntrinsicTypeId, variable.Type.IntrinsicTypeId)) { Context.Log.Error(variable.Location, DiagnosticCode.CastToUnrelatedType, "Cannot cast {1} variable {0} to unrelated type {2}", ruleVar.Name, ruleVar.Type.Name, variable.Type.Name); return; } if (IsRiskyComparison(ruleVar.Type.IntrinsicTypeId, variable.Type.IntrinsicTypeId)) { Context.Log.Error(variable.Location, DiagnosticCode.RiskyComparison, "Coercion of {1} variable {0} to {2} may trigger incorrect behavior", ruleVar.Name, ruleVar.Type.Name, variable.Type.Name); return; } if (IsGuidAliasToAliasCast(ruleVar.Type, variable.Type)) { Context.Log.Error(variable.Location, DiagnosticCode.CastToUnrelatedGuidAlias, "{1} variable {0} converted to unrelated type {2}", ruleVar.Name, ruleVar.Type.Name, variable.Type.Name); } } }
private void PropagateRuleTypesFromParamList(IRRule rule, List<IRValue> parameters, FunctionSignature signature) { Int32 index = 0; foreach (var param in parameters) { if (param is IRVariable) { var irVar = param as IRVariable; PropagateIRVariableType(rule, param as IRVariable, signature.Params[index].Type); } index++; } }
private void VerifyIRVariable(IRRule rule, IRVariable variable) { var ruleVar = rule.Variables[variable.Index]; if (variable.Type == null) { Context.Log.Error(variable.Location, DiagnosticCode.UnresolvedType, "Type of variable {0} could not be determined", ruleVar.Name); return; } if (ruleVar.Type == null) { Context.Log.Error(variable.Location, DiagnosticCode.UnresolvedType, "Type of rule variable {0} could not be determined", ruleVar.Name); return; } if (!AreIntrinsicTypesCompatible(ruleVar.Type.IntrinsicTypeId, variable.Type.IntrinsicTypeId)) { Context.Log.Error(variable.Location, DiagnosticCode.CastToUnrelatedType, "Cannot cast {1} variable {0} to unrelated type {2}", ruleVar.Name, ruleVar.Type.Name, variable.Type.Name); return; } if (IsRiskyComparison(ruleVar.Type.IntrinsicTypeId, variable.Type.IntrinsicTypeId)) { Context.Log.Error(variable.Location, DiagnosticCode.RiskyComparison, "Coercion of {1} variable {0} to {2} may trigger incorrect behavior", ruleVar.Name, ruleVar.Type.Name, variable.Type.Name); return; } if (IsGuidAliasToAliasCast(ruleVar.Type, variable.Type)) { Context.Log.Error(variable.Location, DiagnosticCode.CastToUnrelatedGuidAlias, "{1} variable {0} converted to unrelated type {2}", ruleVar.Name, ruleVar.Type.Name, variable.Type.Name); } }
private void VerifyIRBinaryConditionValue(IRRule rule, IRValue value, Int32 conditionIndex) { VerifyIRValue(rule, value); if (value is IRVariable) { var variable = value as IRVariable; var ruleVar = rule.Variables[variable.Index]; if (ruleVar.FirstBindingIndex == -1 || ruleVar.FirstBindingIndex >= conditionIndex) { Context.Log.Error(variable.Location, DiagnosticCode.ParamNotBound, "Variable {0} is not bound (when used in a binary expression)", ruleVar.Name); } } }
private bool PropagateSignatureIfRequired(IRRule rule, FunctionNameAndArity name, FunctionType? type, List<IRValue> parameters, bool allowPartial) { var signature = Context.LookupSignature(name); bool signatureOk = (signature != null && signature.FullyTyped); if (!signatureOk && TryPropagateSignature(rule, name, type, parameters, allowPartial)) { signature = Context.LookupSignature(name); signatureOk = signature.FullyTyped; } if (signatureOk) { PropagateRuleTypesFromParamList(rule, parameters, signature); } return signatureOk; }
private bool PropagateRuleTypes(IRRule rule) { bool updated = false; Int32 lastTupleSize = 0; foreach (var condition in rule.Conditions) { if (condition is IRFuncCondition) { var func = condition as IRFuncCondition; PropagateSignatureIfRequired(rule, func.Func.Name, null, func.Params, false, ref updated); if (func.TupleSize == -1) { func.TupleSize = ComputeTupleSize(rule, func, lastTupleSize); updated = true; } } else { var bin = condition as IRBinaryCondition; if (PropagateRuleTypes(rule, bin)) { updated = true; } if (bin.TupleSize == -1) { bin.TupleSize = ComputeTupleSize(rule, bin, lastTupleSize); updated = true; } } lastTupleSize = condition.TupleSize; } foreach (var action in rule.Actions) { if (action.Func != null) { PropagateSignatureIfRequired(rule, action.Func.Name, null, action.Params, false, ref updated); } } return(updated); }
private UInt32 ComputeTupleSize(IRRule rule, IRFuncCondition condition, UInt32 lastTupleSize) { UInt32 tupleSize = lastTupleSize; foreach (var param in condition.Params) { if (param is IRVariable) { var variable = param as IRVariable; if (variable.Index >= tupleSize) { tupleSize = (UInt32)variable.Index + 1; } } } return tupleSize; }
private bool PropagateRuleTypesFromParamList(IRRule rule, List <IRValue> parameters, FunctionSignature signature) { bool updated = false; Int32 index = 0; foreach (var param in parameters) { if (param is IRVariable) { var irVar = param as IRVariable; if (PropagateIRVariableType(rule, param as IRVariable, signature.Params[index].Type)) { updated = true; } } index++; } return(updated); }
private IRValue ASTValueToIR(IRRule rule, ASTRValue astValue) { if (astValue is ASTConstantValue) { return(ASTConstantToIR(astValue as ASTConstantValue)); } else if (astValue is ASTLocalVar) { var astVar = astValue as ASTLocalVar; // TODO - compiler error if type resolution fails ValueType type; if (astVar.Type != null) { type = Context.LookupType(astVar.Type); if (type == null) { Context.Log.Error(astVar.Location, DiagnosticCode.UnresolvedType, String.Format("Type \"{0}\" does not exist", astVar.Type)); } } else { type = null; } var ruleVar = rule.FindOrAddVariable(astVar.Name, type); return(new IRVariable { Index = ruleVar.Index, Type = type, Location = astValue.Location }); } else { throw new InvalidOperationException("Cannot convert unknown AST value type to IR"); } }
private IRCondition ASTConditionToIR(IRRule rule, ASTCondition astCondition) { if (astCondition is ASTFuncCondition) { var astFunc = astCondition as ASTFuncCondition; var func = new IRFuncCondition { Func = new IRSymbolRef(new FunctionNameAndArity(astFunc.Name, astFunc.Params.Count)), Not = astFunc.Not, Params = new List <IRValue>(astFunc.Params.Count), TupleSize = -1, Location = astCondition.Location }; foreach (var param in astFunc.Params) { func.Params.Add(ASTValueToIR(rule, param)); } return(func); } else if (astCondition is ASTBinaryCondition) { var astBin = astCondition as ASTBinaryCondition; return(new IRBinaryCondition { LValue = ASTValueToIR(rule, astBin.LValue), Op = astBin.Op, RValue = ASTValueToIR(rule, astBin.RValue), TupleSize = -1, Location = astCondition.Location }); } else { throw new InvalidOperationException("Cannot convert unknown AST condition type to IR"); } }
private IRStatement ASTActionToIR(IRRule rule, ASTAction astAction) { if (astAction is ASTGoalCompletedAction) { var astGoal = astAction as ASTGoalCompletedAction; return(new IRStatement { Func = null, Goal = rule.Goal, Not = false, Params = new List <IRValue>(), Location = astAction.Location }); } else if (astAction is ASTStatement) { var astStmt = astAction as ASTStatement; var stmt = new IRStatement { Func = new IRSymbolRef(new FunctionNameAndArity(astStmt.Name, astStmt.Params.Count)), Goal = null, Not = astStmt.Not, Params = new List <IRValue>(astStmt.Params.Count), Location = astAction.Location }; foreach (var param in astStmt.Params) { stmt.Params.Add(ASTValueToIR(rule, param)); } return(stmt); } else { throw new InvalidOperationException("Cannot convert unknown AST condition type to IR"); } }
private UInt32 ComputeTupleSize(IRRule rule, IRBinaryCondition condition, UInt32 lastTupleSize) { UInt32 tupleSize = lastTupleSize; if (condition.LValue is IRVariable) { var variable = condition.LValue as IRVariable; if (variable.Index >= tupleSize) { tupleSize = (UInt32)variable.Index + 1; } } if (condition.RValue is IRVariable) { var variable = condition.RValue as IRVariable; if (variable.Index >= tupleSize) { tupleSize = (UInt32)variable.Index + 1; } } return tupleSize; }
private void VerifyIRStatement(IRRule rule, IRStatement statement) { if (statement.Func == null) return; var func = Context.LookupSignature(statement.Func.Name); if (func == null) { Context.Log.Error(statement.Location, DiagnosticCode.UnresolvedSymbol, "Symbol \"{0}\" could not be resolved", statement.Func.Name); return; } if (!func.FullyTyped) { Context.Log.Error(statement.Location, DiagnosticCode.UnresolvedSignature, "Signature of \"{0}\" could not be determined", statement.Func.Name); return; } if (func.Type != FunctionType.Database && func.Type != FunctionType.Call && func.Type != FunctionType.SysCall && func.Type != FunctionType.Proc) { Context.Log.Error(statement.Location, DiagnosticCode.InvalidSymbolInStatement, "KB rule actions can only reference databases, calls and PROCs; \"{0}\" is a {1}", statement.Func.Name, func.Type); return; } if (statement.Not && func.Type != FunctionType.Database) { Context.Log.Error(statement.Location, DiagnosticCode.CanOnlyDeleteFromDatabase, "KB rule NOT actions can only reference databases; \"{0}\" is a {1}", statement.Func.Name, func.Type); return; } if (statement.Not) { func.Deleted = true; } else { func.Inserted = true; } int index = 0; foreach (var param in func.Params) { var ele = statement.Params[index]; ValueType type = ele.Type; if (type == null) { Context.Log.Error(ele.Location, DiagnosticCode.InternalError, "No type information available for statement argument"); continue; } VerifyIRValue(rule, ele); VerifyIRValueCall(rule, ele, func, index, -1, statement.Not); VerifyParamCompatibility(func, index, param, ele); index++; } }
private void VerifyIRBinaryCondition(IRRule rule, IRBinaryCondition condition, Int32 conditionIndex) { ValueType lhs = condition.LValue.Type, rhs = condition.RValue.Type; // Don't raise compiler errors if the untyped value is a variable, // as we already have a separate rule-level error for untyped variables. if ((lhs == null && condition.LValue is IRVariable) || (rhs == null && condition.RValue is IRVariable)) { return; } if (condition.LValue is IRVariable && condition.RValue is IRVariable && (condition.LValue as IRVariable).Index == (condition.RValue as IRVariable).Index // This bug was fixed in DOS2 DE && Game == TargetGame.DOS2 // There is a known bug in the main campaign that we have to ignore && rule.Goal.Name != "EndGame_PrisonersDilemma") { Context.Log.Error(condition.Location, DiagnosticCode.BinaryOperationSameRhsLhs, "Same variable used on both sides of a binary expression; this will result in an invalid compare in runtime"); return; } VerifyIRBinaryConditionValue(rule, condition.LValue, conditionIndex); VerifyIRBinaryConditionValue(rule, condition.RValue, conditionIndex); if (!AreIntrinsicTypesCompatible(lhs.IntrinsicTypeId, rhs.IntrinsicTypeId)) { Context.Log.Error(condition.Location, DiagnosticCode.LocalTypeMismatch, "Type of left expression ({0}) differs from type of right expression ({1})", TypeToName(lhs.IntrinsicTypeId), TypeToName(rhs.IntrinsicTypeId)); return; } if (IsRiskyComparison(lhs.IntrinsicTypeId, rhs.IntrinsicTypeId)) { Context.Log.Error(condition.Location, DiagnosticCode.RiskyComparison, "Comparison between {0} and {1} may trigger incorrect behavior", TypeToName(lhs.IntrinsicTypeId), TypeToName(rhs.IntrinsicTypeId)); return; } if (IsGuidAliasToAliasCast(lhs, rhs)) { Context.Log.Error(condition.Location, DiagnosticCode.GuidAliasMismatch, "GUID alias type of left expression ({0}) differs from type of right expression ({1})", TypeToName(lhs.TypeId), TypeToName(rhs.TypeId)); return; } // Using greater than/less than operators for strings and GUIDs is probably a mistake. if ((lhs.IntrinsicTypeId == Value.Type.String || lhs.IntrinsicTypeId == Value.Type.GuidString) && (condition.Op == RelOpType.Greater || condition.Op == RelOpType.GreaterOrEqual || condition.Op == RelOpType.Less || condition.Op == RelOpType.LessOrEqual)) { Context.Log.Warn(condition.Location, DiagnosticCode.StringLtGtComparison, "String comparison using operator {0} - probably a mistake?", condition.Op); return; } }
private void VerifyIRFuncCondition(IRRule rule, IRFuncCondition condition, int conditionIndex) { // TODO - Merge FuncCondition and IRStatement base? // Base --> IRParameterizedCall --> FuncCond: has (NOT) field var func = Context.LookupSignature(condition.Func.Name); if (func == null) { Context.Log.Error(condition.Location, DiagnosticCode.UnresolvedSymbol, "Symbol \"{0}\" could not be resolved", condition.Func.Name); return; } if (!func.FullyTyped) { Context.Log.Error(condition.Location, DiagnosticCode.UnresolvedSignature, "Signature of \"{0}\" could not be determined", condition.Func.Name); return; } func.Read = true; if (conditionIndex == 0) { switch (rule.Type) { case RuleType.Proc: if (func.Type != FunctionType.Proc) { Context.Log.Error(condition.Location, DiagnosticCode.InvalidSymbolInInitialCondition, "Initial proc condition can only be a PROC name; \"{0}\" is a {1}", condition.Func.Name, func.Type); return; } break; case RuleType.Query: if (func.Type != FunctionType.UserQuery) { Context.Log.Error(condition.Location, DiagnosticCode.InvalidSymbolInInitialCondition, "Initial query condition can only be a user-defined QRY name; \"{0}\" is a {1}", condition.Func.Name, func.Type); return; } break; case RuleType.Rule: if (func.Type != FunctionType.Event && func.Type != FunctionType.Database) { Context.Log.Error(condition.Location, DiagnosticCode.InvalidSymbolInInitialCondition, "Initial rule condition can only be an event or a DB; \"{0}\" is a {1}", condition.Func.Name, func.Type); return; } break; default: throw new Exception("Unknown rule type"); } } else { if (func.Type != FunctionType.SysQuery && func.Type != FunctionType.Query && func.Type != FunctionType.Database && func.Type != FunctionType.UserQuery) { Context.Log.Error(condition.Location, DiagnosticCode.InvalidFunctionTypeInCondition, "Subsequent rule conditions can only be queries or DBs; \"{0}\" is a {1}", condition.Func.Name, func.Type); return; } } int index = 0; foreach (var param in func.Params) { var condParam = condition.Params[index]; ValueType type = condParam.Type; if (type == null) { Context.Log.Error(condParam.Location, DiagnosticCode.InternalError, "No type information available for func condition arg"); continue; } VerifyIRValue(rule, condParam); VerifyIRValueCall(rule, condParam, func, index, conditionIndex, condition.Not); VerifyParamCompatibility(func, index, param, condParam); index++; } }
private void VerifyIRVariableCall(IRRule rule, IRVariable variable, FunctionSignature signature, Int32 parameterIndex, Int32 conditionIndex, bool not) { var ruleVar = rule.Variables[variable.Index]; var param = signature.Params[parameterIndex]; if (param.Direction == ParamDirection.Out && !not) { Debug.Assert(conditionIndex != -1); if (ruleVar.FirstBindingIndex == -1) { ruleVar.FirstBindingIndex = conditionIndex; } } else if ( // We're in the THEN section of a rule, so we cannot bind here conditionIndex == -1 // NOT conditions never bind, but they allow unbound unused variables || (!ruleVar.IsUnused() && not) || ( // Databases and events always bind signature.Type != FunctionType.Database && signature.Type != FunctionType.Event // PROC/QRYs bind if they're the first condition in a rule && !(rule.Type == RuleType.Proc && conditionIndex == 0 && signature.Type == FunctionType.Proc) && !(rule.Type == RuleType.Query && conditionIndex == 0 && signature.Type == FunctionType.UserQuery) && param.Direction != ParamDirection.Out ) ) { if ( // The variable was never bound ruleVar.FirstBindingIndex == -1 // The variable was bound after this node (so it is still unbound here) || (conditionIndex != -1 && ruleVar.FirstBindingIndex >= conditionIndex) ) { object paramName = (param.Name != null) ? (object)param.Name : (parameterIndex + 1); if (!ruleVar.IsUnused()) { Context.Log.Error(variable.Location, DiagnosticCode.ParamNotBound, "Variable {0} is not bound here (when used as parameter {1} of {2} \"{3}\")", ruleVar.Name, paramName, signature.Type, signature.GetNameAndArity()); } else { Context.Log.Error(variable.Location, DiagnosticCode.ParamNotBound, "Parameter {0} of {1} \"{2}\" requires a variable or constant, not a placeholder", paramName, signature.Type, signature.GetNameAndArity()); } } } else { if (conditionIndex != -1 && ruleVar.FirstBindingIndex == -1 && !not) { ruleVar.FirstBindingIndex = conditionIndex; } } }