diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 789de8f2df163..b2727cd5572fb 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -335,7 +335,7 @@ class alignas(1 << DeclAlignInBits) Decl { IsStatic : 1 ); - SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1+1+1, /// Encodes whether this is a 'let' binding. Introducer : 1, @@ -358,7 +358,10 @@ class alignas(1 << DeclAlignInBits) Decl { IsLazyStorageProperty : 1, /// Whether this is the backing storage for a property wrapper. - IsPropertyWrapperBackingProperty : 1 + IsPropertyWrapperBackingProperty : 1, + + /// Whether this is a lazily top-level global variable from the main file. + IsTopLevelGlobal : 1 ); SWIFT_INLINE_BITFIELD(ParamDecl, VarDecl, 1+2+NumDefaultArgumentKindBits, @@ -5084,6 +5087,10 @@ class VarDecl : public AbstractStorageDecl { Bits.VarDecl.IsLazyStorageProperty = IsLazyStorage; } + /// True if this is a top-level global variable from the main source file. + bool isTopLevelGlobal() const { return Bits.VarDecl.IsTopLevelGlobal; } + void setTopLevelGlobal(bool b) { Bits.VarDecl.IsTopLevelGlobal = b; } + /// Retrieve the custom attributes that attach property wrappers to this /// property. The returned list contains all of the attached property wrapper attributes in source order, /// which means the outermost wrapper attribute is provided first. diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index fef7af3b04473..ef604bed44967 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1138,14 +1138,12 @@ NOTE(candidate_expected_different_labels,none, "incorrect labels for candidate (have: '%0', expected: '%1')", (StringRef, StringRef)) +ERROR(member_shadows_function,none, + "use of %0 refers to %1 rather than %2 %3", + (DeclNameRef, DescriptiveDeclKind, DescriptiveDeclKind, DeclName)) ERROR(member_shadows_global_function,none, - "use of %0 refers to %1 %2 rather than %3 %4 in %5 %6", - (DeclNameRef, DescriptiveDeclKind, DeclName, DescriptiveDeclKind, - DeclName, DescriptiveDeclKind, DeclName)) -ERROR(member_shadows_global_function_near_match,none, - "use of %0 nearly matches %3 %4 in %5 %6 rather than %1 %2", - (DeclNameRef, DescriptiveDeclKind, DeclName, DescriptiveDeclKind, - DeclName, DescriptiveDeclKind, DeclName)) + "use of %0 refers to %1 rather than %2 %3 in module %4", + (DeclNameRef, DescriptiveDeclKind, DescriptiveDeclKind, DeclName, DeclName)) ERROR(instance_member_use_on_type,none, "instance member %1 cannot be used on type %0; " diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 25f90c3d45002..8bdb94abff1c9 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5312,6 +5312,7 @@ VarDecl::VarDecl(DeclKind kind, bool isStatic, VarDecl::Introducer introducer, Bits.VarDecl.IsLazyStorageProperty = false; Bits.VarDecl.HasNonPatternBindingInit = false; Bits.VarDecl.IsPropertyWrapperBackingProperty = false; + Bits.VarDecl.IsTopLevelGlobal = false; } Type VarDecl::getType() const { @@ -5410,11 +5411,7 @@ bool VarDecl::isLazilyInitializedGlobal() const { // Top-level global variables in the main source file and in the REPL are not // lazily initialized. - auto sourceFileContext = dyn_cast(getDeclContext()); - if (!sourceFileContext) - return true; - - return !sourceFileContext->isScriptMode(); + return !isTopLevelGlobal(); } SourceRange VarDecl::getSourceRange() const { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 15b3c1b33d293..6edbf335b8d78 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -5840,6 +5840,7 @@ Parser::parseDeclVar(ParseDeclOptions Flags, VD->setStatic(StaticLoc.isValid()); VD->getAttrs() = Attributes; setLocalDiscriminator(VD); + VD->setTopLevelGlobal(topLevelDecl); // Set original declaration in `@differentiable` attributes. setOriginalDeclarationForDifferentiableAttributes(Attributes, VD); diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 1910e242ce091..5e755aad3e72a 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -201,10 +201,6 @@ class FailureDiagnosis :public ASTVisitor{ ContextualTypePurpose CTP, Type suggestedType = Type()); - bool diagnoseImplicitSelfErrors(Expr *fnExpr, Expr *argExpr, - CalleeCandidateInfo &CCI, - ArrayRef argLabels); - private: /// Validate potential contextual type for type-checking one of the /// sub-expressions, usually correct/valid types are the ones which @@ -964,223 +960,6 @@ decomposeArgType(Type argType, ArrayRef argLabels) { return result; } -bool FailureDiagnosis::diagnoseImplicitSelfErrors( - Expr *fnExpr, Expr *argExpr, CalleeCandidateInfo &CCI, - ArrayRef argLabels) { - // If candidate list is empty it means that problem is somewhere else, - // since we need to have candidates which might be shadowing other funcs. - if (CCI.empty() || !CCI[0].getDecl()) - return false; - - auto &ctx = CS.getASTContext(); - // Call expression is formed as 'foo.bar' where 'foo' might be an - // implicit "Self" reference, such use wouldn't provide good diagnostics - // for situations where instance members have equal names to functions in - // Swift Standard Library e.g. min/max. - auto UDE = dyn_cast(fnExpr); - if (!UDE) - return false; - - auto baseExpr = dyn_cast(UDE->getBase()); - if (!baseExpr) - return false; - - auto baseDecl = baseExpr->getDecl(); - if (!baseExpr->isImplicit() || baseDecl->getFullName() != ctx.Id_self) - return false; - - // Our base expression is an implicit 'self.' reference e.g. - // - // extension Sequence { - // func test() -> Int { - // return max(1, 2) - // } - // } - // - // In this example the Sequence class already has two methods named 'max' - // none of which accept two arguments, but there is a function in - // Swift Standard Library called 'max' which does accept two arguments, - // so user might have called that by mistake without realizing that - // compiler would add implicit 'self.' prefix to the call of 'max'. - auto argType = CS.getType(argExpr); - // If argument wasn't properly type-checked, let's retry without changing AST. - if (!argType || argType->hasUnresolvedType() || argType->hasTypeVariable() || - argType->hasTypeParameter()) { - auto *argTuple = dyn_cast(argExpr); - if (!argTuple) { - // Bail out if we don't have a well-formed argument list. - return false; - } - - // Let's type check individual argument expressions without any - // contextual information to try to recover an argument type that - // matches what the user actually wrote instead of what the typechecker - // expects. - SmallVector elts; - for (unsigned i = 0, e = argTuple->getNumElements(); i < e; ++i) { - ConcreteDeclRef ref = nullptr; - auto *el = argTuple->getElement(i); - auto typeResult = - TypeChecker::getTypeOfExpressionWithoutApplying(el, CS.DC, ref); - if (!typeResult) - return false; - auto flags = ParameterTypeFlags().withInOut(typeResult->is()); - elts.push_back(TupleTypeElt(typeResult->getInOutObjectType(), - argTuple->getElementName(i), - flags)); - } - - argType = TupleType::get(elts, CS.getASTContext()); - } - - auto typeKind = argType->getKind(); - if (typeKind != TypeKind::Tuple && typeKind != TypeKind::Paren) - return false; - - // If argument type couldn't be properly resolved or has errors, - // we can't diagnose anything in here, it points to the different problem. - if (isUnresolvedOrTypeVarType(argType) || argType->hasError()) - return false; - - auto context = CS.DC; - using CandidateMap = - llvm::SmallDenseMap>; - - auto getBaseKind = [](ValueDecl *base) -> DescriptiveDeclKind { - DescriptiveDeclKind kind = DescriptiveDeclKind::Module; - if (!base) - return kind; - - auto context = base->getDeclContext(); - do { - if (isa(context)) - return DescriptiveDeclKind::Extension; - - if (auto nominal = dyn_cast(context)) { - kind = nominal->getDescriptiveKind(); - break; - } - - context = context->getParent(); - } while (context); - - return kind; - }; - - auto diagnoseShadowing = [&](ValueDecl *base, - ArrayRef candidates) -> bool { - CalleeCandidateInfo calleeInfo(base ? base->getInterfaceType() : nullptr, - candidates, CCI.hasTrailingClosure, CS, - base); - - calleeInfo.filterListArgs(decomposeArgType(argType, argLabels)); - - auto diagnostic = diag::member_shadows_global_function_near_match; - switch (calleeInfo.closeness) { - case CC_Unavailable: - case CC_Inaccessible: - case CC_SelfMismatch: - case CC_ArgumentLabelMismatch: - case CC_ArgumentCountMismatch: - case CC_GeneralMismatch: - return false; - - case CC_NonLValueInOut: - case CC_OneArgumentNearMismatch: - case CC_OneArgumentMismatch: - case CC_OneGenericArgumentNearMismatch: - case CC_OneGenericArgumentMismatch: - case CC_ArgumentNearMismatch: - case CC_ArgumentMismatch: - case CC_GenericNonsubstitutableMismatch: - break; // Near match cases - - case CC_ExactMatch: - diagnostic = diag::member_shadows_global_function; - break; - } - - auto choice = calleeInfo.candidates[0].getDecl(); - auto baseKind = getBaseKind(base); - auto baseName = getBaseName(choice->getDeclContext()); - - auto origCandidate = CCI[0].getDecl(); - ctx.Diags.diagnose(UDE->getLoc(), diagnostic, UDE->getName(), - origCandidate->getDescriptiveKind(), - origCandidate->getFullName(), - choice->getDescriptiveKind(), - choice->getFullName(), baseKind, baseName); - - auto topLevelDiag = diag::fix_unqualified_access_top_level; - if (baseKind == DescriptiveDeclKind::Module) - topLevelDiag = diag::fix_unqualified_access_top_level_multi; - - emitFixItForExplicitlyQualifiedReference(ctx.Diags, UDE, topLevelDiag, - baseName, - choice->getDescriptiveKind()); - - for (auto &candidate : calleeInfo.candidates) { - if (auto decl = candidate.getDecl()) - ctx.Diags.diagnose(decl, diag::decl_declared_here, decl->getFullName()); - } - - return true; - }; - - // For each of the parent contexts, let's try to find any candidates - // which have the same name and the same number of arguments as callee. - while (context->getParent()) { - auto result = - TypeChecker::lookupUnqualified(context, UDE->getName(), UDE->getLoc()); - context = context->getParent(); - - if (!result || result.empty()) - continue; - - CandidateMap candidates; - for (const auto &candidate : result) { - auto base = candidate.getBaseDecl(); - auto decl = candidate.getValueDecl(); - if ((base && base->isInvalid()) || decl->isInvalid()) - continue; - - // If base is present but it doesn't represent a valid nominal, - // we can't use current candidate as one of the choices. - if (base && !base->getInterfaceType()->getNominalOrBoundGenericNominal()) - continue; - - auto context = decl->getDeclContext(); - // We are only interested in static or global functions, because - // there is no way to call anything else properly. - if (!decl->isStatic() && !context->isModuleScopeContext()) - continue; - - OverloadChoice choice(base ? base->getInterfaceType() : nullptr, - decl, UDE->getFunctionRefKind()); - - if (base) { // Let's group all of the candidates have a common base. - candidates[base].push_back(choice); - continue; - } - - // If there is no base, it means this is one of the global functions, - // let's try to diagnose its shadowing inline. - if (diagnoseShadowing(base, choice)) - return true; - } - - if (candidates.empty()) - continue; - - for (const auto &candidate : candidates) { - if (diagnoseShadowing(candidate.getFirst(), candidate.getSecond())) - return true; - } - } - - return false; -} - // Extract expression for failed argument number static Expr *getFailedArgumentExpr(CalleeCandidateInfo CCI, Expr *argExpr) { if (auto *TE = dyn_cast(argExpr)) @@ -1202,10 +981,6 @@ static Expr *getFailedArgumentExpr(CalleeCandidateInfo CCI, Expr *argExpr) { bool FailureDiagnosis::diagnoseParameterErrors(CalleeCandidateInfo &CCI, Expr *fnExpr, Expr *argExpr, ArrayRef argLabels) { - // Try to diagnose errors related to the use of implicit self reference. - if (diagnoseImplicitSelfErrors(fnExpr, argExpr, CCI, argLabels)) - return true; - // If we have a failure where the candidate set differs on exactly one // argument, and where we have a consistent mismatch across the candidate set // (often because there is only one candidate in the set), then diagnose this diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index b8cf4a95a75bb..e24c0cae87bab 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -6117,3 +6117,45 @@ bool UnableToInferProtocolLiteralType::diagnoseAsError() { return true; } + +bool MissingQuialifierInMemberRefFailure::diagnoseAsError() { + auto selectedOverload = getOverloadChoiceIfAvailable(getLocator()); + if (!selectedOverload) + return false; + + auto *UDE = cast(getRawAnchor()); + + auto baseType = getType(UDE->getBase()); + + auto methodKind = baseType->isAnyExistentialType() + ? DescriptiveDeclKind::StaticMethod + : DescriptiveDeclKind::Method; + + auto choice = selectedOverload->choice.getDeclOrNull(); + if (!choice) + return false; + + auto *DC = choice->getDeclContext(); + if (!(DC->isModuleContext() || DC->isModuleScopeContext())) { + emitDiagnostic(UDE->getLoc(), diag::member_shadows_function, UDE->getName(), + methodKind, choice->getDescriptiveKind(), + choice->getFullName()); + return true; + } + + auto qualifier = DC->getParentModule()->getName(); + + emitDiagnostic(UDE->getLoc(), diag::member_shadows_global_function, + UDE->getName(), methodKind, choice->getDescriptiveKind(), + choice->getFullName(), qualifier); + + SmallString<32> namePlusDot = qualifier.str(); + namePlusDot.push_back('.'); + + emitDiagnostic(UDE->getLoc(), diag::fix_unqualified_access_top_level_multi, + namePlusDot, choice->getDescriptiveKind(), qualifier) + .fixItInsert(UDE->getStartLoc(), namePlusDot); + + emitDiagnostic(choice, diag::decl_declared_here, choice->getFullName()); + return true; +} diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 90f64c5175acf..5053f227cc08e 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1936,6 +1936,29 @@ class UnableToInferProtocolLiteralType final : public FailureDiagnostic { bool diagnoseAsError(); }; +/// Diagnose an attempt to reference a top-level name shadowed by a local +/// member e.g. +/// +/// ```swift +/// extension Sequence { +/// func test() -> Int { +/// return max(1, 2) +/// } +/// } +/// ``` +/// +/// Here `max` refers to a global function `max(_: T, _: T)` in `Swift` +/// module and can only be accessed by adding `Swift.` to it, because `Sequence` +/// has a member named `max` which accepts a single argument. +class MissingQuialifierInMemberRefFailure final : public FailureDiagnostic { +public: + MissingQuialifierInMemberRefFailure(ConstraintSystem &cs, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator) {} + + bool diagnoseAsError(); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index ffc4e502509a5..5bc424e4e9adb 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1180,3 +1180,15 @@ SpecifyObjectLiteralTypeImport::create(ConstraintSystem &cs, ConstraintLocator *locator) { return new (cs.getAllocator()) SpecifyObjectLiteralTypeImport(cs, locator); } + +bool AddQualifierToAccessTopLevelName::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + MissingQuialifierInMemberRefFailure failure(cs, getLocator()); + return failure.diagnose(asNote); +} + +AddQualifierToAccessTopLevelName * +AddQualifierToAccessTopLevelName::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) AddQualifierToAccessTopLevelName(cs, locator); +} diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 05959c8ddbaf4..f60c8b8859281 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -235,12 +235,15 @@ enum class FixKind : uint8_t { /// Closure return type has to be explicitly specified because it can't be /// inferred in current context e.g. because it's a multi-statement closure. SpecifyClosureReturnType, - + /// Object literal type coudn't be infered because the module where /// the default type that implements the associated literal protocol /// is declared was not imported. SpecifyObjectLiteralTypeImport, + /// Member shadows a top-level name, such a name could only be accessed by + /// prefixing it with a module name. + AddQualifierToAccessTopLevelName, }; class ConstraintFix { @@ -1652,7 +1655,22 @@ class SpecifyObjectLiteralTypeImport final : public ConstraintFix { static SpecifyObjectLiteralTypeImport *create(ConstraintSystem &cs, ConstraintLocator *locator); +}; + +class AddQualifierToAccessTopLevelName final : public ConstraintFix { + AddQualifierToAccessTopLevelName(ConstraintSystem &cs, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::AddQualifierToAccessTopLevelName, locator) {} + +public: + std::string getName() const { + return "qualify reference to access top-level function"; + } + bool diagnose(bool asNote = false) const; + + static AddQualifierToAccessTopLevelName *create(ConstraintSystem &cs, + ConstraintLocator *locator); }; } // end namespace constraints diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 43a00626b0e8d..570d72d33b9da 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3265,6 +3265,30 @@ bool ConstraintSystem::repairFailures( locator)) break; + { + auto *calleeLocator = getCalleeLocator(loc); + if (hasFixFor(calleeLocator, FixKind::AddQualifierToAccessTopLevelName)) { + if (auto overload = findSelectedOverloadFor(calleeLocator)) { + if (auto choice = overload->choice.getDeclOrNull()) { + // If this is an argument of a symetric function/operator let's + // not fix any position rather than first because we'd just end + // up with ambiguity instead of reporting an actual problem with + // mismatched type since each argument can have district bindings. + if (auto *AFD = dyn_cast(choice)) { + auto *paramList = AFD->getParameters(); + auto firstParamType = paramList->get(0)->getInterfaceType(); + if (elt.castTo().getParamIdx() > + 0 && + llvm::all_of(*paramList, [&](const ParamDecl *param) -> bool { + return param->getInterfaceType()->isEqual(firstParamType); + })) + return true; + } + } + } + } + } + conversionsOrFixes.push_back( AllowArgumentMismatch::create(*this, lhs, rhs, loc)); break; @@ -6188,8 +6212,27 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( if (candidates.size() == 1) candidates.front()->setFavored(); - generateConstraints(candidates, memberTy, outerAlternatives, - useDC, locator); + // We *might* include any non-members that we found in outer contexts in + // some special cases, for backwards compatibility: first, we have to be + // looking for one of the special names ('min' or 'max'), and second, all + // of the inner (viable) results need to come from conditional + // conformances. The second condition is how the problem here was + // encountered: a type ('Range') was made to conditionally conform to a + // new protocol ('Sequence'), which introduced some extra methods + // ('min' and 'max') that shadowed global functions that people regularly + // called within extensions to that type (usually adding 'clamp'). + bool treatAsViable = + (member.isSimpleName("min") || member.isSimpleName("max")) && + allFromConditionalConformances(DC, baseTy, result.ViableCandidates); + + generateConstraints( + candidates, memberTy, outerAlternatives, useDC, locator, None, + /*requiresFix=*/!treatAsViable, + [&](unsigned, const OverloadChoice &) { + return treatAsViable ? nullptr + : AddQualifierToAccessTopLevelName::create( + *this, locator); + }); } } @@ -8838,6 +8881,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::AllowTupleSplatForSingleParameter: case FixKind::AllowInvalidUseOfTrailingClosure: case FixKind::SpecifyClosureReturnType: + case FixKind::AddQualifierToAccessTopLevelName: llvm_unreachable("handled elsewhere"); } @@ -9301,7 +9345,12 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { case ConstraintKind::BindOverload: if (auto *fix = constraint.getFix()) { - if (recordFix(fix)) + // TODO(diagnostics): Impact should be associated with a fix unless + // it's a contextual problem, then only solver can decide what the impact + // would be in each particular situation. + auto impact = + fix->getKind() == FixKind::AddQualifierToAccessTopLevelName ? 10 : 1; + if (recordFix(fix, impact)) return SolutionKind::Error; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index de6d54af3adce..7e5b6714d6de0 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -452,20 +452,6 @@ static bool findNonMembers(ArrayRef lookupResults, return AllDeclRefs; } -/// Whether we should be looking at the outer results for a function called \c -/// name. -/// -/// This is very restrictive because it's a source compatibility issue (see the -/// if (AllConditionalConformances) { (void)findNonMembers(...); } below). -static bool shouldConsiderOuterResultsFor(DeclNameRef name) { - const StringRef specialNames[] = {"min", "max"}; - for (auto specialName : specialNames) - if (name.isSimpleName(specialName)) - return true; - - return false; -} - /// Bind an UnresolvedDeclRefExpr by performing name lookup and /// returning the resultant expression. Context is the DeclContext used /// for the lookup. @@ -479,8 +465,11 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; if (isa(DC)) lookupOptions |= NameLookupFlags::KnownPrivate; - if (shouldConsiderOuterResultsFor(Name)) - lookupOptions |= NameLookupFlags::IncludeOuterResults; + + // TODO: Include all of the possible members to give a solver a + // chance to diagnose name shadowing which requires explicit + // name/module qualifier to access top-level name. + lookupOptions |= NameLookupFlags::IncludeOuterResults; auto Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions); @@ -625,14 +614,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, // better matching candidates. if (localDeclAfterUse) { auto innerDecl = localDeclAfterUse; - - // Perform a thorough lookup if outer results was not included before. - if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) { - auto option = lookupOptions; - option |= NameLookupFlags::IncludeOuterResults; - Lookup = lookupUnqualified(DC, Name, Loc, option); - } - while (localDeclAfterUse) { if (Lookup.outerResults().empty()) { Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name); @@ -649,13 +630,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), /*breakOnMember=*/true, ResultValues, isValid); } - - // Drop outer results if they are not supposed to be included. - if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) { - Lookup.filter([&](LookupResultEntry Result, bool isOuter) { - return !isOuter; - }); - } } // If we have an unambiguous reference to a type decl, form a TypeExpr. @@ -715,7 +689,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, ResultValues.clear(); bool AllMemberRefs = true; - bool AllConditionalConformances = true; ValueDecl *Base = nullptr; DeclContext *BaseDC = nullptr; for (auto Result : Lookup) { @@ -732,26 +705,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, Base = ThisBase; BaseDC = Result.getDeclContext(); - - // Check if this result is derived through a conditional conformance, - // meaning it comes from a protocol (or extension) where there's a - // conditional conformance for the type with the method in question - // (NB. that type may not be the type associated with DC, for tested types - // with static methods). - if (auto Proto = Value->getDeclContext()->getSelfProtocolDecl()) { - auto contextSelfType = - BaseDC->getInnermostTypeContext()->getDeclaredInterfaceType(); - auto conformance = conformsToProtocol( - contextSelfType, Proto, DC, - ConformanceCheckFlags::InExpression | - ConformanceCheckFlags::SkipConditionalRequirements); - - if (conformance.isInvalid() || - conformance.getConditionalRequirements().empty()) { - AllConditionalConformances = false; - } - } - continue; } @@ -774,22 +727,12 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, /*Implicit=*/true); } - // We *might* include any non-members that we found in outer contexts in - // some special cases, for backwards compatibility: first, we have to be - // looking for one of the special names - // ('shouldConsiderOuterResultsFor(Name)'), and second, all of the inner - // results need to come from conditional conformances. The second condition - // is how the problem here was encountered: a type ('Range') was made to - // conditionally conform to a new protocol ('Sequence'), which introduced - // some extra methods ('min' and 'max') that shadowed global functions that - // people regularly called within extensions to that type (usually adding - // 'clamp'). llvm::SmallVector outerAlternatives; - if (AllConditionalConformances) { - (void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(), - /*breakOnMember=*/false, outerAlternatives, - /*isValid=*/[&](ValueDecl *) { return true; }); - } + (void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(), + /*breakOnMember=*/false, outerAlternatives, + /*isValid=*/[](ValueDecl *choice) -> bool { + return !choice->isInvalid(); + }); // Otherwise, form an UnresolvedDotExpr and sema will resolve it based on // type information. diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 7ad524f45df3f..707d454122c5e 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -195,6 +195,12 @@ namespace { addResult(found); return; } + } else if (isa(found)) { + // Declaring nested types inside other types is currently + // not supported by lookup would still return such members + // so we have to account for that here as well. + addResult(found); + return; } // FIXME: the "isa()" check will be wrong for diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 4c62bb3728b1a..e7ae80813ba74 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2657,6 +2657,7 @@ class DeclDeserializer { uint8_t rawIntroducer; bool isGetterMutating, isSetterMutating; bool isLazyStorageProperty; + bool isTopLevelGlobal; DeclID lazyStorageID; unsigned numAccessors, numBackingProperties; uint8_t readImpl, writeImpl, readWriteImpl, opaqueReadOwnership; @@ -2673,6 +2674,7 @@ class DeclDeserializer { hasNonPatternBindingInit, isGetterMutating, isSetterMutating, isLazyStorageProperty, + isTopLevelGlobal, lazyStorageID, opaqueReadOwnership, readImpl, writeImpl, readWriteImpl, @@ -2804,6 +2806,7 @@ class DeclDeserializer { } var->setLazyStorageProperty(isLazyStorageProperty); + var->setTopLevelGlobal(isTopLevelGlobal); // If there are any backing properties, record them. if (numBackingProperties > 0) { diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 265ea709a7c89..9c32d255477b4 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -1191,6 +1191,7 @@ namespace decls_block { BCFixed<1>, // is getter mutating? BCFixed<1>, // is setter mutating? BCFixed<1>, // is this the backing storage for a lazy property? + BCFixed<1>, // top level global? DeclIDField, // if this is a lazy property, this is the backing storage OpaqueReadOwnershipField, // opaque read ownership ReadImplKindField, // read implementation diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index ce49f72c4bccc..18abc58342f53 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3255,6 +3255,7 @@ class Serializer::DeclSerializer : public DeclVisitor { var->isGetterMutating(), var->isSetterMutating(), var->isLazyStorageProperty(), + var->isTopLevelGlobal(), S.addDeclRef(lazyStorage), accessors.OpaqueReadOwnership, accessors.ReadImpl, diff --git a/test/APINotes/versioned-objc.swift b/test/APINotes/versioned-objc.swift index b2b5cc76ea6c9..9f9b2b64611cd 100644 --- a/test/APINotes/versioned-objc.swift +++ b/test/APINotes/versioned-objc.swift @@ -166,7 +166,7 @@ extension PrintingInterference { func testDroppingRenamedPrints() { // CHECK-DIAGS-4: [[@LINE+1]]:{{[0-9]+}}: warning: use of 'print' treated as a reference to instance method print(self) - // CHECK-DIAGS-5: [[@LINE-1]]:{{[0-9]+}}: error: missing argument for parameter 'extra' in call + // CHECK-DIAGS-5: [[@LINE-1]]:{{[0-9]+}}: error: use of 'print' refers to instance method rather than global function 'print(_:separator:terminator:)' in module 'Swift' // CHECK-DIAGS-4-NOT: [[@LINE+1]]:{{[0-9]+}}: print(self, extra: self) diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index c2de930b8b443..1720948609d3c 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -355,7 +355,7 @@ do { // rdar://problem/25341015 extension Sequence { func r25341015_1() -> Int { - return max(1, 2) // expected-error {{use of 'max' refers to instance method 'max(by:)' rather than global function 'max' in module 'Swift'}} expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} + return max(1, 2) // expected-error {{use of 'max' refers to instance method rather than global function 'max' in module 'Swift'}} expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} } } @@ -381,7 +381,7 @@ func r25341015() { class Bar { func baz() {} func qux() { - baz(1, 2) // expected-error {{argument passed to call that takes no arguments}} + baz(1, 2) // expected-error {{use of 'baz' refers to instance method rather than local function 'baz'}} } } } @@ -405,17 +405,17 @@ func bar_32854314() -> Int { extension Array where Element == Int { func foo() { let _ = min(foo_32854314(), bar_32854314()) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } func foo(_ x: Int, _ y: Double) { let _ = min(x, y) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } func bar() { let _ = min(1.0, 2) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } } diff --git a/test/DebugInfo/inlinescopes.swift b/test/DebugInfo/inlinescopes.swift index 769b8a38ce8f0..6c4a3a4fefc3d 100644 --- a/test/DebugInfo/inlinescopes.swift +++ b/test/DebugInfo/inlinescopes.swift @@ -6,7 +6,7 @@ // RUN: %FileCheck %s -check-prefix=TRANSPARENT-CHECK < %t.ll // CHECK: define{{( dllexport)?}}{{( protected)?( signext)?}} i32 @main{{.*}} -// CHECK: call swiftcc i64 @"$s4main8noinlineys5Int64VADF"(i64 %4), !dbg ![[CALL:.*]] +// CHECK: call swiftcc i64 @"$s4main8noinlineys5Int64VADF"(i64 %{{.*}}), !dbg ![[CALL:.*]] // CHECK-DAG: ![[TOPLEVEL:.*]] = !DIFile(filename: "{{.*}}inlinescopes.swift" import FooBar diff --git a/test/NameBinding/name_lookup_min_max_conditional_conformance.swift b/test/NameBinding/name_lookup_min_max_conditional_conformance.swift index 81d2e59582cfa..e79212e6aa318 100644 --- a/test/NameBinding/name_lookup_min_max_conditional_conformance.swift +++ b/test/NameBinding/name_lookup_min_max_conditional_conformance.swift @@ -16,7 +16,7 @@ extension ContainsMinMax { func min() {} } -func foo(_: Int, _: Int) {} +func foo(_: Int, _: Int) {} // expected-note 2 {{'foo' declared here}} protocol ContainsFoo {} extension ContainsFoo { @@ -34,15 +34,14 @@ extension NonConditional { // expected-error@-1{{use of 'min' refers to instance method}} // expected-note@-2{{use 'Swift.' to reference the global function}} - // FIXME(diagnostics): Better diagnostic in this case would be to suggest to add `name_lookup_min_max_conditional_conformance.` - // to call because name `foo` is shadowed by instance method without arguments. Would be fixed by `resolveDeclRefExpr` refactoring. - _ = foo(5, 6) // expected-error {{argument passed to call that takes no arguments}} + _ = foo(5, 6) // expected-error {{use of 'foo' refers to instance method rather than global function 'foo' in module 'name_lookup_min_max_conditional_conformance'}} + // expected-note@-1 {{use 'name_lookup_min_max_conditional_conformance.' to reference the global function in module 'name_lookup_min_max_conditional_conformance'}} {{13-13=name_lookup_min_max_conditional_conformance.}} } } struct Conditional {} extension Conditional: ContainsMinMax where T: ContainsMinMax {} -extension Conditional: ContainsFoo where T: ContainsFoo {} // expected-note {{requirement from conditional conformance of 'Conditional' to 'ContainsFoo'}} +extension Conditional: ContainsFoo where T: ContainsFoo {} extension Conditional { func f() { @@ -53,9 +52,8 @@ extension Conditional { // expected-warning@-1{{use of 'min' as reference to global function in module 'Swift' will change in future versions of Swift to reference instance method in generic struct 'Conditional' which comes via a conditional conformance}} // expected-note@-2{{use 'Swift.' to continue to reference the global function}} - // FIXME(diagnostics): Same as line 39, there should be only one error here about shadowing. _ = foo(5, 6) - // expected-error@-1 {{referencing instance method 'foo()' on 'Conditional' requires that 'T' conform to 'ContainsFoo'}} - // expected-error@-2 {{argument passed to call that takes no arguments}} + // expected-error@-1 {{use of 'foo' refers to instance method rather than global function 'foo' in module 'name_lookup_min_max_conditional_conformance'}} + // expected-note@-2 {{use 'name_lookup_min_max_conditional_conformance.' to reference the global function in module 'name_lookup_min_max_conditional_conformance'}} {{13-13=name_lookup_min_max_conditional_conformance.}} } } diff --git a/test/SILGen/expressions.swift b/test/SILGen/expressions.swift index f642702602900..54654a6ee7b29 100644 --- a/test/SILGen/expressions.swift +++ b/test/SILGen/expressions.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: echo "public var x = Int()" | %target-swift-frontend -module-name FooBar -emit-module -o %t - +// RUN: echo "public var x = Int()" | %target-swift-frontend -parse-as-library -module-name FooBar -emit-module -o %t - // RUN: %target-swift-emit-silgen -parse-stdlib -module-name expressions %s -I%t -disable-access-control | %FileCheck %s import Swift diff --git a/test/SILGen/global_init_attribute.swift b/test/SILGen/global_init_attribute.swift index f47bde0192a73..95167c7127078 100644 --- a/test/SILGen/global_init_attribute.swift +++ b/test/SILGen/global_init_attribute.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-module -o %t %S/Inputs/def_global.swift +// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -parse-as-library -emit-module -o %t %S/Inputs/def_global.swift // RUN: %target-swift-emit-silgen -Xllvm -sil-full-demangle -parse-as-library -I %t %s | %FileCheck %s // // Test that SILGen uses the "global_init" attribute for all global diff --git a/test/SILGen/global_resilience.swift b/test/SILGen/global_resilience.swift index c05720d0713eb..78b425a9ed684 100644 --- a/test/SILGen/global_resilience.swift +++ b/test/SILGen/global_resilience.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift +// RUN: %target-swift-frontend -emit-module -parse-as-library -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift // RUN: %target-swift-emit-silgen -I %t -enable-library-evolution -parse-as-library %s | %FileCheck %s // RUN: %target-swift-emit-sil -I %t -O -enable-library-evolution -parse-as-library %s | %FileCheck --check-prefix=CHECK-OPT %s diff --git a/test/Sema/circular_decl_checking.swift b/test/Sema/circular_decl_checking.swift index acb78f0520803..3e8d6e2a9a542 100644 --- a/test/Sema/circular_decl_checking.swift +++ b/test/Sema/circular_decl_checking.swift @@ -23,9 +23,10 @@ class HasGenericFunc { } } -class HasProp { +class HasProp { // expected-note {{'HasProp' declared here}} var HasProp: HasProp { - return HasProp() // expected-error {{cannot call value of non-function type 'HasProp'}}{{19-21=}} + return HasProp() // expected-error {{use of 'HasProp' refers to instance method rather than class 'HasProp' in module 'circular_decl_checking'}} + // expected-note@-1 {{use 'circular_decl_checking.' to reference the class in module 'circular_decl_checking'}} {{12-12=circular_decl_checking.}} } var SomethingElse: SomethingElse? { // expected-error {{use of undeclared type 'SomethingElse'}} return nil diff --git a/test/Serialization/Recovery/typedefs.swift b/test/Serialization/Recovery/typedefs.swift index a9fab839e045b..60d0eda010d56 100644 --- a/test/Serialization/Recovery/typedefs.swift +++ b/test/Serialization/Recovery/typedefs.swift @@ -1,4 +1,7 @@ // RUN: %empty-directory(%t) + +// Cannot use -parse-as-library here because that would compile also the +// #if VERIFY path, which contains top-level code. // RUN: %target-swift-frontend -emit-sil -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules -disable-objc-attr-requires-foundation-module -enable-objc-interop %s | %FileCheck -check-prefix CHECK-VTABLE %s // RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s @@ -23,9 +26,9 @@ import Lib // CHECK-SIL-LABEL: sil hidden [ossa] @$s8typedefs11testSymbolsyyF func testSymbols() { // Check that the symbols are not using 'Bool'. - // CHECK-SIL: function_ref @$s3Lib1xs5Int32Vvau + // CHECK-SIL: global_addr @$s3Lib9usesAssocs5Int32VSgvp _ = Lib.x - // CHECK-SIL: function_ref @$s3Lib9usesAssocs5Int32VSgvau + // CHECK-SIL: global_addr @$s3Lib1xs5Int32Vvp _ = Lib.usesAssoc } // CHECK-SIL: end sil function '$s8typedefs11testSymbolsyyF' diff --git a/test/decl/nested/protocol.swift b/test/decl/nested/protocol.swift index ea5c973467b10..6d25970753ba7 100644 --- a/test/decl/nested/protocol.swift +++ b/test/decl/nested/protocol.swift @@ -19,7 +19,7 @@ class OuterGenericClass { } } -protocol OuterProtocol { // expected-note{{'OuterProtocol' declared here}} +protocol OuterProtocol { associatedtype Hen protocol InnerProtocol { // expected-error{{protocol 'InnerProtocol' cannot be nested inside another declaration}} associatedtype Rooster @@ -32,7 +32,7 @@ struct ConformsToOuterProtocol : OuterProtocol { typealias Hen = Int func f() { let _ = InnerProtocol.self } - // expected-error@-1 {{use of unresolved identifier 'InnerProtocol'}} + // expected-error@-1 {{protocol 'InnerProtocol' can only be used as a generic constraint because it has Self or associated type requirements}} } protocol Racoon {