Skip to content

[DNM][stdlib] Add exponentiation precedence group and operator for diagnostic purposes #36041

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,11 @@ DECL_ATTR(hasAsyncAlternative, HasAsyncAlternative,
APIStableToAdd | APIStableToRemove,
111)

SIMPLE_DECL_ATTR(apex, Apex,
OnPrecedenceGroup |
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove,
112)

#undef TYPE_ATTR
#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6994,6 +6994,10 @@ class PrecedenceGroupDecl : public Decl {
MutableArrayRef<Relation> getMutableLowerThan() {
return { getLowerThanBuffer(), NumLowerThan };
}

bool isApex() const {
return getAttrs().hasAttribute<ApexAttr>();
}

static bool classof(const Decl *D) {
return D->getKind() == DeclKind::PrecedenceGroup;
Expand Down
11 changes: 9 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,7 @@ ERROR(unknown_precedence_group,none,
ERROR(precedence_group_cycle,none,
"cycle in '%select{lowerThan|higherThan}0' relation", (bool))
ERROR(higher_than_precedence_group_cycle,none,
"cycle in higherThan relation: %0", (StringRef))
"cycle in 'higherThan' relation: %0", (StringRef))
ERROR(precedence_group_lower_within_module,none,
"precedence group cannot be given lower precedence than group in same"
" module; make the other precedence group higher than this one instead",
Expand All @@ -976,6 +976,9 @@ NOTE(previous_precedence_group_decl,none,
"previous precedence group declaration here", ())
NOTE(circular_reference_through_precedence_group, none,
"through reference to precedence group %0 here", (Identifier))
ERROR(precedence_group_higher_than_apex,none,
"precedence group cannot be given higher precedence than '@apex' group",
())

//------------------------------------------------------------------------------
// MARK: Expression Type Checking Errors
Expand Down Expand Up @@ -3381,13 +3384,17 @@ ERROR(construct_protocol_by_name,none,

// Operators
ERROR(unknown_binop,none,
"operator is not a known binary operator", ())
"operator is not a known binary operator", ())
ERROR(non_associative_adjacent_operators,none,
"adjacent operators are in non-associative precedence group %0",
(Identifier))
ERROR(unordered_adjacent_operators,none,
"adjacent operators are in unordered precedence groups %0 and %1",
(Identifier, Identifier))
ERROR(unordered_adjacent_unary_operator,none,
"operator %0 has undefined precedence relative to unary operator; use"
"grouping parentheses",
(Identifier))
ERROR(missing_builtin_precedence_group,none,
"broken standard library: missing builtin precedence group %0",
(Identifier))
Expand Down
19 changes: 18 additions & 1 deletion lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1450,8 +1450,18 @@ void swift::validatePrecedenceGroup(PrecedenceGroupDecl *PGD) {
continue;

// TODO: Requestify the lookup of a relation's group.
rel.Group = lookupPrecedenceGroupForRelation(
auto *group = lookupPrecedenceGroupForRelation(
dc, rel, PrecedenceGroupDescriptor::HigherThan);
rel.Group = group;

if (group && group->isApex()) {
if (!PGD->isInvalid()) {
Diags.diagnose(rel.NameLoc, diag::precedence_group_higher_than_apex);
Diags.diagnose(group->getNameLoc(), diag::kind_declared_here,
DescriptiveDeclKind::PrecedenceGroup);
}
PGD->setInvalid();
}
if (rel.Group) {
addedHigherThan = true;
} else {
Expand All @@ -1474,6 +1484,13 @@ void swift::validatePrecedenceGroup(PrecedenceGroupDecl *PGD) {
if (!group)
group = dc->lookupPrecedenceGroup(rel.Name).getSingle();

if (group && PGD->isApex()) {
if (!PGD->isInvalid()) {
Diags.diagnose(rel.NameLoc, diag::precedence_group_higher_than_apex);
}
PGD->setInvalid();
}

if (group &&
group->getDeclContext()->getParentModule() == dc->getParentModule()) {
if (!PGD->isInvalid()) {
Expand Down
19 changes: 19 additions & 0 deletions lib/Sema/TypeCheckExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,25 @@ static Expr *makeBinOp(ASTContext &Ctx, Expr *Op, Expr *LHS, Expr *RHS,
return await;
}

// Operators in apex precedence groups are unordered relative to prefix and
// postfix operators.
if (opPrecedence && opPrecedence->isApex()) {
if (auto *prefixUnary = dyn_cast<PrefixUnaryExpr>(LHS)) {
Ctx.Diags.diagnose(Op->getLoc(),
diag::unordered_adjacent_unary_operator,
Op->getName())
.highlight(prefixUnary->getFn()->getSourceRange())
.highlight(Op->getSourceRange());
}
if (auto *postfixUnary = dyn_cast<PostfixUnaryExpr>(RHS)) {
Ctx.Diags.diagnose(Op->getLoc(),
diag::unordered_adjacent_unary_operator,
Op->getName())
.highlight(Op->getSourceRange())
.highlight(postfixUnary->getFn()->getSourceRange());
}
}

// If this is an assignment operator, and the left operand is an optional
// evaluation, pull the operator into the chain.
if (opPrecedence && opPrecedence->isAssignment()) {
Expand Down
52 changes: 51 additions & 1 deletion stdlib/public/core/Policy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,10 @@ precedencegroup MultiplicationPrecedence {
precedencegroup BitwiseShiftPrecedence {
higherThan: MultiplicationPrecedence
}

@apex precedencegroup ExponentiationPrecedence {
associativity: right
higherThan: MultiplicationPrecedence
}

//===----------------------------------------------------------------------===//
// Standard operators
Expand Down Expand Up @@ -398,6 +401,7 @@ prefix operator ..<: Comparable

// "Exponentiative"

infix operator **: ExponentiationPrecedence
infix operator <<: BitwiseShiftPrecedence, BinaryInteger
infix operator &<<: BitwiseShiftPrecedence, FixedWidthInteger
infix operator >>: BitwiseShiftPrecedence, BinaryInteger
Expand Down Expand Up @@ -463,6 +467,7 @@ infix operator ||: LogicalDisjunctionPrecedence, Bool

// Compound

infix operator **=: AssignmentPrecedence
infix operator *=: AssignmentPrecedence, Numeric
infix operator &*=: AssignmentPrecedence, FixedWidthInteger
infix operator /=: AssignmentPrecedence, BinaryInteger
Expand All @@ -486,3 +491,48 @@ infix operator |=: AssignmentPrecedence, BinaryInteger
// example of how this operator is used, and how its use can be hidden
// from users.
infix operator ~>

//===----------------------------------------------------------------------===//
// Placeholder operator functions for diagnostic purposes
//===----------------------------------------------------------------------===//

extension Float {
@available(*, unavailable, message: "use 'Foundation.pow' instead")
@_alwaysEmitIntoClient
public static func ** (lhs: Self, rhs: Self) -> Self {
fatalError("Not implemented")
}
@available(*, unavailable, message: "use 'Foundation.pow' instead")
@_alwaysEmitIntoClient
public static func **= (lhs: inout Self, rhs: Self) {
fatalError("Not implemented")
}
}

extension Double {
@available(*, unavailable, message: "use 'Foundation.pow' instead")
@_alwaysEmitIntoClient
public static func ** (lhs: Self, rhs: Self) -> Self {
fatalError("Not implemented")
}
@available(*, unavailable, message: "use 'Foundation.pow' instead")
@_alwaysEmitIntoClient
public static func **= (lhs: inout Self, rhs: Self) {
fatalError("Not implemented")
}
}

#if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64))
extension Float80 {
@available(*, unavailable, message: "use 'Foundation.pow' instead")
@_alwaysEmitIntoClient
public static func ** (lhs: Self, rhs: Self) -> Self {
fatalError("Not implemented")
}
@available(*, unavailable, message: "use 'Foundation.pow' instead")
@_alwaysEmitIntoClient
public static func **= (lhs: inout Self, rhs: Self) {
fatalError("Not implemented")
}
}
#endif