Skip to content

[SR-11295] [Re-apply] Unnecessary Coercion explicitly same type coercion warning #27895

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
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
58be8eb
[Diagnostics] Adding diagnostic message for redundant coercions
LucianoPAlmeida Dec 16, 2019
9408302
[ConstraintLocator] Creating ExplicitTypeCoercion constraint locator
LucianoPAlmeida Dec 16, 2019
08c886e
[CSDiagnostics] Creating RemoveUnnecessaryCoercion Fix and Unnecessar…
LucianoPAlmeida Dec 16, 2019
428639f
[ConstraintSystem] Recording the type variables bindings and attempti…
LucianoPAlmeida Dec 16, 2019
cd2d6ce
[CSSolver] Clean up type variable bindings in end of solver scope
LucianoPAlmeida Dec 16, 2019
9e0ff0a
[Frontend] Creating compiler flag to disable the redundant coercion w…
LucianoPAlmeida Dec 16, 2019
b0e2928
[stdlib] Fixing remaining redundant coercions in stdlib
LucianoPAlmeida Dec 16, 2019
f8d4bd4
[tests] Adding disable flag to invocations under the tests/ClangImporter
LucianoPAlmeida Dec 16, 2019
aa9abfc
[tests] Fixing tests under test/Constraints
LucianoPAlmeida Dec 16, 2019
1f2e848
[tests] Fixing tests under test/Generics, test/Interpreter and test/s…
LucianoPAlmeida Dec 16, 2019
867e9bb
[tests] Fixing tests under test/Sema and test/decl
LucianoPAlmeida Dec 16, 2019
3740783
[tests] Fixing tests under test/type, test/expr and test/Parse
LucianoPAlmeida Dec 16, 2019
4e512a9
[CSFix] Getting the source locator from the representative
LucianoPAlmeida Dec 24, 2019
3c778a4
[test] Add explicit coercion test for tuple element expr/cast/as_coerce
LucianoPAlmeida Dec 26, 2019
3f39531
[CSFix] Skipping the RemoveUnnecessaryCoercion if representative type…
LucianoPAlmeida Dec 26, 2019
60ab6db
[tests] Adding function overload test cases
LucianoPAlmeida Dec 30, 2019
933075d
[CSFix] Add check to dont diagnose if has SubExpressionDiagnostics flag
LucianoPAlmeida Jan 2, 2020
4fc26b7
[Diagnostics] Improving logic and removing old checks on RemoveUnnece…
LucianoPAlmeida Jan 23, 2020
49ef402
[tests] Adding few more test cases for redundant coercion warning
LucianoPAlmeida Feb 5, 2020
d2d32a2
[CSFix] Remove CSDiag flag on RemoveUnnecessaryCoercion::attempt
LucianoPAlmeida Feb 23, 2020
f66405c
[Diagnostics] Adjust RemoveUnnecessaryCoercion and UnnecessaryCoercio…
LucianoPAlmeida Mar 20, 2020
2a85acb
[ConstraintLocator] Adjust isCoerce() to check for LocatorPathElt::Ex…
LucianoPAlmeida Mar 20, 2020
3225f62
[Diagnostics] Adjust to get expr from type node anchor
LucianoPAlmeida Apr 26, 2020
b22b1b8
[Sema] Fixing compilation errors caused by removed APIs
LucianoPAlmeida Aug 19, 2020
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
20 changes: 13 additions & 7 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -998,23 +998,29 @@ ERROR(super_initializer_not_in_initializer,none,
WARNING(isa_is_always_true,none, "'%0' test is always true",
(StringRef))
WARNING(isa_is_foreign_check,none,
"'is' test is always true because %0 is a Core Foundation type",
(Type))
"'is' test is always true because %0 is a Core Foundation type",
(Type))
WARNING(conditional_downcast_coercion,none,
"conditional cast from %0 to %1 always succeeds",
(Type, Type))
"conditional cast from %0 to %1 always succeeds",
(Type, Type))

WARNING(literal_conditional_downcast_to_coercion,none,
"conditional downcast from literal to %0 always fails; "
"consider using 'as' coercion",
(Type))

WARNING(unnecessary_same_type_coercion,none,
"redundant cast to %0 has no effect",
(Type))
WARNING(unnecessary_same_typealias_type_coercion,none,
"redundant cast from %0 to %1 has no effect",
(Type, Type))

WARNING(forced_downcast_noop,none,
"forced cast of %0 to same type has no effect", (Type))

WARNING(forced_downcast_coercion,none,
"forced cast from %0 to %1 always succeeds; did you mean to use 'as'?",
(Type, Type))
"forced cast from %0 to %1 always succeeds; did you mean to use 'as'?",
(Type, Type))

// Note: the Boolean at the end indicates whether bridging is required after
// the cast.
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,10 @@ namespace swift {
/// If set to true, the diagnosis engine can assume the emitted diagnostics
/// will be used in editor. This usually leads to more aggressive fixit.
bool DiagnosticsEditorMode = false;

/// If set to true, disable redundant coercions e.g. Double(1) as Double
/// warning diagnostics.
bool DisableRedundantCoercionWarning = false;

/// Whether to enable Swift 3 @objc inference, e.g., for members of
/// Objective-C-derived classes and 'dynamic' members.
Expand Down
13 changes: 8 additions & 5 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ def prebuilt_module_cache_path_EQ :
Alias<prebuilt_module_cache_path>;

def force_public_linkage : Flag<["-"], "force-public-linkage">,
HelpText<"Force public linkage for private symbols. Used by LLDB.">;
HelpText<"Force public linkage for private symbols. Used by LLDB.">;

def dump_api_path : Separate<["-"], "dump-api-path">,
HelpText<"The path to output swift interface files for the compiled source files">;
Expand All @@ -676,14 +676,17 @@ def group_info_path : Separate<["-"], "group-info-path">,
HelpText<"The path to collect the group information of the compiled module">;

def diagnostics_editor_mode : Flag<["-"], "diagnostics-editor-mode">,
HelpText<"Diagnostics will be used in editor">;
HelpText<"Diagnostics will be used in editor">;

def disable_redundant_coercion_warning : Flag<["-"], "disable-redundant-coercion-warning">,
HelpText<"Disable redundant coercion warning diagnostic">;

def validate_tbd_against_ir_EQ: Joined<["-"], "validate-tbd-against-ir=">,
HelpText<"Compare the symbols in the IR against the TBD file that would be generated.">,
MetaVarName<"<level>">;
HelpText<"Compare the symbols in the IR against the TBD file that would be generated.">,
MetaVarName<"<level>">;

def bypass_batch_mode_checks: Flag<["-"], "bypass-batch-mode-checks">,
HelpText<"Bypass checks for batch-mode errors.">;
HelpText<"Bypass checks for batch-mode errors.">;

def enable_verify_exclusivity : Flag<["-"], "enable-verify-exclusivity">,
HelpText<"Enable verification of access markers used to enforce exclusivity.">;
Expand Down
3 changes: 3 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,

Opts.DiagnosticsEditorMode |= Args.hasArg(OPT_diagnostics_editor_mode,
OPT_serialize_diagnostics_path);

Opts.DisableRedundantCoercionWarning |=
Args.hasArg(OPT_disable_redundant_coercion_warning);

Opts.EnableExperimentalStaticAssert |=
Args.hasArg(OPT_enable_experimental_static_assert);
Expand Down
33 changes: 32 additions & 1 deletion lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,11 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() {
break;
}

case ConstraintLocator::ExplicitTypeCoercion: {
diagnostic = diag::cannot_convert_coerce;
break;
}

default:
break;
}
Expand Down Expand Up @@ -2069,7 +2074,8 @@ bool ContextualFailure::diagnoseAsError() {
diagnostic = diag::cannot_convert_condition_value;
break;
}


case ConstraintLocator::ExplicitTypeCoercion:
case ConstraintLocator::InstanceType: {
if (diagnoseCoercionToUnrelatedType())
return true;
Expand Down Expand Up @@ -5540,6 +5546,31 @@ bool ThrowingFunctionConversionFailure::diagnoseAsError() {
return true;
}

bool UnnecessaryCoercionFailure::diagnoseAsError() {
auto expr = getAsExpr<CoerceExpr>(getAnchor());
auto sourceRange =
SourceRange(expr->getLoc(), expr->getCastTypeRepr()->getSourceRange().End);
auto castType = getType(expr->getCastTypeRepr());

if (isa<TypeAliasType>(getFromType().getPointer()) &&
isa<TypeAliasType>(getToType().getPointer())) {
auto fromTypeAlias = cast<TypeAliasType>(getFromType().getPointer());
auto toTypeAlias = cast<TypeAliasType>(getToType().getPointer());
// If the typealias are different, we need a warning mentioning both types.
if (fromTypeAlias->getDecl() != toTypeAlias->getDecl()) {
emitDiagnostic(diag::unnecessary_same_typealias_type_coercion,
getFromType(), castType)
.fixItRemove(sourceRange);
return true;
}
}

emitDiagnostic(diag::unnecessary_same_type_coercion,
castType)
.fixItRemove(sourceRange);
return true;
}

bool InOutConversionFailure::diagnoseAsError() {
auto *locator = getLocator();
auto path = locator->getPath();
Expand Down
21 changes: 21 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,27 @@ class ExpandArrayIntoVarargsFailure final : public ContextualFailure {
void tryDropArrayBracketsFixIt(const Expr *anchor) const;
};

/// Diagnose a situation where there is an explicit type coercion
/// to the same type e.g.:
///
/// ```swift
/// Double(1) as Double // redundant cast to 'Double' has no effect
/// 1 as Double as Double // redundant cast to 'Double' has no effect
/// let string = "String"
/// let s = string as String // redundant cast to 'String' has no effect
/// ```
class UnnecessaryCoercionFailure final
: public ContextualFailure {

public:
UnnecessaryCoercionFailure(const Solution &solution,
Type fromType, Type toType,
ConstraintLocator *locator)
: ContextualFailure(solution, fromType, toType, locator) {}

bool diagnoseAsError() override;
};

/// Diagnose a situation there is a mismatch between argument and parameter
/// types e.g.:
///
Expand Down
70 changes: 70 additions & 0 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,76 @@ IgnoreContextualType *IgnoreContextualType::create(ConstraintSystem &cs,
IgnoreContextualType(cs, resultTy, specifiedTy, locator);
}

bool RemoveUnnecessaryCoercion::diagnose(const Solution &solution, bool asNote) const {
UnnecessaryCoercionFailure failure(solution, getFromType(), getToType(),
getLocator());
return failure.diagnose(asNote);
}

bool RemoveUnnecessaryCoercion::attempt(ConstraintSystem &cs, Type fromType,
Type toType,
ConstraintLocatorBuilder locator) {
auto &ctx = cs.getASTContext();
if (ctx.LangOpts.DisableRedundantCoercionWarning)
return false;

auto last = locator.last();
bool isExplicitCoercion =
last && last->is<LocatorPathElt::ExplicitTypeCoercion>();
if (!isExplicitCoercion)
return false;

auto expr = getAsExpr<CoerceExpr>(locator.getAnchor());

// Check to ensure the from and to types are equal the cast type.
// This is required to handle cases where the conversion is done
// using compiler intrinsics of _HasCustomAnyHashableRepresentation
// to AnyHashable where if we coerce a generic type that conforms to
// this protocol to AnyHashable we match equal types here, but the
// explicit coercion is still required.
auto castType = cs.getType(expr->getCastTypeRepr());
if (!fromType->isEqual(castType) && cs.isAnyHashableType(castType))
return false;

auto toTypeRepr = expr->getCastTypeRepr();

// Don't emit this diagnostic for Implicitly unwrapped optional types
// e.g. i as Int!
if (isa<ImplicitlyUnwrappedOptionalTypeRepr>(toTypeRepr))
return false;

// If we are dealing with an OverloadedDeclRefExpr the coercion
// is probably being used to match a specific overload function type.
bool hasOverloadedDeclRefSubExpr = false;
expr->forEachChildExpr([&](Expr *childExpr) -> Expr *{
if (isa<OverloadedDeclRefExpr>(childExpr)) {
hasOverloadedDeclRefSubExpr = true;
return nullptr;
}
return childExpr;
});

if (hasOverloadedDeclRefSubExpr)
return false;

auto coercedType = cs.getType(expr->getSubExpr());
if (auto *typeVariable = coercedType->getAs<TypeVariableType>()) {
auto representative = cs.getRepresentative(typeVariable);
if (auto *typeSourceLocator =
cs.getTypeVariableBindingLocator(representative)) {
// If the type variable binding source locator is the same the
// contextual type equality is coming from this coercion.
if (typeSourceLocator == cs.getConstraintLocator(locator))
return false;
}
}

auto *fix = new (cs.getAllocator()) RemoveUnnecessaryCoercion(
cs, fromType, toType, cs.getConstraintLocator(locator));

return cs.recordFix(fix);
}

bool IgnoreAssignmentDestinationType::diagnose(const Solution &solution,
bool asNote) const {
auto &cs = getConstraintSystem();
Expand Down
22 changes: 22 additions & 0 deletions lib/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ enum class FixKind : uint8_t {
/// Allow a single tuple parameter to be matched with N arguments
/// by forming all of the given arguments into a single tuple.
AllowTupleSplatForSingleParameter,

/// Remove an unnecessary coercion ('as') if the types are already equal.
/// e.g. Double(1) as Double
RemoveUnnecessaryCoercion,

/// Allow a single argument type mismatch. This is the most generic
/// failure related to argument-to-parameter conversions.
Expand Down Expand Up @@ -1561,6 +1565,24 @@ class IgnoreContextualType : public ContextualMismatch {
ConstraintLocator *locator);
};

class RemoveUnnecessaryCoercion : public ContextualMismatch {
protected:
RemoveUnnecessaryCoercion(ConstraintSystem &cs, Type fromType, Type toType,
ConstraintLocator *locator)
: ContextualMismatch(cs, FixKind::RemoveUnnecessaryCoercion, fromType,
toType, locator, /*isWarning*/ true) {}

public:
std::string getName() const override {
return "remove unnecessary explicit type coercion";
}

bool diagnose(const Solution &solution, bool asNote = false) const override;

static bool attempt(ConstraintSystem &cs, Type fromType, Type toType,
ConstraintLocatorBuilder locator);
};

class IgnoreAssignmentDestinationType final : public ContextualMismatch {
IgnoreAssignmentDestinationType(ConstraintSystem &cs, Type sourceTy,
Type destTy, ConstraintLocator *locator)
Expand Down
40 changes: 29 additions & 11 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1949,7 +1949,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
auto *typeVar = createTypeVariable(getConstraintLocator(argLoc),
TVO_CanBindToNoEscape);
params.emplace_back(typeVar);
assignFixedType(typeVar, input);
assignFixedType(typeVar, input, getConstraintLocator(argLoc));
};

{
Expand Down Expand Up @@ -2825,7 +2825,7 @@ ConstraintSystem::matchTypesBindTypeVar(
: getTypeMatchFailure(locator);
}

assignFixedType(typeVar, type);
assignFixedType(typeVar, type, getConstraintLocator(locator));

return getTypeMatchSuccess();
}
Expand Down Expand Up @@ -3424,7 +3424,8 @@ bool ConstraintSystem::repairFailures(
});
};

if (path.empty()) {
if (path.empty() ||
path.back().is<LocatorPathElt::ExplicitTypeCoercion>()) {
if (!anchor)
return false;

Expand Down Expand Up @@ -4465,9 +4466,16 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
// let's defer it until later proper check.
if (!(desugar1->is<DependentMemberType>() &&
desugar2->is<DependentMemberType>())) {
// If the types are obviously equivalent, we're done.
if (desugar1->isEqual(desugar2) && !isa<InOutType>(desugar2)) {
return getTypeMatchSuccess();
if (desugar1->isEqual(desugar2)) {
if (kind == ConstraintKind::Conversion &&
!flags.contains(TMF_ApplyingFix)) {
if (RemoveUnnecessaryCoercion::attempt(*this, type1, type2,
getConstraintLocator(locator))) {
return getTypeMatchFailure(locator);
}
}
if (!isa<InOutType>(desugar2))
return getTypeMatchSuccess();
}
}

Expand Down Expand Up @@ -4554,9 +4562,10 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
if (auto *iot = type1->getAs<InOutType>()) {
if (!rep2->getImpl().canBindToLValue())
return getTypeMatchFailure(locator);
assignFixedType(rep2, LValueType::get(iot->getObjectType()));
assignFixedType(rep2, LValueType::get(iot->getObjectType()),
getConstraintLocator(locator));
} else {
assignFixedType(rep2, type1);
assignFixedType(rep2, type1, getConstraintLocator(locator));
}
return getTypeMatchSuccess();
} else if (typeVar1 && !typeVar2) {
Expand All @@ -4569,9 +4578,10 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
if (auto *lvt = type2->getAs<LValueType>()) {
if (!rep1->getImpl().canBindToInOut())
return getTypeMatchFailure(locator);
assignFixedType(rep1, InOutType::get(lvt->getObjectType()));
assignFixedType(rep1, InOutType::get(lvt->getObjectType()),
getConstraintLocator(locator));
} else {
assignFixedType(rep1, type2);
assignFixedType(rep1, type2, getConstraintLocator(locator));
}
return getTypeMatchSuccess();
} if (typeVar1 && typeVar2) {
Expand Down Expand Up @@ -10112,6 +10122,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
case FixKind::SpecifyClosureParameterType:
case FixKind::SpecifyClosureReturnType:
case FixKind::AddQualifierToAccessTopLevelName:
case FixKind::RemoveUnnecessaryCoercion:
llvm_unreachable("handled elsewhere");
}

Expand Down Expand Up @@ -10497,11 +10508,18 @@ void ConstraintSystem::addExplicitConversionConstraint(
SmallVector<Constraint *, 3> constraints;

auto locatorPtr = getConstraintLocator(locator);
ConstraintLocator *coerceLocator = locatorPtr;

auto *anchor = getAsExpr(locator.getAnchor());
if (anchor && isa<CoerceExpr>(anchor) && !anchor->isImplicit()) {
coerceLocator =
getConstraintLocator(anchor, LocatorPathElt::ExplicitTypeCoercion());
}

// Coercion (the common case).
Constraint *coerceConstraint =
Constraint::create(*this, ConstraintKind::Conversion,
fromType, toType, locatorPtr);
fromType, toType, coerceLocator);
coerceConstraint->setFavored();
constraints.push_back(coerceConstraint);

Expand Down
Loading