mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Check operator declaration binding in the first pass for FuncDecls.
This eliminates the pre-pass we were performing to bind the operator
decls, which was only a pre-pass because we used to bind unresolved
declaration references too early. In the process, fixed some bugs
(e.g., it wasn't checking methods at all) and improved the QoI with
Fix-Its and notes:
t.swift:2:6: error: prefix unary operator missing 'prefix' attribute
func ~~~(x : Float) {}
^
[prefix]
t.swift:1:17: note: prefix operator found here
operator prefix ~~~ {}
^
Swift SVN r7099
This commit is contained in:
@@ -995,6 +995,11 @@ ERROR(declared_operator_without_operator_decl,sema_nb,none,
|
|||||||
"operator implementation without matching operator declaration", ())
|
"operator implementation without matching operator declaration", ())
|
||||||
ERROR(declared_unary_op_without_attribute,sema_nb,none,
|
ERROR(declared_unary_op_without_attribute,sema_nb,none,
|
||||||
"unary operator implementation must have a 'prefix' or 'postfix' attribute", ())
|
"unary operator implementation must have a 'prefix' or 'postfix' attribute", ())
|
||||||
|
ERROR(unary_op_missing_prepos_attribute,sema_nb,none,
|
||||||
|
"%select{prefix|postfix}0 unary operator missing "
|
||||||
|
"'%select{prefix|postfix}0' attribute", (bool))
|
||||||
|
NOTE(unary_operator_declaration_here,sema_nb,none,
|
||||||
|
"%select{prefix|postfix}0 operator found here", (bool))
|
||||||
ERROR(invalid_arg_count_for_operator,sema_nb,none,
|
ERROR(invalid_arg_count_for_operator,sema_nb,none,
|
||||||
"operators must have one or two arguments", ())
|
"operators must have one or two arguments", ())
|
||||||
|
|
||||||
|
|||||||
@@ -238,12 +238,12 @@ Optional<OP_DECL *> lookupOperatorDeclForName(Module *M,
|
|||||||
|
|
||||||
auto *TU = dyn_cast<TranslationUnit>(M);
|
auto *TU = dyn_cast<TranslationUnit>(M);
|
||||||
if (!TU)
|
if (!TU)
|
||||||
return nullptr;
|
return Nothing;
|
||||||
|
|
||||||
// Look for an operator declaration in the current module.
|
// Look for an operator declaration in the current module.
|
||||||
auto found = (TU->*OP_MAP).find(Name.get());
|
auto found = (TU->*OP_MAP).find(Name.get());
|
||||||
if (found != (TU->*OP_MAP).end())
|
if (found != (TU->*OP_MAP).end())
|
||||||
return found->getValue();
|
return found->getValue()? Optional<OP_DECL *>(found->getValue()) : Nothing;
|
||||||
|
|
||||||
// Look for imported operator decls.
|
// Look for imported operator decls.
|
||||||
|
|
||||||
@@ -262,7 +262,7 @@ Optional<OP_DECL *> lookupOperatorDeclForName(Module *M,
|
|||||||
if (importedOperators.empty()) {
|
if (importedOperators.empty()) {
|
||||||
// Cache the mapping so we don't need to troll imports next time.
|
// Cache the mapping so we don't need to troll imports next time.
|
||||||
(TU->*OP_MAP)[Name.get()] = nullptr;
|
(TU->*OP_MAP)[Name.get()] = nullptr;
|
||||||
return nullptr;
|
return Nothing;
|
||||||
}
|
}
|
||||||
if (importedOperators.size() == 1) {
|
if (importedOperators.size() == 1) {
|
||||||
// Cache the mapping so we don't need to troll imports next time.
|
// Cache the mapping so we don't need to troll imports next time.
|
||||||
|
|||||||
@@ -947,6 +947,88 @@ public:
|
|||||||
FE->setType(funcTy);
|
FE->setType(funcTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bind the given function declaration, which declares an operator, to
|
||||||
|
/// the corresponding operator declaration.
|
||||||
|
void bindFuncDeclToOperator(FuncDecl *FD) {
|
||||||
|
OperatorDecl *op = nullptr;
|
||||||
|
auto &TU = TC.TU;
|
||||||
|
if (FD->isUnaryOperator()) {
|
||||||
|
if (FD->getAttrs().isPrefix()) {
|
||||||
|
if (auto maybeOp = TU.lookupPrefixOperator(FD->getName(), FD->getLoc()))
|
||||||
|
op = *maybeOp;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
} else if (FD->getAttrs().isPostfix()) {
|
||||||
|
if (auto maybeOp = TU.lookupPostfixOperator(FD->getName(),FD->getLoc()))
|
||||||
|
op = *maybeOp;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
auto prefixOp = TU.lookupPrefixOperator(FD->getName(), FD->getLoc());
|
||||||
|
auto postfixOp = TU.lookupPostfixOperator(FD->getName(), FD->getLoc());
|
||||||
|
|
||||||
|
// If we found both prefix and postfix, or neither prefix nor postfix,
|
||||||
|
// complain. We can't fix this situation.
|
||||||
|
if (static_cast<bool>(prefixOp) == static_cast<bool>(postfixOp)) {
|
||||||
|
TC.diagnose(FD, diag::declared_unary_op_without_attribute);
|
||||||
|
|
||||||
|
// If we found both, point at them.
|
||||||
|
if (prefixOp) {
|
||||||
|
SourceLoc insertionLoc = FD->getLoc();
|
||||||
|
|
||||||
|
TC.diagnose(*prefixOp, diag::unary_operator_declaration_here,false)
|
||||||
|
.fixItInsert(insertionLoc, "[prefix] ");
|
||||||
|
TC.diagnose(*postfixOp, diag::unary_operator_declaration_here, true)
|
||||||
|
.fixItInsert(insertionLoc, "[postfix] ");
|
||||||
|
} else {
|
||||||
|
// FIXME: Introduce a Fix-It that adds the operator declaration?
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Errors could cascade here, because name lookup for this
|
||||||
|
// operator won't find this declaration.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We found only one operator declaration, so we know whether this
|
||||||
|
// should be a prefix or a postfix operator.
|
||||||
|
|
||||||
|
// Fix the AST and determine the insertion text.
|
||||||
|
SourceLoc insertionLoc = FD->getLoc();
|
||||||
|
const char *insertionText;
|
||||||
|
if (postfixOp) {
|
||||||
|
insertionText = "[postfix] ";
|
||||||
|
op = *postfixOp;
|
||||||
|
FD->getMutableAttrs().ExplicitPostfix = true;
|
||||||
|
} else {
|
||||||
|
insertionText = "[prefix] ";
|
||||||
|
op = *prefixOp;
|
||||||
|
FD->getMutableAttrs().ExplicitPrefix = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit diagnostic with the Fix-It.
|
||||||
|
TC.diagnose(FD, diag::unary_op_missing_prepos_attribute,
|
||||||
|
static_cast<bool>(postfixOp))
|
||||||
|
.fixItInsert(insertionLoc, insertionText);
|
||||||
|
TC.diagnose(op, diag::unary_operator_declaration_here,
|
||||||
|
static_cast<bool>(postfixOp));
|
||||||
|
}
|
||||||
|
} else if (FD->isBinaryOperator()) {
|
||||||
|
if (auto maybeOp = TU.lookupInfixOperator(FD->getName(), FD->getLoc()))
|
||||||
|
op = *maybeOp;
|
||||||
|
else {
|
||||||
|
// FIXME: Add Fix-It introducing an operator declaration?
|
||||||
|
TC.diagnose(FD, diag::declared_operator_without_operator_decl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TC.diagnose(FD, diag::invalid_arg_count_for_operator);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(op && "Should have computed operator above");
|
||||||
|
FD->setOperatorDecl(op);
|
||||||
|
}
|
||||||
|
|
||||||
void visitFuncDecl(FuncDecl *FD) {
|
void visitFuncDecl(FuncDecl *FD) {
|
||||||
if (!IsFirstPass) {
|
if (!IsFirstPass) {
|
||||||
if (auto body = FD->getBody())
|
if (auto body = FD->getBody())
|
||||||
@@ -957,6 +1039,10 @@ public:
|
|||||||
if (IsSecondPass)
|
if (IsSecondPass)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Bind operator functions to the corresponding operator declaration.
|
||||||
|
if (FD->isOperator())
|
||||||
|
bindFuncDeclToOperator(FD);
|
||||||
|
|
||||||
FuncExpr *body = FD->getBody();
|
FuncExpr *body = FD->getBody();
|
||||||
|
|
||||||
// Before anything else, set up the 'this' argument correctly.
|
// Before anything else, set up the 'this' argument correctly.
|
||||||
|
|||||||
@@ -465,41 +465,6 @@ static void bindExtensionDecl(ExtensionDecl *ED, TypeChecker &TC) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bindFuncDeclToOperator(TypeChecker &TC,
|
|
||||||
TranslationUnit *TU,
|
|
||||||
FuncDecl *FD) {
|
|
||||||
OperatorDecl *op;
|
|
||||||
if (FD->isUnaryOperator()) {
|
|
||||||
if (FD->getAttrs().isPrefix()) {
|
|
||||||
if (auto maybeOp = TU->lookupPrefixOperator(FD->getName(), FD->getLoc()))
|
|
||||||
op = *maybeOp;
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
} else if (FD->getAttrs().isPostfix()) {
|
|
||||||
if (auto maybeOp = TU->lookupPostfixOperator(FD->getName(), FD->getLoc()))
|
|
||||||
op = *maybeOp;
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
TC.diagnose(FD, diag::declared_unary_op_without_attribute);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (FD->isBinaryOperator()) {
|
|
||||||
if (auto maybeOp = TU->lookupInfixOperator(FD->getName(), FD->getLoc()))
|
|
||||||
op = *maybeOp;
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
TC.diagnose(FD, diag::invalid_arg_count_for_operator);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!op)
|
|
||||||
TC.diagnose(FD, diag::declared_operator_without_operator_decl);
|
|
||||||
else
|
|
||||||
FD->setOperatorDecl(op);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// performTypeChecking - Once parsing and namebinding are complete, these
|
/// performTypeChecking - Once parsing and namebinding are complete, these
|
||||||
/// walks the AST to resolve types and diagnose problems therein.
|
/// walks the AST to resolve types and diagnose problems therein.
|
||||||
///
|
///
|
||||||
@@ -514,21 +479,13 @@ void swift::performTypeChecking(TranslationUnit *TU, unsigned StartElem) {
|
|||||||
TypeChecker TC(*TU);
|
TypeChecker TC(*TU);
|
||||||
auto &FuncExprs = TC.definedFunctions;
|
auto &FuncExprs = TC.definedFunctions;
|
||||||
|
|
||||||
// Resolve extensions and operator declarations.
|
// Resolve extensions. This has to occur first during type checking,
|
||||||
// FIXME: Feels too early to care about operator declarations.
|
// because
|
||||||
for (unsigned i = StartElem, e = TU->Decls.size(); i != e; ++i) {
|
for (unsigned i = StartElem, e = TU->Decls.size(); i != e; ++i) {
|
||||||
if (ExtensionDecl *ED = dyn_cast<ExtensionDecl>(TU->Decls[i])) {
|
if (ExtensionDecl *ED = dyn_cast<ExtensionDecl>(TU->Decls[i])) {
|
||||||
bindExtensionDecl(ED, TC);
|
bindExtensionDecl(ED, TC);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FuncDecl *FD = dyn_cast<FuncDecl>(TU->Decls[i])) {
|
|
||||||
// If this is an operator implementation, bind it to an operator decl.
|
|
||||||
if (FD->isOperator())
|
|
||||||
bindFuncDeclToOperator(TC, TU, FD);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Check for cycles in class inheritance here?
|
// FIXME: Check for cycles in class inheritance here?
|
||||||
|
|||||||
Reference in New Issue
Block a user