void ClassScope::importUsedTraits(AnalysisResultPtr ar) { // Trait flattening is supposed to happen only when we have awareness of the // whole program. assert(Option::WholeProgram); if (m_traitStatus == FLATTENED) return; if (m_traitStatus == BEING_FLATTENED) { getStmt()->analysisTimeFatal( Compiler::CyclicDependentTraits, "Cyclic dependency between traits involving %s", getScopeName().c_str() ); return; } if (m_usedTraitNames.size() == 0) { m_traitStatus = FLATTENED; return; } m_traitStatus = BEING_FLATTENED; m_numDeclMethods = m_functionsVec.size(); // First, make sure that parent classes have their traits imported. if (!m_parent.empty()) { ClassScopePtr parent = ar->findClass(m_parent); if (parent) { parent->importUsedTraits(ar); } } TMIData tmid; if (isTrait()) { for (auto const& req : getClassRequiredExtends()) { ClassScopePtr rCls = ar->findClass(req); if (!rCls || rCls->isFinal() || rCls->isInterface()) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, Strings::TRAIT_BAD_REQ_EXTENDS, m_scopeName.c_str(), req.c_str(), req.c_str() ); } } for (auto const& req : getClassRequiredImplements()) { ClassScopePtr rCls = ar->findClass(req); if (!rCls || !(rCls->isInterface())) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, Strings::TRAIT_BAD_REQ_IMPLEMENTS, m_scopeName.c_str(), req.c_str(), req.c_str() ); } } } // Find trait methods to be imported. for (unsigned i = 0; i < m_usedTraitNames.size(); i++) { ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]); if (!tCls || !(tCls->isTrait())) { setAttribute(UsesUnknownTrait); // XXX: is this useful ... for anything? getStmt()->analysisTimeFatal( Compiler::UnknownTrait, Strings::TRAITS_UNKNOWN_TRAIT, m_usedTraitNames[i].c_str() ); } // First, make sure the used trait is flattened. tCls->importUsedTraits(ar); findTraitMethodsToImport(ar, tCls, tmid); // Import any interfaces implemented. tCls->getInterfaces(ar, m_bases, /* recursive */ false); importClassRequirements(ar, tCls); } // Apply rules. applyTraitRules(tmid); // Remove methods declared on the current class from the trait import list; // the class methods take precedence. for (auto const& methName : tmid.methodNames()) { if (findFunction(ar, methName, false /* recursive */, false /* exclIntfBase */)) { // This does not affect the methodNames() vector. tmid.erase(methName); } } auto traitMethods = tmid.finish(this); std::map<string, MethodStatementPtr, stdltistr> importedTraitMethods; std::vector<std::pair<string,const TraitMethod*>> importedTraitsWithOrigName; // Actually import the methods. for (auto const& mdata : traitMethods) { if ((mdata.tm.modifiers ? mdata.tm.modifiers : mdata.tm.method->getModifiers() )->isAbstract()) { // Skip abstract methods, if the method already exists in the class. if (findFunction(ar, mdata.name, true) || importedTraitMethods.count(mdata.name)) { continue; } } auto sourceName = mdata.tm.ruleStmt ? ((TraitAliasStatement*)mdata.tm.ruleStmt.get())->getMethodName() : mdata.name; importedTraitMethods[sourceName] = MethodStatementPtr(); importedTraitsWithOrigName.push_back( std::make_pair(sourceName, &mdata.tm)); } // Make sure there won't be 2 constructors after importing auto traitConstruct = importedTraitMethods.count("__construct"); auto traitName = importedTraitMethods.count(getScopeName()); auto classConstruct = m_functions.count("__construct"); auto className = m_functions.count(getScopeName()); if ((traitConstruct && traitName) || (traitConstruct && className) || (classConstruct && traitName)) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, "%s has colliding constructor definitions coming from traits", getScopeName().c_str() ); } for (auto const& traitPair : importedTraitsWithOrigName) { auto traitMethod = traitPair.second; MethodStatementPtr newMeth = importTraitMethod( *traitMethod, ar, traitMethod->originalName ); } // Import trait properties importTraitProperties(ar); m_traitStatus = FLATTENED; }
void ClassScope::importUsedTraits(AnalysisResultPtr ar) { // Trait flattening is supposed to happen only when we have awareness of // the whole program. assert(Option::WholeProgram); if (m_traitStatus == FLATTENED) return; if (m_traitStatus == BEING_FLATTENED) { getStmt()->analysisTimeFatal( Compiler::CyclicDependentTraits, "Cyclic dependency between traits involving %s", getOriginalName().c_str() ); return; } if (m_usedTraitNames.size() == 0) { m_traitStatus = FLATTENED; return; } m_traitStatus = BEING_FLATTENED; // First, make sure that parent classes have their traits imported if (!m_parent.empty()) { ClassScopePtr parent = ar->findClass(m_parent); if (parent) { parent->importUsedTraits(ar); } } if (isTrait()) { for (auto const& req : getTraitRequiredExtends()) { ClassScopePtr rCls = ar->findClass(req); if (!rCls || rCls->isFinal() || rCls->isInterface()) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, Strings::TRAIT_BAD_REQ_EXTENDS, m_originalName.c_str(), req.c_str(), req.c_str() ); } } for (auto const& req : getTraitRequiredImplements()) { ClassScopePtr rCls = ar->findClass(req); if (!rCls || !(rCls->isInterface())) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, Strings::TRAIT_BAD_REQ_IMPLEMENTS, m_originalName.c_str(), req.c_str(), req.c_str() ); } } } // Find trait methods to be imported for (unsigned i = 0; i < m_usedTraitNames.size(); i++) { ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]); if (!tCls || !(tCls->isTrait())) { setAttribute(UsesUnknownTrait); // XXX: is this useful ... for anything? getStmt()->analysisTimeFatal( Compiler::UnknownTrait, Strings::TRAITS_UNKNOWN_TRAIT, m_usedTraitNames[i].c_str() ); } // First, make sure the used trait is flattened tCls->importUsedTraits(ar); findTraitMethodsToImport(ar, tCls); // Import any interfaces implemented tCls->getInterfaces(ar, m_bases, false); } for (unsigned i = 0; i < m_usedTraitNames.size(); i++) { // Requirements must be checked in a separate loop because the // interfaces required by one trait may be implemented by another trait // whose "use" appears later in the class' scope ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]); importTraitRequirements(ar, tCls); } // Apply rules applyTraitRules(ar); // Remove trait abstract methods provided by other traits and duplicates removeSpareTraitAbstractMethods(ar); // Apply precedence of current class over used traits for (MethodToTraitListMap::iterator iter = m_importMethToTraitMap.begin(); iter != m_importMethToTraitMap.end(); ) { MethodToTraitListMap::iterator thisiter = iter; iter++; if (findFunction(ar, thisiter->first, 0, 0) != FunctionScopePtr()) { m_importMethToTraitMap.erase(thisiter); } } std::map<string, MethodStatementPtr> importedTraitMethods; std::vector<std::pair<string,const TraitMethod*>> importedTraitsWithOrigName; // Actually import the methods for (MethodToTraitListMap::const_iterator iter = m_importMethToTraitMap.begin(); iter != m_importMethToTraitMap.end(); iter++) { // The rules may rule out a method from all traits. // In this case, simply don't import the method. if (iter->second.size() == 0) { continue; } // Consistency checking: each name must only refer to one imported method if (iter->second.size() > 1) { getStmt()->analysisTimeFatal( Compiler::MethodInMultipleTraits, Strings::METHOD_IN_MULTIPLE_TRAITS, iter->first.c_str() ); } else { TraitMethodList::const_iterator traitMethIter = iter->second.begin(); if ((traitMethIter->m_modifiers ? traitMethIter->m_modifiers : traitMethIter->m_method->getModifiers())->isAbstract()) { // Skip abstract methods, if method already exists in the class if (findFunction(ar, iter->first, true) || importedTraitMethods.count(iter->first)) { continue; } } string sourceName = traitMethIter->m_ruleStmt ? toLower(((TraitAliasStatement*)traitMethIter->m_ruleStmt.get())-> getMethodName()) : iter->first; importedTraitMethods[sourceName] = MethodStatementPtr(); importedTraitsWithOrigName.push_back( make_pair(sourceName, &*traitMethIter)); } } // Make sure there won't be 2 constructors after importing auto traitConstruct = importedTraitMethods.count("__construct"); auto traitName = importedTraitMethods.count(getName()); auto classConstruct = m_functions.count("__construct"); auto className = m_functions.count(getName()); if ((traitConstruct && traitName) || (traitConstruct && className) || (classConstruct && traitName)) { getStmt()->analysisTimeFatal( Compiler::InvalidDerivation, "%s has colliding constructor definitions coming from traits", getOriginalName().c_str() ); } for (unsigned i = 0; i < importedTraitsWithOrigName.size(); i++) { const string &sourceName = importedTraitsWithOrigName[i].first; const TraitMethod *traitMethod = importedTraitsWithOrigName[i].second; MethodStatementPtr newMeth = importTraitMethod( *traitMethod, ar, toLower(traitMethod->m_originalName), importedTraitMethods); if (newMeth) { importedTraitMethods[sourceName] = newMeth; } } // Import trait properties importTraitProperties(ar); m_traitStatus = FLATTENED; }