Expression::Ptr TypeChecker::verifyType(const Expression::Ptr &operand, const SequenceType::Ptr &reqSeqType, const StaticContext::Ptr &context, const ReportContext::ErrorCode code, const Options options) { const ItemType::Ptr reqType(reqSeqType->itemType()); const Expression::Properties props(operand->properties()); /* If operand requires a focus, do the necessary type checking for that. */ if(props.testFlag(Expression::RequiresFocus) && options.testFlag(CheckFocus)) { const ItemType::Ptr contextType(context->contextItemType()); if(contextType) { if(props.testFlag(Expression::RequiresContextItem)) { Q_ASSERT_X(operand->expectedContextItemType(), Q_FUNC_INFO, "When the Expression sets the RequiresContextItem property, it must " "return a type in expectedContextItemType()"); const ItemType::Ptr expectedContextType(operand->expectedContextItemType()); /* Allow the empty sequence. We don't want to trigger XPTY0020 on ()/... . */ if(!expectedContextType->xdtTypeMatches(contextType) && contextType != CommonSequenceTypes::Empty) { context->error(wrongType(context->namePool(), operand->expectedContextItemType(), contextType), ReportContext::XPTY0020, operand.data()); return operand; } } } else { context->error(QtXmlPatterns::tr("The focus is undefined."), ReportContext::XPDY0002, operand.data()); return operand; } } SequenceType::Ptr operandSeqType(operand->staticType()); ItemType::Ptr operandType(operandSeqType->itemType()); /* This returns the operand if the types are identical or if operandType * is a subtype of reqType. */ if(reqType->xdtTypeMatches(operandType) || *operandType == *CommonSequenceTypes::Empty) return operand; /* Since we haven't exited yet, it means that the operandType is a super type * of reqType, and that there hence is a path down to it through the * type hierachy -- but that doesn't neccessarily mean that a up-cast(down the * hierarchy) would succeed. */ Expression::Ptr result(operand); if(reqType->isAtomicType()) { const Expression::ID opID = operand->id(); if((opID == Expression::IDArgumentReference || (opID == Expression::IDCardinalityVerifier && operand->operands().first()->is(Expression::IDArgumentReference))) && *BuiltinTypes::item == *operandType) return Expression::Ptr(new ArgumentConverter(result, reqType)); if(!operandType->isAtomicType()) { result = Expression::Ptr(new Atomizer(result)); /* The atomizer might know more about the type. */ operandType = result->staticType()->itemType(); } if(reqType->xdtTypeMatches(operandType)) { /* Atomization was sufficient. Either the expected type is xs:anyAtomicType * or the type the Atomizer knows it returns, matches the required type. */ return result; } const bool compatModeEnabled = context->compatModeEnabled(); if((options.testFlag(AutomaticallyConvert) && BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(operandType)) || (compatModeEnabled && BuiltinTypes::xsString->xdtTypeMatches(reqType))) { if(*reqType == *BuiltinTypes::numeric) { result = typeCheck(new UntypedAtomicConverter(result, BuiltinTypes::xsDouble, code), context, reqSeqType); } else result = typeCheck(new UntypedAtomicConverter(result, reqType, code), context, reqSeqType); /* The UntypedAtomicConverter might know more about the type, so reload. */ operandType = result->staticType()->itemType(); } else if(compatModeEnabled && *reqType == *BuiltinTypes::xsDouble) { const FunctionFactory::Ptr functions(context->functionSignatures()); Expression::List numberArgs; numberArgs.append(operand); result = functions->createFunctionCall(QXmlName(StandardNamespaces::fn, StandardLocalNames::number), numberArgs, context, operand.data())->typeCheck(context, reqSeqType); operandType = result->staticType()->itemType(); context->wrapExpressionWith(operand.data(), result); } if(reqType->xdtTypeMatches(operandType)) return result; /* Test if promotion will solve it; the xdtTypeMatches didn't * do that. */ if(options.testFlag(AutomaticallyConvert) && promotionPossible(operandType, reqType, context)) { if(options.testFlag(GeneratePromotion)) return Expression::Ptr(new UntypedAtomicConverter(result, reqType)); else return result; } if(operandType->xdtTypeMatches(reqType)) { /* For example, operandType is numeric, and reqType is xs:integer. */ return Expression::Ptr(new ItemVerifier(result, reqType, code)); } else { context->error(wrongType(context->namePool(), reqType, operandType), code, operand.data()); return result; } } else if(reqType->isNodeType()) { ReportContext::ErrorCode myCode; if(*reqType == *CommonSequenceTypes::EBV->itemType()) myCode = ReportContext::FORG0006; else myCode = code; /* empty-sequence() is considered valid because it's ok to do * for example nilled( () ). That is, to pass an empty sequence to a * function requiring for example node()?. */ if(*operandType == *CommonSequenceTypes::Empty) return result; else if(!operandType->xdtTypeMatches(reqType)) { context->error(wrongType(context->namePool(), reqType, operandType), myCode, operand.data()); return result; } /* Operand must be an item. Thus, the sequence can contain both * nodes and atomic values: we have to verify. */ return Expression::Ptr(new ItemVerifier(result, reqType, myCode)); } else { Q_ASSERT(*reqType == *CommonSequenceTypes::Empty); /* element() doesn't match empty-sequence(), but element()* does. */ if(!reqType->xdtTypeMatches(operandType) && !operandSeqType->cardinality().allowsEmpty()) { context->error(wrongType(context->namePool(), reqType, operandType), code, operand.data()); return result; } } /* This line should be reached if required type is * EBVType, and the operand is compatible. */ return result; }
void GeneralComparison::updateType(ItemType::Ptr &type, const Expression::Ptr &source) { type = source->staticType()->itemType(); }
AtomicComparator::Ptr GeneralComparison::fetchGeneralComparator(Expression::Ptr &op1, Expression::Ptr &op2, const ReportContext::Ptr &context) const { ItemType::Ptr t1(op1->staticType()->itemType()); ItemType::Ptr t2(op2->staticType()->itemType()); /* a. "If one of the atomic values is an instance of xs:untypedAtomic and * the other is an instance of a numeric type, then the xs:untypedAtomic * value is cast to the type xs:double." */ if(BuiltinTypes::numeric->xdtTypeMatches(t1) && BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t2)) { op2 = Expression::Ptr(new UntypedAtomicConverter(op2, BuiltinTypes::xsDouble)); /* The types might have changed, reload. */ updateType(t2, op2); } else if(BuiltinTypes::numeric->xdtTypeMatches(t2) && BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t1)) { op1 = Expression::Ptr(new UntypedAtomicConverter(op1, BuiltinTypes::xsDouble)); /* The types might have changed, reload. */ updateType(t1, op1); } /* "If XPath 1.0 compatibility mode is true, a general comparison is * evaluated by applying the following rules, in order: * 1. If either operand is a single atomic value that is an instance of * xs:boolean, then the other operand is converted to xs:boolean by taking * its effective boolean value." * * Notably, it's not conversion to boolean, it is EBV extraction. */ else if(m_isBackwardsCompat && BuiltinTypes::xsBoolean->xdtTypeMatches(t1)) { op2 = Expression::Ptr(new EBVExtractor(op2)); updateType(t2, op2); } else if(m_isBackwardsCompat && BuiltinTypes::xsBoolean->xdtTypeMatches(t2)) { op1 = Expression::Ptr(new EBVExtractor(op1)); updateType(t1, op1); } /* b. "If one of the atomic values is an instance of xs:untypedAtomic and * the other is an instance of xs:untypedAtomic or xs:string, then the * xs:untypedAtomic value (or values) is (are) cast to the type xs:string." * * c. "If one of the atomic values is an instance of xs:untypedAtomic and the * other is not an instance of xs:string, xs:untypedAtomic, or any numeric * type, then the xs:untypedAtomic value is cast to the dynamic type of the * other value." */ else if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t1) && !BuiltinTypes::xsString->xdtTypeMatches(t2) && !BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t2) && !BuiltinTypes::xsAnyURI->xdtTypeMatches(t2)) { op1 = Expression::Ptr(new UntypedAtomicConverter(op1, t2)); updateType(t1, op1); } else if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t2) && !BuiltinTypes::xsString->xdtTypeMatches(t1) && !BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t1) && !BuiltinTypes::xsAnyURI->xdtTypeMatches(t1)) { op2 = Expression::Ptr(new UntypedAtomicConverter(op2, t1)); updateType(t2, op2); } /* d. "After performing the conversions described above, the atomic * values are compared using one of the value comparison operators * eq, ne, lt, le, gt, or ge, depending on whether the general comparison * operator was =, !=, <, <=, >, or >=. The values have the required * magnitude relationship if and only if the result of this value comparison * is true." */ return fetchComparator(t1, t2, context); }