* @file
*
* This file implements inheritance checking of structure declarations.
*/
#include "StructInheritanceChecker.h"
#include <algorithm>
#include "cangjie/AST/Match.h"
#include "cangjie/AST/Node.h"
#include "cangjie/AST/Types.h"
#include "cangjie/AST/Utils.h"
#include "cangjie/AST/Walker.h"
#include "cangjie/Sema/TypeManager.h"
#include "Diags.h"
#include "TypeCheckUtil.h"
using namespace Cangjie;
using namespace AST;
using namespace Sema;
using namespace TypeCheckUtil;
namespace {
void MarkUpperBoundIgnored(const Node& node, Generic& generic)
{
for (const auto& constraint : generic.genericConstraints) {
for (const auto& upperBound : constraint->upperBounds) {
if (upperBound->GetTy()->HasGeneric()) {
upperBound->visitedByWalkerID = node.visitedByWalkerID;
}
}
}
}
std::pair<Ptr<Ty>, Ptr<Decl>> GetRealReferenceType(const Node& node)
{
auto target = GetRealTarget(node.GetTarget());
auto baseTy = node.GetTy();
if (target && IsClassOrEnumConstructor(*target)) {
target = target->outerDecl;
CJC_ASSERT(target != nullptr);
if (node.GetTy()->IsFunc()) {
baseTy = RawStaticCast<FuncTy*>(node.GetTy())->retTy;
}
}
return {baseTy, target};
}
}
void StructInheritanceChecker::CheckInstMemberSignatures(
const InheritableDecl& decl, const std::vector<Ptr<Ty>>& instTys)
{
if (instTriggerInfos.empty()) {
return;
}
auto searchKey = std::make_pair(&decl, instTys);
if (instantiatedDecls.count(searchKey) != 0) {
for (auto& [parent, member] : std::as_const(genericMembersForInstantiatedDecl[searchKey])) {
DiagnoseForInstantiatedMember(parent, member);
}
return;
}
auto typeMapping = GenerateTypeMapping(decl, instTys);
CheckMembersWithInheritedDecls(decl);
auto members = structInheritedMembers[&decl];
for (auto& member : decl.GetMemberDecls()) {
CJC_NULLPTR_CHECK(member);
if (!Ty::IsTyCorrect(member->GetTy()) || !member->outerDecl) {
continue;
}
if (IsInstanceConstructor(*member) || member->TestAttr(Attribute::PRIVATE)) {
(void)UpdateInheritedMemberIfNeeded(members, MemberSignature{member, member->GetTy(), decl.GetTy()});
}
}
MemberMap genericTyMembers;
for (auto it = members.begin(); it != members.end();) {
if (it->second.decl->TestAttr(Attribute::GENERIC) || it->second.decl->astKind != ASTKind::FUNC_DECL) {
it = members.erase(it);
} else if (it->second.ty->HasGeneric()) {
genericTyMembers.emplace(it->first, it->second);
it = members.erase(it);
} else {
++it;
}
}
auto& cache = instantiatedTyCache[searchKey];
auto getAndUpdateCache = [this, &cache, &typeMapping](auto ty) {
auto found = cache.find(ty);
if (found != cache.end()) {
return found->second;
} else {
auto instTy = typeManager.GetInstantiatedTy(ty, typeMapping);
cache[ty] = instTy;
return instTy;
}
};
for (auto [identifier, memberSig] : std::as_const(genericTyMembers)) {
memberSig.ty = getAndUpdateCache(memberSig.ty);
memberSig.structTy = getAndUpdateCache(memberSig.structTy);
auto matches = members.equal_range(identifier);
for (auto it = matches.first; it != matches.second; ++it) {
auto parent = it->second;
parent.structTy = getAndUpdateCache(parent.structTy);
DiagnoseForInstantiatedMember(parent, memberSig);
genericMembersForInstantiatedDecl[searchKey].emplace_back(std::make_pair(parent, memberSig));
}
members.emplace(identifier, memberSig);
}
}
std::set<Ptr<ExtendDecl>, CmpNodeByPos> StructInheritanceChecker::GetVisibleExtendsForInstantiation(
const Decl& decl, const std::vector<Ptr<Ty>>& instTys)
{
if (decl.astKind == ASTKind::EXTEND_DECL || !Ty::IsTyCorrect(decl.GetTy())) {
return {};
}
std::set<Ptr<ExtendDecl>, CmpNodeByPos> orderedVisibleExtends;
auto extends = typeManager.GetAllExtendsByTy(*decl.GetTy());
for (auto& extend : extends) {
bool visibleExtend = (IsExtendVisibleInCurpkg(*extend)) && extend->TestAttr(Attribute::GENERIC) &&
typeManager.CheckGenericDeclInstantiation(extend, instTys);
if (visibleExtend) {
orderedVisibleExtends.emplace(extend);
}
}
return orderedVisibleExtends;
}
bool StructInheritanceChecker::WillCauseInfiniteInstantiation(
const Node& triggerNode, const Decl& decl, const std::vector<Ptr<Ty>>& instTys)
{
auto generic = decl.GetGeneric();
CJC_ASSERT(generic && generic->typeParameters.size() == instTys.size());
bool triggeredInside = false;
TypeSubst typeMapping = institutionMaps.empty() ? TypeSubst{} : institutionMaps.top();
if (HaveCyclicSubstitution(typeManager, GenerateTypeMapping(decl, instTys)) ||
HaveCyclicSubstitution(typeManager, typeMapping)) {
diag.Diagnose(triggerNode, DiagKind::sema_generic_infinite_instantiation);
infiniteInstantiationOccured = true;
return true;
}
if (!instTriggerInfos.empty()) {
triggeredInside = &decl == std::get<1>(instTriggerInfos.back());
}
Ptr<Ty> genericTy = TypeManager::GetInvalidTy();
auto checkRecursion = [&genericTy, triggeredInside, typeMapping, this](auto ty) -> bool {
CJC_ASSERT(ty);
auto substitutedTy = typeManager.GetInstantiatedTy(ty, typeMapping);
bool hasRecursion = ty->Contains(genericTy) || substitutedTy->Contains(genericTy);
if (hasRecursion) {
return true;
}
auto typeArg = Ty::GetGenericTyOfInsTy(*ty);
bool noRecursion = !triggeredInside || (typeArg && typeArg->Contains(genericTy));
if (noRecursion) {
return false;
}
substitutedTy = Ty::GetGenericTyOfInsTy(*substitutedTy);
return substitutedTy && substitutedTy->Contains(genericTy);
};
bool hasSelfRecursion = false;
for (size_t i = 0; i < instTys.size(); ++i) {
genericTy = generic->typeParameters[i]->GetTy();
hasSelfRecursion = std::any_of(instTys[i]->typeArgs.begin(), instTys[i]->typeArgs.end(), checkRecursion);
if (hasSelfRecursion) {
break;
}
}
if (hasSelfRecursion) {
diag.Diagnose(triggerNode, DiagKind::sema_generic_infinite_instantiation);
infiniteInstantiationOccured = true;
}
return hasSelfRecursion;
}
VisitAction StructInheritanceChecker::CheckInstDupFuncsRecursively(Node& node)
{
if (infiniteInstantiationOccured) {
return VisitAction::STOP_NOW;
}
if (node.astKind == ASTKind::GENERIC) {
MarkUpperBoundIgnored(node, static_cast<Generic&>(node));
}
if (!Ty::IsTyCorrect(node.GetTy()) || node.TestAttr(Attribute::HAS_BROKEN)) {
return VisitAction::WALK_CHILDREN;
}
auto [baseTy, target] = GetRealReferenceType(node);
if (!target || target->TestAttr(Attribute::IN_REFERENCE_CYCLE) || !Ty::IsTyCorrect(baseTy)) {
return VisitAction::WALK_CHILDREN;
}
if (target->IsNominalDecl() && target->TestAttr(Attribute::GENERIC)) {
if (auto [_, success] = instantiatedDecls.insert(std::make_pair(target, std::vector<Ptr<Ty>>{})); success) {
Walker(target, [this](auto node) { return CheckInstDupFuncsRecursively(*node); }).Walk();
}
}
CheckInstWithCStructTypeArg(node);
auto ref = DynamicCast<NameReferenceExpr*>(&node);
std::vector<Ptr<Ty>> instTys = ref ? ref->instTys : typeManager.GetTypeArgs(*baseTy);
TypeSubst typeMapping = GenerateTypeMapping(*target, instTys);
if (typeMapping.empty()) {
return VisitAction::WALK_CHILDREN;
}
if (WillCauseInfiniteInstantiation(node, *target, instTys)) {
return VisitAction::STOP_NOW;
}
if (!institutionMaps.empty()) {
for (auto& ty : instTys) {
ty = typeManager.GetInstantiatedTy(ty, institutionMaps.top());
}
}
bool updateTriggerInfo = !baseTy->HasGeneric() || institutionMaps.empty();
auto instantiateCtx = InstantiatedContext(*this, &node, target, instTys, updateTriggerInfo);
if (target->IsNominalDecl()) {
CheckInstMemberSignatures(*RawStaticCast<InheritableDecl*>(target), instTys);
}
auto extends = GetVisibleExtendsForInstantiation(*target, instTys);
for (auto extend : extends) {
CheckInstMemberSignatures(*extend, instTys);
instantiatedDecls.insert(std::make_pair(target, instTys));
}
if (auto [_, success] = instantiatedDecls.insert(std::make_pair(target, instTys)); success) {
CheckInstantiatedDecl(*target, instTys);
}
return VisitAction::WALK_CHILDREN;
}
void StructInheritanceChecker::CheckInstantiatedDecl(Decl& decl, const std::vector<Ptr<Ty>>& instTys)
{
if (decl.IsBuiltIn()) {
return;
}
TypeSubst typeMapping;
if (auto ed = DynamicCast<ExtendDecl*>(&decl); ed && ed->GetTy() && ed->GetTy()->IsNominal()) {
auto target = Ty::GetDeclPtrOfTy(ed->GetTy());
if (target == nullptr) {
return;
}
typeMapping = GenerateTypeMapping(*target, instTys);
typeMapping.merge(GenerateTypeMapping(decl, ed->GetTy()->typeArgs));
} else {
typeMapping = GenerateTypeMapping(decl, instTys);
}
institutionMaps.push(typeMapping);
Walker(&decl, [this](auto node) { return CheckInstDupFuncsRecursively(*node); }).Walk();
institutionMaps.pop();
}
void StructInheritanceChecker::CheckInstDupFuncsInNominalDecls()
{
for (auto& file : pkg.files) {
for (auto& decl : file->decls) {
if (auto [_, success] = instantiatedDecls.insert(std::make_pair(decl.get(), std::vector<Ptr<Ty>>{}));
success) {
Walker(decl.get(), [this](auto node) { return CheckInstDupFuncsRecursively(*node); }).Walk();
}
}
}
}
void StructInheritanceChecker::CheckInstWithCStructTypeArg(const Node& node)
{
if (!Utils::In(node.astKind, {ASTKind::REF_TYPE, ASTKind::REF_EXPR, ASTKind::MEMBER_ACCESS})) {
return;
}
if (auto rt = DynamicCast<const RefType*>(&node); rt) {
if (!rt->typeArguments.empty()) {
CheckCStructArguments(*rt, *rt->GetTy(), rt->leftAnglePos, rt->GetTypeArgs());
}
return;
}
auto target = node.GetTarget();
if (!target) {
return;
}
if (node.astKind == ASTKind::MEMBER_ACCESS && target->TestAttr(Attribute::ENUM_CONSTRUCTOR)) {
return;
}
auto& ref = static_cast<const NameReferenceExpr&>(node);
if (ref.instTys.empty()) {
return;
}
auto typeArgs = ref.GetTypeArgs();
if (ref.instTys.size() == typeArgs.size()) {
auto ty = ref.GetTy();
if (ref.astKind == ASTKind::REF_EXPR && target->TestAttr(Attribute::CONSTRUCTOR)) {
CJC_NULLPTR_CHECK(target->outerDecl);
ty = target->outerDecl->GetTy();
}
Position leftAnglePos = ref.astKind == ASTKind::REF_EXPR ? static_cast<const RefExpr&>(ref).leftAnglePos
: static_cast<const MemberAccess&>(ref).leftAnglePos;
CheckCStructArguments(ref, *ty, leftAnglePos, typeArgs);
return;
}
}
void StructInheritanceChecker::CheckCStructArguments(const Node& node, const AST::Ty& ty, const Position& leftAnglePos,
const std::vector<Ptr<Cangjie::AST::Type>>& typeArgs)
{
if (typeArgs.empty()) {
return;
}
if (Ty::IsCStructType(ty)) {
(void)diag.Diagnose(node, leftAnglePos, DiagKind::sema_cffi_cannot_have_type_param, "struct with @C");
}
for (auto& type : typeArgs) {
CheckCStructArgument(ty, *type);
}
}
void StructInheritanceChecker::CheckCStructArgument(const Ty& ty, const Type& typeArg)
{
if (!Ty::IsTyCorrect(typeArg.GetTy())) {
return;
}
if (ty.IsPointer()) {
if (!typeArg.GetTy()->IsGeneric() && !Ty::IsMetCType(*typeArg.GetTy())) {
diag.Diagnose(typeArg, DiagKind::sema_illegal_cpointer_generic_type);
}
return;
}
if (ty.IsStructArray() && (typeArg.GetTy()->IsCString() || typeArg.GetTy()->IsPointer())) {
return;
}
CheckCStruct(*typeArg.GetTy(), typeArg);
}
void StructInheritanceChecker::CheckCStruct(const Ty& ty, const Type& typeArg)
{
if (!Ty::IsTyCorrect(&ty)) {
return;
}
if (ty.IsPointer()) {
if (!ty.typeArgs.empty() && ty.typeArgs[0] && !ty.typeArgs[0]->IsGeneric() &&
!Ty::IsMetCType(*ty.typeArgs[0])) {
diag.Diagnose(typeArg, DiagKind::sema_illegal_cpointer_generic_type);
}
return;
}
if (auto fty = DynamicCast<const FuncTy*>(&ty); fty) {
for (auto it : fty->typeArgs) {
CheckCStruct(*it, typeArg);
}
return;
}
for (auto it : ty.typeArgs) {
auto sty = DynamicCast<StructTy*>(it);
if (sty == nullptr) {
continue;
}
CheckCStruct(*it, typeArg);
}
}
void StructInheritanceChecker::DiagnoseForInstantiatedMember(
const MemberSignature& parent, const MemberSignature& child) const
{
if (instTriggerInfos.empty()) {
return;
}
auto parentFuncTy = DynamicCast<FuncTy*>(parent.ty);
auto childFuncTy = DynamicCast<FuncTy*>(child.ty);
bool sameStatus = parent.decl->TestAttr(Attribute::STATIC) == child.decl->TestAttr(Attribute::STATIC);
bool isConflicted = sameStatus && parentFuncTy && childFuncTy &&
typeManager.IsFuncParameterTypesIdentical(*parentFuncTy, *childFuncTy);
if (isConflicted) {
std::string functionName = child.decl->identifier;
OrderedDeclSet candidates = {parent.decl, child.decl};
auto [triggerNode, triggerDecl, instTys] = instTriggerInfos.back();
std::string triggeredDeclType = triggerDecl->identifier.Val() + '<' + Ty::GetTypesToStr(instTys, ", ") + ">";
auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_instantiation_causes_ambiguous_functions,
*triggerNode, triggeredDeclType, functionName);
for (auto& candidate : candidates) {
builder.AddNote(*candidate, MakeRangeForDeclIdentifier(*candidate), "found candidate");
}
}
}