Skip to content

Add syntax for fully qualifying names in shadowed modules #26949

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ TYPE_ATTR(autoclosure)
TYPE_ATTR(convention)
TYPE_ATTR(noescape)
TYPE_ATTR(escaping)
TYPE_ATTR(qualified)

// SIL-specific attributes
TYPE_ATTR(block_storage)
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/NameLookup.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ class UnqualifiedLookup {
/// This lookup should include results from outside the innermost scope with
/// results.
IncludeOuterResults = 0x10,
/// Look up only modules visible at this location, not other kinds of names.
IncludeOnlyModules = 0x20,
};
using Options = OptionSet<Flags>;

Expand Down
23 changes: 16 additions & 7 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1163,27 +1163,36 @@ class Parser {
using TypeResult = ParsedSyntaxResult<ParsedTypeSyntax>;
using TypeErrorResult = ParsedSyntaxResult<ParsedUnknownTypeSyntax>;

enum class ParseTypeFlags : uint8_t {
IgnoreCodeCompletion = 1 << 0,
IsSILFuncDecl = 1 << 1,
Minimal = 1 << 2
};
using ParseTypeOptions = OptionSet<ParseTypeFlags>;

LayoutConstraint parseLayoutConstraint(Identifier LayoutConstraintID);

TypeASTResult parseType();
TypeASTResult parseType(Diag<> MessageID, bool HandleCodeCompletion = true,
bool IsSILFuncDecl = false);
TypeASTResult parseType(ParseTypeOptions Flags = {});
TypeASTResult parseType(Diag<> MessageID,
ParseTypeOptions Flags = {});
ParserStatus
parseGenericArgumentsAST(llvm::SmallVectorImpl<TypeRepr *> &ArgsAST,
SourceLoc &LAngleLoc, SourceLoc &RAngleLoc);
TypeASTResult parseSILBoxType(GenericParamList *generics,
const TypeAttributes &attrs,
Optional<Scope> &GenericsScope);
TypeASTResult parseTypeSimpleOrCompositionAST(Diag<> MessageID,
bool HandleCodeCompletion);
ParseTypeOptions Flags = {});
TypeASTResult parseAnyTypeAST();

ParsedSyntaxResult<ParsedGenericArgumentClauseSyntax>
parseGenericArgumentClauseSyntax();

TypeResult parseTypeSimple(Diag<> MessageID, bool HandleCodeCompletion);
TypeResult parseTypeSimpleOrComposition(Diag<> MessageID, bool HandleCodeCompletion);
TypeResult parseTypeIdentifier();
TypeResult parseTypeSimple(Diag<> MessageID,
ParseTypeOptions Flags = {});
TypeResult parseTypeSimpleOrComposition(Diag<> MessageID,
ParseTypeOptions Flags = {});
TypeResult parseTypeIdentifier(ParseTypeOptions Flags = {});
TypeResult parseAnyType();
TypeResult parseTypeTupleBody();
TypeResult parseTypeCollection();
Expand Down
3 changes: 3 additions & 0 deletions lib/AST/TypeRepr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ void AttributedTypeRepr::printAttrs(ASTPrinter &Printer,
Printer.printStructurePost(PrintStructureKind::BuiltinAttribute);
Printer << " ";
}

if (hasAttr(TAK_qualified))
Printer.printSimpleAttr("@qualified") << " ";
}

IdentTypeRepr *IdentTypeRepr::create(ASTContext &C,
Expand Down
6 changes: 6 additions & 0 deletions lib/AST/UnqualifiedLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,12 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() {
#endif
SharedTimer timer("UnqualifiedLookupFactory performUnqualifiedLookup");

if (options.contains(UnqualifiedLookup::Flags::IncludeOnlyModules)) {
lookForAModuleWithTheGivenName(DC);
recordCompletionOfAScope();
return;
}

const Optional<bool> initialIsCascadingUse = getInitialIsCascadingUse();

ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUse{
Expand Down
29 changes: 24 additions & 5 deletions lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,18 +402,19 @@ ParserResult<Expr> Parser::parseExprSequenceElement(Diag<> message,
consumeToken();
}

// Try to parse '@' sign or 'inout' as a attributed typerepr.
if (Tok.isAny(tok::at_sign, tok::kw_inout)) {
// Try to parse 'inout' as a attributed typerepr.
if (Tok.isAny(tok::kw_inout)) {
bool isType = false;
{
BacktrackingScope backtrack(*this);
isType = canParseType();
}
if (isType) {
ParserResult<TypeRepr> ty = parseType();
if (ty.isNonNull())
return makeParserResult(
new (Context) TypeExpr(TypeLoc(ty.get(), Type())));
if (ty.isNonNull()) {
auto E = new (Context) TypeExpr(TypeLoc(ty.get(), Type()));
return makeParserResult(static_cast<Expr*>(E));
}
checkForInputIncomplete();
return nullptr;
}
Expand Down Expand Up @@ -1480,6 +1481,24 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {
SyntaxParsingContext ExprContext(SyntaxContext, SyntaxContextKind::Expr);
switch (Tok.getKind()) {
case tok::at_sign:
{
// Try to parse as an attributed type. This is mainly for @qualified.
bool isType = false;
{
BacktrackingScope backtrack(*this);
isType = canParseType();
}
if (isType) {
// Parse a TypeExpr, but make it the smallest we possibly can so we don't
// misinterpret references to decls as types instead.
ParserResult<TypeRepr> ty = parseType(ParseTypeFlags::Minimal);
if (ty.isNonNull()) {
auto E = new (Context) TypeExpr(TypeLoc(ty.get(), Type()));
return makeParserResult(static_cast<Expr*>(E));
}
}
}

// Objective-C programmers habitually type @"foo", so recover gracefully
// with a fixit. If this isn't @"foo", just handle it like an unknown
// input.
Expand Down
6 changes: 4 additions & 2 deletions lib/Parse/ParsePattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
}

if (isBareType && paramContext == ParameterContextKind::EnumElement) {
auto type = parseType(diag::expected_parameter_type, false);
auto type = parseType(diag::expected_parameter_type,
ParseTypeFlags::IgnoreCodeCompletion);
status |= type;
param.Type = type.getPtrOrNull();
param.FirstName = Identifier();
Expand All @@ -348,7 +349,8 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
// Otherwise, if this is a bare type, then the user forgot to name the
// parameter, e.g. "func foo(Int) {}"
SourceLoc typeStartLoc = Tok.getLoc();
auto type = parseType(diag::expected_parameter_type, false);
auto type = parseType(diag::expected_parameter_type,
ParseTypeFlags::IgnoreCodeCompletion);
status |= type;
param.Type = type.getPtrOrNull();

Expand Down
31 changes: 16 additions & 15 deletions lib/Parse/ParseType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ LayoutConstraint Parser::parseLayoutConstraint(Identifier LayoutConstraintID) {
/// type-collection
/// type-array
Parser::TypeResult Parser::parseTypeSimple(Diag<> MessageID,
bool HandleCodeCompletion) {
ParseTypeOptions Flags) {
if (Tok.is(tok::kw_inout) ||
(Tok.is(tok::identifier) && (Tok.getRawText().equals("__shared") ||
Tok.getRawText().equals("__owned")))) {
Expand All @@ -168,13 +168,13 @@ Parser::TypeResult Parser::parseTypeSimple(Diag<> MessageID,
case tok::kw_Self:
case tok::kw_Any:
case tok::identifier:
Result = parseTypeIdentifier();
Result = parseTypeIdentifier(Flags);
break;
case tok::l_paren:
Result = parseTypeTupleBody();
break;
case tok::code_complete: {
if (!HandleCodeCompletion)
if (Flags.contains(ParseTypeFlags::IgnoreCodeCompletion))
break;
if (CodeCompletion)
CodeCompletion->completeTypeSimpleBeginning();
Expand Down Expand Up @@ -240,8 +240,8 @@ Parser::TypeResult Parser::parseTypeSimple(Diag<> MessageID,
return *Result;
}

Parser::TypeASTResult Parser::parseType() {
return parseType(diag::expected_type);
Parser::TypeASTResult Parser::parseType(ParseTypeOptions Flags) {
return parseType(diag::expected_type, Flags);
}

Parser::TypeASTResult Parser::parseSILBoxType(GenericParamList *generics,
Expand Down Expand Up @@ -325,8 +325,7 @@ Parser::TypeASTResult Parser::parseSILBoxType(GenericParamList *generics,
/// type-composition 'throws'? '->' type
///
Parser::TypeASTResult Parser::parseType(Diag<> MessageID,
bool HandleCodeCompletion,
bool IsSILFuncDecl) {
ParseTypeOptions Flags) {
// Start a context for creating type syntax.
SyntaxParsingContext TypeParsingContext(SyntaxContext,
SyntaxContextKind::Type);
Expand All @@ -345,7 +344,7 @@ Parser::TypeASTResult Parser::parseType(Diag<> MessageID,
if (isInSILMode()) {
// If this is part of a sil function decl, generic parameters are visible in
// the function body; otherwise, they are visible when parsing the type.
if (!IsSILFuncDecl)
if (!Flags.contains(ParseTypeFlags::IsSILFuncDecl))
GenericsScope.emplace(this, ScopeKind::Generics);
generics = maybeParseGenericParams().getPtrOrNull();
}
Expand All @@ -360,7 +359,7 @@ Parser::TypeASTResult Parser::parseType(Diag<> MessageID,
auto RealTypeLoc = leadingTriviaLoc();

ParserResult<TypeRepr> ty =
parseTypeSimpleOrCompositionAST(MessageID, HandleCodeCompletion);
parseTypeSimpleOrCompositionAST(MessageID, Flags);
if (ty.hasCodeCompletion())
return makeParserCodeCompletionResult<TypeRepr>();
if (ty.isNull())
Expand Down Expand Up @@ -614,7 +613,7 @@ Parser::parseGenericArgumentsAST(SmallVectorImpl<TypeRepr *> &ArgsAST,
/// type-identifier:
/// identifier generic-args? ('.' identifier generic-args?)*
///
Parser::TypeResult Parser::parseTypeIdentifier() {
Parser::TypeResult Parser::parseTypeIdentifier(Parser::ParseTypeOptions Flags) {
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_Self)) {
// is this the 'Any' type
if (Tok.is(tok::kw_Any))
Expand Down Expand Up @@ -702,6 +701,8 @@ Parser::TypeResult Parser::parseTypeIdentifier() {
}
if (!peekToken().isContextualKeyword("Type") &&
!peekToken().isContextualKeyword("Protocol")) {
if (Flags.contains(ParseTypeFlags::Minimal))
break;
Period = consumeTokenSyntax();
continue;
}
Expand Down Expand Up @@ -747,11 +748,11 @@ Parser::TypeResult Parser::parseTypeIdentifier() {

Parser::TypeASTResult
Parser::parseTypeSimpleOrCompositionAST(Diag<> MessageID,
bool HandleCodeCompletion) {
ParseTypeOptions Flags) {
auto Loc = leadingTriviaLoc();

auto CompositionResult =
parseTypeSimpleOrComposition(MessageID, HandleCodeCompletion);
parseTypeSimpleOrComposition(MessageID, Flags);

if (!CompositionResult.isSuccess()) {
auto nodes = CompositionResult.getUnknownNodes();
Expand Down Expand Up @@ -786,7 +787,7 @@ Parser::parseTypeSimpleOrCompositionAST(Diag<> MessageID,
/// type-composition '&' type-simple
Parser::TypeResult
Parser::parseTypeSimpleOrComposition(Diag<> MessageID,
bool HandleCodeCompletion) {
ParseTypeOptions Flags) {
// Check for the opaque modifier.
// This is only semantically allowed in certain contexts, but we parse it
// generally for diagnostics and recovery.
Expand All @@ -803,7 +804,7 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID,
};

// Parse the first type
auto FirstTypeResult = parseTypeSimple(MessageID, HandleCodeCompletion);
auto FirstTypeResult = parseTypeSimple(MessageID, Flags);

// todo [gsoc]: handle Junk properly here
if (!FirstTypeResult.isSuccess())
Expand Down Expand Up @@ -834,7 +835,7 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID,
}

auto NextTypeResult = parseTypeSimple(diag::expected_identifier_for_type,
HandleCodeCompletion);
Flags);

if (!NextTypeResult.isSuccess()) {
auto following = NextTypeResult.getUnknownNodes();
Expand Down
7 changes: 4 additions & 3 deletions lib/ParseSIL/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1272,9 +1272,10 @@ bool SILParser::parseSILType(SILType &Result,
attrs.convention = "thin";
}

ParserResult<TypeRepr> TyR = P.parseType(diag::expected_sil_type,
/*handleCodeCompletion*/ true,
/*isSILFuncDecl*/ IsFuncDecl);
Parser::ParseTypeOptions Flags;
if (IsFuncDecl)
Flags |= Parser::ParseTypeFlags::IsSILFuncDecl;
ParserResult<TypeRepr> TyR = P.parseType(diag::expected_sil_type, Flags);

if (TyR.isNull())
return true;
Expand Down
39 changes: 39 additions & 0 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,10 @@ namespace {
/// The expressions that are direct arguments of call expressions.
llvm::SmallPtrSet<Expr *, 4> CallArgs;

/// Simplify TypeExprs which represent a fully-qualified module name only
/// into a DeclRefExpr.
DeclRefExpr *simplifyQualifiedModuleRef(Expr *TE);

/// Simplify expressions which are type sugar productions that got parsed
/// as expressions due to the parser not knowing which identifiers are
/// type names.
Expand Down Expand Up @@ -1329,6 +1333,11 @@ namespace {
return DAE;
}

// Turn qualified references to modules into DeclRefExprs.
if (auto DRE = simplifyQualifiedModuleRef(expr)) {
return DRE;
}

// If this is a sugared type that needs to be folded into a single
// TypeExpr, do it.
if (auto *simplified = simplifyTypeExpr(expr))
Expand Down Expand Up @@ -1397,6 +1406,36 @@ bool PreCheckExpression::walkToClosureExprPre(ClosureExpr *closure) {
return true;
}

static TypeRepr *skipParens(TypeRepr *TR) {
assert(TR);
while (isa<TupleTypeRepr>(TR) && cast<TupleTypeRepr>(TR)->isParenType())
TR = cast<TupleTypeRepr>(TR)->getElement(0).Type;
return TR;
}

DeclRefExpr *PreCheckExpression::simplifyQualifiedModuleRef(Expr *E) {
auto TE = dyn_cast<TypeExpr>(E);
if (!TE || !TE->getTypeRepr()) return nullptr;

TypeRepr * Repr = skipParens(TE->getTypeRepr());

auto ATR = dyn_cast<AttributedTypeRepr>(Repr);
if (!ATR || !ATR->getAttrs().has(TAK_qualified)) return nullptr;

Repr = skipParens(ATR->getTypeRepr());

auto SITR = dyn_cast<SimpleIdentTypeRepr>(Repr);
if (!SITR) return nullptr;

auto modules = TC.lookupUnqualified(DC, SITR->getIdentifier(), SITR->getLoc(),
NameLookupFlags::IncludeOnlyModules);
if (modules.size() != 1) return nullptr;

ConcreteDeclRef concreteRef(modules.front().getValueDecl());
return new (TC.Context) DeclRefExpr(concreteRef, DeclNameLoc(TE->getLoc()),
/*Implicit=*/false);
}

TypeExpr *PreCheckExpression::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) {
if (!UDE->getName().isSimpleName() ||
UDE->getName().isSpecial())
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/TypeCheckNameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ convertToUnqualifiedLookupOptions(NameLookupOptions options) {
newOptions |= UnqualifiedLookup::Flags::IgnoreAccessControl;
if (options.contains(NameLookupFlags::IncludeOuterResults))
newOptions |= UnqualifiedLookup::Flags::IncludeOuterResults;
if (options.contains(NameLookupFlags::IncludeOnlyModules))
newOptions |= UnqualifiedLookup::Flags::IncludeOnlyModules;

return newOptions;
}
Expand Down
10 changes: 10 additions & 0 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@ adjustOptionsForGenericArgs(TypeResolutionOptions options) {
options.setContext(None);
options -= TypeResolutionFlags::SILType;
options -= TypeResolutionFlags::AllowUnavailableProtocol;
options -= TypeResolutionFlags::FullyQualified;

return options;
}
Expand Down Expand Up @@ -1325,6 +1326,9 @@ resolveTopLevelIdentTypeComponent(TypeResolution resolution,
NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions;
if (options.contains(TypeResolutionFlags::KnownNonCascadingDependency))
lookupOptions |= NameLookupFlags::KnownPrivate;
if (options.contains(TypeResolutionFlags::FullyQualified))
lookupOptions |= NameLookupFlags::IncludeOnlyModules;

auto globals = TypeChecker::lookupUnqualifiedType(DC,
id,
comp->getIdLoc(),
Expand Down Expand Up @@ -2057,6 +2061,12 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
// The type we're working with, in case we want to build it differently
// based on the attributes we see.
Type ty;

if (attrs.has(TAK_qualified)) {
TypeResolutionOptions innerOptions = options;
innerOptions |= TypeResolutionFlags::FullyQualified;
return resolveType(repr, innerOptions);
}

// If this is a reference to an opaque return type, resolve it.
if (auto &opaque = attrs.OpaqueReturnTypeOf) {
Expand Down
Loading