From 9584508d5c4eb4ca57fd457d914bb9866ce13d18 Mon Sep 17 00:00:00 2001 From: George Burgess IV Date: Wed, 15 Feb 2017 22:43:27 +0000 Subject: [PATCH] [Modules] Consider enable_if attrs in isSameEntity. Two functions that differ only in their enable_if attributes are considered overloads, so we should check for those when we're trying to figure out if two functions are mergeable. We need to do the same thing for pass_object_size, as well. Looks like that'll be a bit less trivial, since we sometimes do these merging checks before we have pass_object_size attributes available (see the merge checks in ASTDeclReader::VisitFunctionDecl that happen before we read parameters, and merge checks in calls to ReadDeclAs<>()). llvm-svn: 295252 --- clang/lib/Serialization/ASTReaderDecl.cpp | 44 ++++++++++++++++++- .../Modules/Inputs/overloadable-attrs/a.h | 16 +++++++ .../overloadable-attrs/module.modulemap | 3 ++ clang/test/Modules/overloadable-attrs.cpp | 19 ++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 clang/test/Modules/Inputs/overloadable-attrs/a.h create mode 100644 clang/test/Modules/Inputs/overloadable-attrs/module.modulemap create mode 100644 clang/test/Modules/overloadable-attrs.cpp diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 8b14c2d8254f..9be3981329a0 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2656,6 +2656,44 @@ static bool isSameTemplateParameterList(const TemplateParameterList *X, return true; } +/// Determine whether the attributes we can overload on are identical for A and +/// B. Expects A and B to (otherwise) have the same type. +static bool hasSameOverloadableAttrs(const FunctionDecl *A, + const FunctionDecl *B) { + SmallVector AEnableIfs; + // Since this is an equality check, we can ignore that enable_if attrs show up + // in reverse order. + for (const auto *EIA : A->specific_attrs()) + AEnableIfs.push_back(EIA); + + SmallVector BEnableIfs; + for (const auto *EIA : B->specific_attrs()) + BEnableIfs.push_back(EIA); + + // Two very common cases: either we have 0 enable_if attrs, or we have an + // unequal number of enable_if attrs. + if (AEnableIfs.empty() && BEnableIfs.empty()) + return true; + + if (AEnableIfs.size() != BEnableIfs.size()) + return false; + + llvm::FoldingSetNodeID Cand1ID, Cand2ID; + for (unsigned I = 0, E = AEnableIfs.size(); I != E; ++I) { + Cand1ID.clear(); + Cand2ID.clear(); + + AEnableIfs[I]->getCond()->Profile(Cand1ID, A->getASTContext(), true); + BEnableIfs[I]->getCond()->Profile(Cand2ID, B->getASTContext(), true); + if (Cand1ID != Cand2ID) + return false; + } + + // FIXME: This doesn't currently consider pass_object_size attributes, since + // we aren't guaranteed that A and B have valid parameter lists yet. + return true; +} + /// \brief Determine whether the two declarations refer to the same entity. static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { assert(X->getDeclName() == Y->getDeclName() && "Declaration name mismatch!"); @@ -2711,8 +2749,10 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { CtorY->getInheritedConstructor().getConstructor())) return false; } - return (FuncX->getLinkageInternal() == FuncY->getLinkageInternal()) && - FuncX->getASTContext().hasSameType(FuncX->getType(), FuncY->getType()); + return FuncX->getLinkageInternal() == FuncY->getLinkageInternal() && + FuncX->getASTContext().hasSameType(FuncX->getType(), + FuncY->getType()) && + hasSameOverloadableAttrs(FuncX, FuncY); } // Variables with the same type and linkage match. diff --git a/clang/test/Modules/Inputs/overloadable-attrs/a.h b/clang/test/Modules/Inputs/overloadable-attrs/a.h new file mode 100644 index 000000000000..e7dabfbe586c --- /dev/null +++ b/clang/test/Modules/Inputs/overloadable-attrs/a.h @@ -0,0 +1,16 @@ +namespace enable_if_attrs { +constexpr int fn1() __attribute__((enable_if(0, ""))) { return 0; } +constexpr int fn1() { return 1; } + +constexpr int fn2() { return 1; } +constexpr int fn2() __attribute__((enable_if(0, ""))) { return 0; } + +constexpr int fn3(int i) __attribute__((enable_if(!i, ""))) { return 0; } +constexpr int fn3(int i) __attribute__((enable_if(i, ""))) { return 1; } + +constexpr int fn4(int i) { return 0; } +constexpr int fn4(int i) __attribute__((enable_if(i, ""))) { return 1; } + +constexpr int fn5(int i) __attribute__((enable_if(i, ""))) { return 1; } +constexpr int fn5(int i) { return 0; } +} diff --git a/clang/test/Modules/Inputs/overloadable-attrs/module.modulemap b/clang/test/Modules/Inputs/overloadable-attrs/module.modulemap new file mode 100644 index 000000000000..514d745d1465 --- /dev/null +++ b/clang/test/Modules/Inputs/overloadable-attrs/module.modulemap @@ -0,0 +1,3 @@ +module a { + header "a.h" +} diff --git a/clang/test/Modules/overloadable-attrs.cpp b/clang/test/Modules/overloadable-attrs.cpp new file mode 100644 index 000000000000..ae9bd73f99f8 --- /dev/null +++ b/clang/test/Modules/overloadable-attrs.cpp @@ -0,0 +1,19 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -I%S/Inputs/overloadable-attrs -fmodules \ +// RUN: -fmodule-map-file=%S/Inputs/overloadable-attrs/module.modulemap \ +// RUN: -fmodules-cache-path=%t -verify %s -std=c++11 +// +// Ensures that we don't merge decls with attrs that we allow overloading on. +// +// expected-no-diagnostics + +#include "a.h" + +static_assert(enable_if_attrs::fn1() == 1, ""); +static_assert(enable_if_attrs::fn2() == 1, ""); +static_assert(enable_if_attrs::fn3(0) == 0, ""); +static_assert(enable_if_attrs::fn3(1) == 1, ""); +static_assert(enable_if_attrs::fn4(0) == 0, ""); +static_assert(enable_if_attrs::fn4(1) == 1, ""); +static_assert(enable_if_attrs::fn5(0) == 0, ""); +static_assert(enable_if_attrs::fn5(1) == 1, "");