forked from OSchip/llvm-project
[flang] CheckConformance: tristate-ify result
To ensure that errors are emitted by CheckConformance and its callers in all situations, it's necessary for the returned result of that function to distinguish between three possible outcomes: the arrays are known to conform at compilation time, the arrays are known to not conform (and a message has been produced), and an indeterminate result in which is not possible to determine conformance. So convert CheckConformance's result into an optional<bool>, and convert its confusing Boolean flag arguments into a bit-set of named flags too. Differential Revision: https://reviews.llvm.org/D103654
This commit is contained in:
parent
ffde966cd9
commit
dfecbcae0d
|
@ -144,8 +144,8 @@ public:
|
|||
int Rank() const { return GetRank(shape_); }
|
||||
bool IsCompatibleWith(parser::ContextualMessages &, const TypeAndShape &that,
|
||||
const char *thisIs = "pointer", const char *thatIs = "target",
|
||||
bool isElemental = false, bool thisIsDeferredShape = false,
|
||||
bool thatIsDeferredShape = false) const;
|
||||
bool isElemental = false,
|
||||
enum CheckConformanceFlags::Flags = CheckConformanceFlags::None) const;
|
||||
std::optional<Expr<SubscriptInteger>> MeasureElementSizeInBytes(
|
||||
FoldingContext &, bool align) const;
|
||||
std::optional<Expr<SubscriptInteger>> MeasureSizeInBytes(
|
||||
|
|
|
@ -239,12 +239,30 @@ std::optional<ConstantSubscripts> GetConstantExtents(
|
|||
}
|
||||
|
||||
// Compilation-time shape conformance checking, when corresponding extents
|
||||
// are known.
|
||||
bool CheckConformance(parser::ContextualMessages &, const Shape &left,
|
||||
const Shape &right, const char *leftIs = "left operand",
|
||||
const char *rightIs = "right operand", bool leftScalarExpandable = true,
|
||||
bool rightScalarExpandable = true, bool leftIsDeferredShape = false,
|
||||
bool rightIsDeferredShape = false);
|
||||
// are or should be known. The result is an optional Boolean:
|
||||
// - nullopt: no error found or reported, but conformance cannot
|
||||
// be guaranteed during compilation; this result is possible only
|
||||
// when one or both arrays are allowed to have deferred shape
|
||||
// - true: no error found or reported, arrays conform
|
||||
// - false: errors found and reported
|
||||
// Use "CheckConformance(...).value_or()" to specify a default result
|
||||
// when you don't care whether messages have been emitted.
|
||||
struct CheckConformanceFlags {
|
||||
enum Flags {
|
||||
None = 0,
|
||||
LeftScalarExpandable = 1,
|
||||
RightScalarExpandable = 2,
|
||||
LeftIsDeferredShape = 4,
|
||||
RightIsDeferredShape = 8,
|
||||
EitherScalarExpandable = LeftScalarExpandable | RightScalarExpandable,
|
||||
BothDeferredShape = LeftIsDeferredShape | RightIsDeferredShape,
|
||||
RightIsExpandableDeferred = RightScalarExpandable | RightIsDeferredShape,
|
||||
};
|
||||
};
|
||||
std::optional<bool> CheckConformance(parser::ContextualMessages &,
|
||||
const Shape &left, const Shape &right,
|
||||
CheckConformanceFlags::Flags flags = CheckConformanceFlags::None,
|
||||
const char *leftIs = "left operand", const char *rightIs = "right operand");
|
||||
|
||||
// Increments one-based subscripts in element order (first varies fastest)
|
||||
// and returns true when they remain in range; resets them all to one and
|
||||
|
|
|
@ -149,8 +149,7 @@ std::optional<TypeAndShape> TypeAndShape::Characterize(
|
|||
|
||||
bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages,
|
||||
const TypeAndShape &that, const char *thisIs, const char *thatIs,
|
||||
bool isElemental, bool thisIsDeferredShape,
|
||||
bool thatIsDeferredShape) const {
|
||||
bool isElemental, enum CheckConformanceFlags::Flags flags) const {
|
||||
if (!type_.IsTkCompatibleWith(that.type_)) {
|
||||
messages.Say(
|
||||
"%1$s type '%2$s' is not compatible with %3$s type '%4$s'"_err_en_US,
|
||||
|
@ -158,9 +157,8 @@ bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages,
|
|||
return false;
|
||||
}
|
||||
return isElemental ||
|
||||
CheckConformance(messages, shape_, that.shape_, thisIs, thatIs, false,
|
||||
false /* no scalar expansion */, thisIsDeferredShape,
|
||||
thatIsDeferredShape);
|
||||
CheckConformance(messages, shape_, that.shape_, flags, thisIs, thatIs)
|
||||
.value_or(true /*fail only when nonconformance is known now*/);
|
||||
}
|
||||
|
||||
std::optional<Expr<SubscriptInteger>> TypeAndShape::MeasureElementSizeInBytes(
|
||||
|
|
|
@ -390,8 +390,9 @@ std::optional<Expr<SomeType>> NonPointerInitializationExpr(const Symbol &symbol,
|
|||
.Expand(std::move(folded));
|
||||
} else if (auto resultShape{GetShape(context, folded)}) {
|
||||
if (CheckConformance(context.messages(), symTS->shape(),
|
||||
*resultShape, "initialized object",
|
||||
"initialization expression", false, false)) {
|
||||
*resultShape, CheckConformanceFlags::None,
|
||||
"initialized object", "initialization expression")
|
||||
.value_or(false /*fail if not known now to conform*/)) {
|
||||
// make a constant array with adjusted lower bounds
|
||||
return ArrayConstantBoundChanger{
|
||||
std::move(*AsConstantExtents(
|
||||
|
|
|
@ -1030,8 +1030,9 @@ auto ApplyElementwise(FoldingContext &context,
|
|||
if (rightExpr.Rank() > 0) {
|
||||
if (std::optional<Shape> rightShape{GetShape(context, rightExpr)}) {
|
||||
if (auto right{AsFlatArrayConstructor(rightExpr)}) {
|
||||
if (CheckConformance(
|
||||
context.messages(), *leftShape, *rightShape)) {
|
||||
if (CheckConformance(context.messages(), *leftShape, *rightShape,
|
||||
CheckConformanceFlags::EitherScalarExpandable)
|
||||
.value_or(false /*fail if not known now to conform*/)) {
|
||||
return MapOperation(context, std::move(f), *leftShape,
|
||||
std::move(*left), std::move(*right));
|
||||
} else {
|
||||
|
|
|
@ -759,18 +759,16 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Check conformance of the passed shapes. Only return true if we can verify
|
||||
// that they conform
|
||||
bool CheckConformance(parser::ContextualMessages &messages, const Shape &left,
|
||||
const Shape &right, const char *leftIs, const char *rightIs,
|
||||
bool leftScalarExpandable, bool rightScalarExpandable,
|
||||
bool leftIsDeferredShape, bool rightIsDeferredShape) {
|
||||
// Check conformance of the passed shapes.
|
||||
std::optional<bool> CheckConformance(parser::ContextualMessages &messages,
|
||||
const Shape &left, const Shape &right, CheckConformanceFlags::Flags flags,
|
||||
const char *leftIs, const char *rightIs) {
|
||||
int n{GetRank(left)};
|
||||
if (n == 0 && leftScalarExpandable) {
|
||||
if (n == 0 && (flags & CheckConformanceFlags::LeftScalarExpandable)) {
|
||||
return true;
|
||||
}
|
||||
int rn{GetRank(right)};
|
||||
if (rn == 0 && rightScalarExpandable) {
|
||||
if (rn == 0 && (flags & CheckConformanceFlags::RightScalarExpandable)) {
|
||||
return true;
|
||||
}
|
||||
if (n != rn) {
|
||||
|
@ -787,11 +785,11 @@ bool CheckConformance(parser::ContextualMessages &messages, const Shape &left,
|
|||
j + 1, leftIs, *leftDim, rightIs, *rightDim);
|
||||
return false;
|
||||
}
|
||||
} else if (!rightIsDeferredShape) {
|
||||
return false;
|
||||
} else if (!(flags & CheckConformanceFlags::RightIsDeferredShape)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
} else if (!leftIsDeferredShape) {
|
||||
return false;
|
||||
} else if (!(flags & CheckConformanceFlags::LeftIsDeferredShape)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -160,7 +160,8 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
|
|||
// Let CheckConformance accept scalars; storage association
|
||||
// cases are checked here below.
|
||||
CheckConformance(messages, dummy.type.shape(), actualType.shape(),
|
||||
"dummy argument", "actual argument", true, true);
|
||||
evaluate::CheckConformanceFlags::EitherScalarExpandable,
|
||||
"dummy argument", "actual argument");
|
||||
}
|
||||
} else {
|
||||
const auto &len{actualType.LEN()};
|
||||
|
|
|
@ -1655,17 +1655,21 @@ MaybeExpr ExpressionAnalyzer::Analyze(
|
|||
"Rank-%d array value is not compatible with scalar component '%s'"_err_en_US,
|
||||
GetRank(*valueShape), symbol->name()),
|
||||
*symbol);
|
||||
} else if (CheckConformance(messages, *componentShape,
|
||||
*valueShape, "component", "value", false,
|
||||
true /* can expand scalar value */)) {
|
||||
if (GetRank(*componentShape) > 0 && GetRank(*valueShape) == 0 &&
|
||||
} else {
|
||||
auto checked{
|
||||
CheckConformance(messages, *componentShape, *valueShape,
|
||||
CheckConformanceFlags::RightIsExpandableDeferred,
|
||||
"component", "value")};
|
||||
if (checked && *checked && GetRank(*componentShape) > 0 &&
|
||||
GetRank(*valueShape) == 0 &&
|
||||
!IsExpandableScalar(*converted)) {
|
||||
AttachDeclaration(
|
||||
Say(expr.source,
|
||||
"Scalar value cannot be expanded to shape of array component '%s'"_err_en_US,
|
||||
symbol->name()),
|
||||
*symbol);
|
||||
} else {
|
||||
}
|
||||
if (checked.value_or(true)) {
|
||||
result.Add(*symbol, std::move(*converted));
|
||||
}
|
||||
}
|
||||
|
@ -3146,8 +3150,9 @@ bool ArgumentAnalyzer::CheckConformance() const {
|
|||
auto rhShape{GetShape(foldingContext, *rhs)};
|
||||
if (lhShape && rhShape) {
|
||||
return evaluate::CheckConformance(foldingContext.messages(), *lhShape,
|
||||
*rhShape, "left operand", "right operand", true,
|
||||
true /* scalar expansion is allowed */);
|
||||
*rhShape, CheckConformanceFlags::EitherScalarExpandable,
|
||||
"left operand", "right operand")
|
||||
.value_or(false /*fail when conformance is not known now*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,7 +171,7 @@ bool PointerAssignmentChecker::Check(const evaluate::FunctionRef<T> &f) {
|
|||
CHECK(frTypeAndShape);
|
||||
if (!lhsType_->IsCompatibleWith(context_.messages(), *frTypeAndShape,
|
||||
"pointer", "function result", false /*elemental*/,
|
||||
true /*left: deferred shape*/, true /*right: deferred shape*/)) {
|
||||
evaluate::CheckConformanceFlags::BothDeferredShape)) {
|
||||
msg = "%s is associated with the result of a reference to function '%s'"
|
||||
" whose pointer result has an incompatible type or shape"_err_en_US;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue